summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore5
-rw-r--r--AconfigFlags.bp16
-rw-r--r--Android.bp1
-rw-r--r--apct-tests/perftests/core/src/android/os/TracePerfTest.java25
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java152
-rw-r--r--boot/boot-image-profile.txt1
-rw-r--r--boot/preloaded-classes1
-rw-r--r--cmds/app_process/Android.bp1
-rw-r--r--cmds/incidentd/src/IncidentService.cpp6
-rw-r--r--cmds/uinput/src/com/android/commands/uinput/EvemuParser.java33
-rw-r--r--config/preloaded-classes1
-rw-r--r--core/api/current.txt7
-rw-r--r--core/api/module-lib-current.txt2
-rw-r--r--core/api/system-current.txt41
-rw-r--r--core/api/test-current.txt5
-rw-r--r--core/java/Android.bp75
-rw-r--r--core/java/android/animation/AnimatorSet.java29
-rw-r--r--core/java/android/animation/ValueAnimator.java13
-rw-r--r--core/java/android/app/ActivityManager.java2
-rw-r--r--core/java/android/app/ActivityThread.java9
-rw-r--r--core/java/android/app/AppCompatTaskInfo.java5
-rw-r--r--core/java/android/app/ApplicationPackageManager.java14
-rw-r--r--core/java/android/app/ApplicationStartInfo.java39
-rw-r--r--core/java/android/app/AutomaticZenRule.aidl4
-rw-r--r--core/java/android/app/AutomaticZenRule.java49
-rw-r--r--core/java/android/app/ForegroundServiceTypePolicy.java47
-rw-r--r--core/java/android/app/IActivityTaskManager.aidl5
-rw-r--r--core/java/android/app/INotificationManager.aidl2
-rw-r--r--core/java/android/app/Instrumentation.java12
-rw-r--r--core/java/android/app/LocaleConfig.java27
-rw-r--r--core/java/android/app/NotificationManager.java10
-rw-r--r--core/java/android/app/PictureInPictureParams.java2
-rw-r--r--core/java/android/app/PropertyInvalidatedCache.java37
-rw-r--r--core/java/android/app/StatusBarManager.java4
-rw-r--r--core/java/android/app/WallpaperManager.java25
-rw-r--r--core/java/android/app/WindowConfiguration.java6
-rw-r--r--core/java/android/app/activity_manager.aconfig34
-rw-r--r--core/java/android/app/admin/StringSetIntersection.java80
-rw-r--r--core/java/android/app/admin/flags/flags.aconfig10
-rw-r--r--core/java/android/app/notification.aconfig12
-rw-r--r--core/java/android/app/supervision/SupervisionAppService.java38
-rw-r--r--core/java/android/app/wallpaper.aconfig10
-rw-r--r--core/java/android/app/wallpaper/WallpaperDescription.java6
-rw-r--r--core/java/android/companion/virtual/flags/flags.aconfig7
-rw-r--r--core/java/android/companion/virtual/sensor/VirtualSensor.java2
-rw-r--r--core/java/android/companion/virtual/sensor/VirtualSensorAdditionalInfo.java35
-rw-r--r--core/java/android/content/Intent.java7
-rw-r--r--core/java/android/content/flags/flags.aconfig12
-rw-r--r--core/java/android/content/pm/PackageManager.java34
-rw-r--r--core/java/android/content/pm/PackageParser.java18
-rw-r--r--core/java/android/content/pm/RegisteredServicesCache.java31
-rw-r--r--core/java/android/content/pm/UserInfo.java8
-rw-r--r--core/java/android/content/pm/multiuser.aconfig7
-rw-r--r--core/java/android/content/pm/parsing/FrameworkParsingPackageUtils.java33
-rw-r--r--core/java/android/hardware/biometrics/BiometricConstants.java20
-rw-r--r--core/java/android/hardware/biometrics/flags.aconfig8
-rw-r--r--core/java/android/hardware/camera2/CameraCharacteristics.java28
-rw-r--r--core/java/android/hardware/camera2/CameraManager.java3
-rw-r--r--core/java/android/hardware/display/DisplayTopology.java56
-rw-r--r--core/java/android/hardware/input/IInputManager.aidl2
-rw-r--r--core/java/android/hardware/input/IKeyGestureHandler.aidl10
-rw-r--r--core/java/android/hardware/input/InputManager.java21
-rw-r--r--core/java/android/hardware/input/InputManagerGlobal.java103
-rw-r--r--core/java/android/hardware/input/input_framework.aconfig9
-rw-r--r--core/java/android/hardware/serial/flags/flags.aconfig2
-rw-r--r--core/java/android/inputmethodservice/InputMethodService.java4
-rw-r--r--core/java/android/inputmethodservice/NavigationBarController.java9
-rw-r--r--core/java/android/net/VpnManager.java26
-rw-r--r--core/java/android/net/flags.aconfig8
-rw-r--r--core/java/android/os/BatteryStats.java17
-rw-r--r--core/java/android/os/Binder.java16
-rw-r--r--core/java/android/os/CombinedMessageQueue/MessageQueue.java10
-rw-r--r--core/java/android/os/IpcDataCache.java3
-rw-r--r--core/java/android/os/Parcel.java8
-rw-r--r--core/java/android/os/PerfettoTrace.java15
-rw-r--r--core/java/android/os/PerfettoTrackEventExtra.java67
-rw-r--r--core/java/android/os/flags.aconfig28
-rw-r--r--core/java/android/os/image/flags/trade_in_mode_flags.aconfig9
-rw-r--r--core/java/android/provider/Settings.java10
-rw-r--r--core/java/android/security/advancedprotection/AdvancedProtectionManager.java32
-rw-r--r--core/java/android/security/flags.aconfig7
-rw-r--r--core/java/android/service/dreams/flags.aconfig8
-rw-r--r--core/java/android/service/notification/ZenModeConfig.java57
-rw-r--r--core/java/android/util/ArrayMap.java2
-rw-r--r--core/java/android/view/InsetsController.java16
-rw-r--r--core/java/android/view/OrientationEventListener.java10
-rw-r--r--core/java/android/view/SurfaceControl.java13
-rw-r--r--core/java/android/view/SurfaceView.java2
-rw-r--r--core/java/android/view/ViewTreeObserver.java6
-rw-r--r--core/java/android/view/WindowManagerGlobal.java8
-rw-r--r--core/java/android/view/WindowManagerImpl.java2
-rw-r--r--core/java/android/view/flags/view_tree_observer_flags.aconfig9
-rw-r--r--core/java/android/view/inputmethod/ImeTracker.java4
-rw-r--r--core/java/android/view/inspector/WindowInspector.java5
-rw-r--r--core/java/android/view/translation/ListenerGroup.java4
-rw-r--r--core/java/android/webkit/WebSettings.java13
-rw-r--r--core/java/android/webkit/flags.aconfig8
-rw-r--r--core/java/android/widget/DatePickerCalendarDelegate.java48
-rw-r--r--core/java/android/widget/TimePickerClockDelegate.java52
-rw-r--r--core/java/android/window/BackMotionEvent.java22
-rw-r--r--core/java/android/window/BackTouchTracker.java9
-rw-r--r--core/java/android/window/DesktopExperienceFlags.java1
-rw-r--r--core/java/android/window/DesktopModeFlags.java27
-rw-r--r--core/java/android/window/ImeOnBackInvokedDispatcher.java6
-rw-r--r--core/java/android/window/TaskSnapshot.java20
-rw-r--r--core/java/android/window/WindowContainerTransaction.java19
-rw-r--r--core/java/android/window/flags/lse_desktop_experience.aconfig27
-rw-r--r--core/java/android/window/flags/responsible_apis.aconfig7
-rw-r--r--core/java/android/window/flags/windowing_frontend.aconfig40
-rw-r--r--core/java/com/android/internal/app/AssistUtils.java2
-rw-r--r--core/java/com/android/internal/app/MediaRouteDialogPresenter.java12
-rw-r--r--core/java/com/android/internal/jank/Cuj.java31
-rw-r--r--core/java/com/android/internal/jank/DisplayResolutionTracker.java17
-rw-r--r--core/java/com/android/internal/os/BatteryStatsHistory.java107
-rw-r--r--core/java/com/android/internal/protolog/PerfettoProtoLogImpl.java10
-rw-r--r--core/java/com/android/internal/protolog/common/LogDataType.java4
-rw-r--r--core/java/com/android/internal/statusbar/DisableStates.aidl19
-rw-r--r--core/java/com/android/internal/statusbar/DisableStates.java95
-rw-r--r--core/java/com/android/internal/statusbar/IStatusBar.aidl2
-rw-r--r--core/java/com/android/internal/util/LatencyTracker.java16
-rw-r--r--core/java/com/android/internal/widget/ConversationLayout.java20
-rw-r--r--core/java/com/android/internal/widget/MessagingLayout.java8
-rw-r--r--core/java/com/android/internal/widget/NotificationProgressBar.java35
-rw-r--r--core/java/com/android/internal/widget/NotificationProgressDrawable.java68
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/CoreDocument.java2
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/Operations.java3
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/RemoteComposeBuffer.java52
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/DebugMessage.java157
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/TimeAttribute.java3
-rw-r--r--core/java/com/android/internal/widget/remotecompose/core/operations/TouchExpression.java2
-rw-r--r--core/java/com/android/internal/widget/remotecompose/player/platform/RemoteComposeCanvas.java37
-rw-r--r--core/java/com/android/server/servicewatcher/OWNERS2
-rw-r--r--core/jni/Android.bp2
-rw-r--r--core/jni/AndroidRuntime.cpp2
-rw-r--r--core/jni/android_media_AudioSystem.cpp30
-rw-r--r--core/jni/android_media_AudioVolumeGroupCallback.cpp176
-rw-r--r--core/jni/android_media_AudioVolumeGroupCallback.h44
-rw-r--r--core/jni/android_util_Binder.cpp34
-rw-r--r--core/res/AndroidManifest.xml4
-rw-r--r--core/res/res/drawable-w192dp/loader_horizontal_watch.xml97
-rw-r--r--core/res/res/drawable-w204dp/loader_horizontal_watch.xml104
-rw-r--r--core/res/res/drawable-w216dp/loader_horizontal_watch.xml105
-rw-r--r--core/res/res/drawable-w228dp/loader_horizontal_watch.xml103
-rw-r--r--core/res/res/drawable-w240dp/loader_horizontal_watch.xml104
-rw-r--r--core/res/res/drawable/loader_horizontal_watch.xml103
-rw-r--r--core/res/res/drawable/notification_progress.xml5
-rw-r--r--core/res/res/layout/notification_2025_messaging_group.xml1
-rw-r--r--core/res/res/layout/notification_2025_template_collapsed_base.xml1
-rw-r--r--core/res/res/layout/notification_2025_template_collapsed_media.xml1
-rw-r--r--core/res/res/layout/notification_2025_template_expanded_base.xml2
-rw-r--r--core/res/res/layout/notification_2025_template_expanded_big_picture.xml2
-rw-r--r--core/res/res/layout/notification_2025_template_expanded_big_text.xml2
-rw-r--r--core/res/res/layout/notification_2025_template_expanded_call.xml2
-rw-r--r--core/res/res/layout/notification_2025_template_expanded_conversation.xml5
-rw-r--r--core/res/res/layout/notification_2025_template_expanded_inbox.xml2
-rw-r--r--core/res/res/layout/notification_2025_template_expanded_media.xml2
-rw-r--r--core/res/res/layout/notification_2025_template_expanded_messaging.xml5
-rw-r--r--core/res/res/layout/notification_2025_template_expanded_progress.xml2
-rw-r--r--core/res/res/layout/notification_2025_title.xml26
-rw-r--r--core/res/res/values-af/strings.xml21
-rw-r--r--core/res/res/values-am/strings.xml21
-rw-r--r--core/res/res/values-ar/strings.xml9
-rw-r--r--core/res/res/values-as/strings.xml21
-rw-r--r--core/res/res/values-az/strings.xml3
-rw-r--r--core/res/res/values-b+sr+Latn/strings.xml21
-rw-r--r--core/res/res/values-be/strings.xml21
-rw-r--r--core/res/res/values-bg/strings.xml3
-rw-r--r--core/res/res/values-bn/strings.xml21
-rw-r--r--core/res/res/values-bs/strings.xml21
-rw-r--r--core/res/res/values-ca/strings.xml21
-rw-r--r--core/res/res/values-cs/strings.xml21
-rw-r--r--core/res/res/values-da/strings.xml21
-rw-r--r--core/res/res/values-de/strings.xml21
-rw-r--r--core/res/res/values-el/strings.xml35
-rw-r--r--core/res/res/values-en-rAU/strings.xml21
-rw-r--r--core/res/res/values-en-rGB/strings.xml21
-rw-r--r--core/res/res/values-en-rIN/strings.xml21
-rw-r--r--core/res/res/values-es/strings.xml21
-rw-r--r--core/res/res/values-et/strings.xml21
-rw-r--r--core/res/res/values-eu/strings.xml21
-rw-r--r--core/res/res/values-fa/strings.xml3
-rw-r--r--core/res/res/values-fi/strings.xml25
-rw-r--r--core/res/res/values-fr-rCA/strings.xml3
-rw-r--r--core/res/res/values-fr/strings.xml21
-rw-r--r--core/res/res/values-gl/strings.xml21
-rw-r--r--core/res/res/values-gu/strings.xml24
-rw-r--r--core/res/res/values-hi/strings.xml21
-rw-r--r--core/res/res/values-hr/strings.xml21
-rw-r--r--core/res/res/values-hu/strings.xml21
-rw-r--r--core/res/res/values-hy/strings.xml3
-rw-r--r--core/res/res/values-in/strings.xml21
-rw-r--r--core/res/res/values-is/strings.xml18
-rw-r--r--core/res/res/values-it/strings.xml18
-rw-r--r--core/res/res/values-iw/strings.xml21
-rw-r--r--core/res/res/values-kk/strings.xml21
-rw-r--r--core/res/res/values-kn/strings.xml3
-rw-r--r--core/res/res/values-ko/strings.xml21
-rw-r--r--core/res/res/values-ky/strings.xml21
-rw-r--r--core/res/res/values-lo/strings.xml3
-rw-r--r--core/res/res/values-lt/strings.xml3
-rw-r--r--core/res/res/values-lv/strings.xml21
-rw-r--r--core/res/res/values-mk/strings.xml21
-rw-r--r--core/res/res/values-ml/strings.xml21
-rw-r--r--core/res/res/values-mn/strings.xml21
-rw-r--r--core/res/res/values-mr/strings.xml21
-rw-r--r--core/res/res/values-my/strings.xml3
-rw-r--r--core/res/res/values-nb/strings.xml21
-rw-r--r--core/res/res/values-ne/strings.xml21
-rw-r--r--core/res/res/values-nl/strings.xml21
-rw-r--r--core/res/res/values-or/strings.xml21
-rw-r--r--core/res/res/values-pa/strings.xml21
-rw-r--r--core/res/res/values-pl/strings.xml21
-rw-r--r--core/res/res/values-pt-rBR/strings.xml21
-rw-r--r--core/res/res/values-pt-rPT/strings.xml3
-rw-r--r--core/res/res/values-pt/strings.xml21
-rw-r--r--core/res/res/values-ro/strings.xml21
-rw-r--r--core/res/res/values-ru/strings.xml21
-rw-r--r--core/res/res/values-si/strings.xml21
-rw-r--r--core/res/res/values-sk/strings.xml21
-rw-r--r--core/res/res/values-sl/strings.xml3
-rw-r--r--core/res/res/values-sq/strings.xml21
-rw-r--r--core/res/res/values-sr/strings.xml21
-rw-r--r--core/res/res/values-sv/strings.xml21
-rw-r--r--core/res/res/values-sw/strings.xml21
-rw-r--r--core/res/res/values-ta/strings.xml21
-rw-r--r--core/res/res/values-th/strings.xml21
-rw-r--r--core/res/res/values-tl/strings.xml21
-rw-r--r--core/res/res/values-tr/strings.xml21
-rw-r--r--core/res/res/values-uk/strings.xml21
-rw-r--r--core/res/res/values-uz/strings.xml3
-rw-r--r--core/res/res/values-vi/strings.xml21
-rw-r--r--core/res/res/values-w192dp/dimens_watch.xml21
-rw-r--r--core/res/res/values-w204dp/dimens_watch.xml21
-rw-r--r--core/res/res/values-w216dp/dimens_watch.xml21
-rw-r--r--core/res/res/values-w228dp/dimens_watch.xml21
-rw-r--r--core/res/res/values-w240dp/dimens_watch.xml21
-rw-r--r--core/res/res/values-watch/styles_device_defaults.xml3
-rw-r--r--core/res/res/values-watch/themes_device_defaults.xml3
-rw-r--r--core/res/res/values-zh-rCN/strings.xml21
-rw-r--r--core/res/res/values-zh-rHK/strings.xml21
-rw-r--r--core/res/res/values-zh-rTW/strings.xml21
-rw-r--r--core/res/res/values-zu/strings.xml21
-rw-r--r--core/res/res/values/attrs.xml24
-rw-r--r--core/res/res/values/config.xml6
-rw-r--r--core/res/res/values/config_telephony.xml21
-rw-r--r--core/res/res/values/dimens.xml7
-rw-r--r--core/res/res/values/dimens_watch.xml4
-rw-r--r--core/res/res/values/styles_material.xml3
-rw-r--r--core/res/res/values/symbols.xml1
-rw-r--r--core/res/res/xml/sms_short_codes.xml2
-rw-r--r--core/tests/FileSystemUtilsTest/OWNERS2
-rw-r--r--core/tests/FileSystemUtilsTest/src/com/android/internal/content/FileSystemUtilsTest.java14
-rw-r--r--core/tests/coretests/src/android/animation/AnimatorSetCallsTest.java28
-rw-r--r--core/tests/coretests/src/android/animation/ValueAnimatorTests.java25
-rw-r--r--core/tests/coretests/src/android/app/ApplicationPackageManagerTest.java176
-rw-r--r--core/tests/coretests/src/android/content/IntentTest.java44
-rw-r--r--core/tests/coretests/src/android/content/pm/RegisteredServicesCacheUnitTest.java95
-rw-r--r--core/tests/coretests/src/android/content/pm/UserInfoTest.java90
-rw-r--r--core/tests/coretests/src/android/os/ParcelTest.java61
-rw-r--r--core/tests/coretests/src/android/security/advancedprotection/AdvancedProtectionManagerTest.java47
-rw-r--r--core/tests/coretests/src/android/window/BackTouchTrackerTest.kt2
-rw-r--r--core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java3
-rw-r--r--core/tests/coretests/src/com/android/internal/app/MediaRouteDialogPresenterTest.kt92
-rw-r--r--core/tests/coretests/src/com/android/internal/statusbar/DisableStatesTest.java64
-rw-r--r--data/etc/Android.bp8
-rw-r--r--data/etc/com.android.statementservice.xml23
-rw-r--r--data/etc/privapp-permissions-platform.xml6
-rw-r--r--graphics/java/android/graphics/Typeface.java20
-rw-r--r--libs/WindowManager/Shell/Android.bp8
-rw-r--r--libs/WindowManager/Shell/aconfig/multitasking.aconfig7
-rw-r--r--libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerViewTest.kt93
-rw-r--r--libs/WindowManager/Shell/res/drawable/desktop_mode_ic_handle_menu_restart.xml28
-rw-r--r--libs/WindowManager/Shell/res/drawable/desktop_windowing_transition_background.xml6
-rw-r--r--libs/WindowManager/Shell/res/layout/bubble_bar_manage_education.xml2
-rw-r--r--libs/WindowManager/Shell/res/layout/bubble_bar_stack_education.xml2
-rw-r--r--libs/WindowManager/Shell/res/layout/bubble_manage_menu.xml2
-rw-r--r--libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu.xml16
-rw-r--r--libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu_action_button.xml15
-rw-r--r--libs/WindowManager/Shell/res/values-af/strings.xml11
-rw-r--r--libs/WindowManager/Shell/res/values-am/strings.xml11
-rw-r--r--libs/WindowManager/Shell/res/values-ar/strings.xml11
-rw-r--r--libs/WindowManager/Shell/res/values-as/strings.xml11
-rw-r--r--libs/WindowManager/Shell/res/values-az/strings.xml11
-rw-r--r--libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml11
-rw-r--r--libs/WindowManager/Shell/res/values-be/strings.xml11
-rw-r--r--libs/WindowManager/Shell/res/values-bg/strings.xml11
-rw-r--r--libs/WindowManager/Shell/res/values-bn/strings.xml11
-rw-r--r--libs/WindowManager/Shell/res/values-bs/strings.xml11
-rw-r--r--libs/WindowManager/Shell/res/values-ca/strings.xml11
-rw-r--r--libs/WindowManager/Shell/res/values-cs/strings.xml11
-rw-r--r--libs/WindowManager/Shell/res/values-da/strings.xml11
-rw-r--r--libs/WindowManager/Shell/res/values-de/strings.xml11
-rw-r--r--libs/WindowManager/Shell/res/values-el/strings.xml11
-rw-r--r--libs/WindowManager/Shell/res/values-en-rAU/strings.xml11
-rw-r--r--libs/WindowManager/Shell/res/values-en-rCA/strings.xml1
-rw-r--r--libs/WindowManager/Shell/res/values-en-rGB/strings.xml11
-rw-r--r--libs/WindowManager/Shell/res/values-en-rIN/strings.xml11
-rw-r--r--libs/WindowManager/Shell/res/values-es-rUS/strings.xml11
-rw-r--r--libs/WindowManager/Shell/res/values-es/strings.xml11
-rw-r--r--libs/WindowManager/Shell/res/values-et/strings.xml11
-rw-r--r--libs/WindowManager/Shell/res/values-eu/strings.xml11
-rw-r--r--libs/WindowManager/Shell/res/values-fa/strings.xml11
-rw-r--r--libs/WindowManager/Shell/res/values-fi/strings.xml11
-rw-r--r--libs/WindowManager/Shell/res/values-fr-rCA/strings.xml11
-rw-r--r--libs/WindowManager/Shell/res/values-fr/strings.xml11
-rw-r--r--libs/WindowManager/Shell/res/values-gl/strings.xml11
-rw-r--r--libs/WindowManager/Shell/res/values-gu/strings.xml41
-rw-r--r--libs/WindowManager/Shell/res/values-hi/strings.xml11
-rw-r--r--libs/WindowManager/Shell/res/values-hr/strings.xml11
-rw-r--r--libs/WindowManager/Shell/res/values-hu/strings.xml11
-rw-r--r--libs/WindowManager/Shell/res/values-hy/strings.xml11
-rw-r--r--libs/WindowManager/Shell/res/values-in/strings.xml11
-rw-r--r--libs/WindowManager/Shell/res/values-is/strings.xml11
-rw-r--r--libs/WindowManager/Shell/res/values-it/strings.xml11
-rw-r--r--libs/WindowManager/Shell/res/values-iw/strings.xml11
-rw-r--r--libs/WindowManager/Shell/res/values-ja/strings.xml11
-rw-r--r--libs/WindowManager/Shell/res/values-ka/strings.xml11
-rw-r--r--libs/WindowManager/Shell/res/values-kk/strings.xml11
-rw-r--r--libs/WindowManager/Shell/res/values-km/strings.xml11
-rw-r--r--libs/WindowManager/Shell/res/values-kn/strings.xml11
-rw-r--r--libs/WindowManager/Shell/res/values-ko/strings.xml11
-rw-r--r--libs/WindowManager/Shell/res/values-ky/strings.xml11
-rw-r--r--libs/WindowManager/Shell/res/values-lo/strings.xml11
-rw-r--r--libs/WindowManager/Shell/res/values-lt/strings.xml11
-rw-r--r--libs/WindowManager/Shell/res/values-lv/strings.xml11
-rw-r--r--libs/WindowManager/Shell/res/values-mk/strings.xml11
-rw-r--r--libs/WindowManager/Shell/res/values-ml/strings.xml11
-rw-r--r--libs/WindowManager/Shell/res/values-mn/strings.xml11
-rw-r--r--libs/WindowManager/Shell/res/values-mr/strings.xml11
-rw-r--r--libs/WindowManager/Shell/res/values-ms/strings.xml11
-rw-r--r--libs/WindowManager/Shell/res/values-my/strings.xml11
-rw-r--r--libs/WindowManager/Shell/res/values-nb/strings.xml11
-rw-r--r--libs/WindowManager/Shell/res/values-ne/strings.xml11
-rw-r--r--libs/WindowManager/Shell/res/values-nl/strings.xml11
-rw-r--r--libs/WindowManager/Shell/res/values-or/strings.xml11
-rw-r--r--libs/WindowManager/Shell/res/values-pa/strings.xml11
-rw-r--r--libs/WindowManager/Shell/res/values-pl/strings.xml11
-rw-r--r--libs/WindowManager/Shell/res/values-pt-rBR/strings.xml11
-rw-r--r--libs/WindowManager/Shell/res/values-pt-rPT/strings.xml11
-rw-r--r--libs/WindowManager/Shell/res/values-pt/strings.xml11
-rw-r--r--libs/WindowManager/Shell/res/values-ro/strings.xml11
-rw-r--r--libs/WindowManager/Shell/res/values-ru/strings.xml11
-rw-r--r--libs/WindowManager/Shell/res/values-si/strings.xml11
-rw-r--r--libs/WindowManager/Shell/res/values-sk/strings.xml11
-rw-r--r--libs/WindowManager/Shell/res/values-sl/strings.xml11
-rw-r--r--libs/WindowManager/Shell/res/values-sq/strings.xml11
-rw-r--r--libs/WindowManager/Shell/res/values-sr/strings.xml11
-rw-r--r--libs/WindowManager/Shell/res/values-sv/strings.xml11
-rw-r--r--libs/WindowManager/Shell/res/values-sw/strings.xml11
-rw-r--r--libs/WindowManager/Shell/res/values-ta/strings.xml11
-rw-r--r--libs/WindowManager/Shell/res/values-te/strings.xml11
-rw-r--r--libs/WindowManager/Shell/res/values-th/strings.xml11
-rw-r--r--libs/WindowManager/Shell/res/values-tl/strings.xml11
-rw-r--r--libs/WindowManager/Shell/res/values-tr/strings.xml11
-rw-r--r--libs/WindowManager/Shell/res/values-uk/strings.xml11
-rw-r--r--libs/WindowManager/Shell/res/values-ur/strings.xml11
-rw-r--r--libs/WindowManager/Shell/res/values-uz/strings.xml11
-rw-r--r--libs/WindowManager/Shell/res/values-vi/strings.xml11
-rw-r--r--libs/WindowManager/Shell/res/values-zh-rCN/strings.xml11
-rw-r--r--libs/WindowManager/Shell/res/values-zh-rHK/strings.xml11
-rw-r--r--libs/WindowManager/Shell/res/values-zh-rTW/strings.xml11
-rw-r--r--libs/WindowManager/Shell/res/values-zu/strings.xml11
-rw-r--r--libs/WindowManager/Shell/res/values/colors.xml2
-rw-r--r--libs/WindowManager/Shell/res/values/dimen.xml15
-rw-r--r--libs/WindowManager/Shell/res/values/strings.xml2
-rw-r--r--libs/WindowManager/Shell/res/values/styles.xml8
-rw-r--r--libs/WindowManager/Shell/shared/res/values/dimen.xml4
-rw-r--r--libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/GroupedTaskInfo.java41
-rw-r--r--libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/IHomeTransitionListener.aidl6
-rw-r--r--libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/BubbleDropTargetBoundsProvider.kt5
-rw-r--r--libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/ContextUtils.kt (renamed from libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/ContextUtils.kt)2
-rw-r--r--libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/DropTargetView.kt6
-rw-r--r--libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeStatus.java5
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/animation/SizeChangeAnimation.java49
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java45
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java1
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java3
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleFlyoutView.java3
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflowContainerView.java6
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java20
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java11
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTransitions.java2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/ManageEducationView.kt7
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/StackEducationView.kt4
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelper.java10
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedViewDragController.kt16
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java15
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarMenuItemView.java2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarMenuView.java2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleEducationViewController.kt9
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayController.java8
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipBoundsAlgorithm.java7
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipDesktopState.java122
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipDesktopState.kt96
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerRoundedCorner.java28
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/split/FlexParallaxSpec.java5
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java21
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java17
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/compatui/impl/CompatUIRequests.kt5
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java13
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java40
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopActivityOrientationChangeHandler.kt23
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopDisplayEventHandler.kt65
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopDisplayModeController.kt24
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopImeHandler.kt51
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeKeyGestureHandler.kt29
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeMoveToDisplayTransitionHandler.kt24
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeUiEventLogger.kt30
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeUtils.kt28
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopRepository.kt113
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt84
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopUserRepositories.kt21
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/ExitDesktopTaskTransitionHandler.java9
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/VisualIndicatorViewContainer.kt129
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/common/DefaultHomePackageSupplier.kt12
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/multidesks/DeskTransition.kt23
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/multidesks/DesksTransitionObserver.kt105
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/persistence/DesktopPersistentRepository.kt36
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/persistence/DesktopRepositoryInitializer.kt10
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/persistence/DesktopRepositoryInitializerImpl.kt48
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/persistence/persistent_desktop_repositories.proto7
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/docs/debugging.md19
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java3
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserver.java2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java18
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipController.java13
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTaskListener.java17
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java51
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/transition/PipTransitionUtils.java30
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java15
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java16
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java3
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedTransition.java3
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultSurfaceAnimator.java22
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java46
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/HomeTransitionObserver.java23
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/RecentsMixedTransition.java9
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/ScreenRotationAnimation.java6
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/TransitionAnimationHelper.java81
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java73
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java17
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CarWindowDecoration.java13
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java19
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java90
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.kt153
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenuActionButton.kt19
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MaximizeMenu.kt89
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MultiDisplayVeiledResizeTaskPositioner.kt13
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java61
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/ThemeUtils.kt3
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingDividerWindowManager.kt5
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingWindowDecoration.kt59
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/TilingDividerView.kt41
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHandleAnimator.kt101
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHandleViewHolder.kt68
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHeaderViewHolder.kt115
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/utils/CommonAssertions.kt20
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/utils/SplitScreenUtils.kt12
-rw-r--r--libs/WindowManager/Shell/tests/unittest/res/values/dimen.xml2
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackProgressAnimatorTest.java3
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/CustomCrossActivityBackAnimationTest.kt3
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayControllerTests.java21
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/pip/PipDesktopStateTest.java184
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/pip/PipDesktopStateTest.kt153
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/FlexParallaxSpecTests.java1
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java29
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopActivityOrientationChangeHandlerTest.kt18
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopDisplayEventHandlerTest.kt80
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopDisplayModeControllerTest.kt279
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeKeyGestureHandlerTest.kt20
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeMoveToDisplayTransitionHandlerTest.kt3
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopRepositoryTest.kt103
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt131
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/VisualIndicatorViewContainerTest.kt110
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/multidesks/DesksTransitionObserverTest.kt30
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/persistence/DesktopPersistentRepositoryTest.kt54
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/persistence/DesktopRepositoryInitializerTest.kt31
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserverTest.java19
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/phone/PipTaskListenerTest.java1
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/GroupedTaskInfoTest.kt5
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java6
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/DefaultTransitionHandlerTest.java5
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/HomeTransitionObserverTest.java14
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java72
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTestsBase.kt5
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java147
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/HandleMenuTest.kt6
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/MultiDisplayVeiledResizeTaskPositionerTest.kt6
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java50
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingDecorViewModelTest.kt3
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingWindowDecorationTest.kt107
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/viewholder/AppHandleViewHolderTest.kt8
-rw-r--r--libs/androidfw/AssetManager2.cpp4
-rw-r--r--libs/androidfw/CursorWindow.cpp2
-rw-r--r--libs/androidfw/LocaleDataLookup.cpp2
-rw-r--r--libs/androidfw/ResourceTypes.cpp16
-rw-r--r--libs/androidfw/include/androidfw/ResourceTypes.h5
-rw-r--r--libs/hostgraphics/include/gui/BufferItemConsumer.h4
-rw-r--r--libs/hwui/Android.bp1
-rw-r--r--libs/hwui/WebViewFunctorManager.cpp47
-rw-r--r--libs/hwui/WebViewFunctorManager.h13
-rw-r--r--libs/hwui/aconfig/hwui_flags.aconfig7
-rw-r--r--libs/hwui/hwui/Bitmap.cpp9
-rw-r--r--libs/hwui/jni/Bitmap.cpp33
-rw-r--r--libs/hwui/jni/GIFMovie.cpp10
-rw-r--r--libs/hwui/jni/android_graphics_HardwareRenderer.cpp14
-rw-r--r--libs/hwui/pipeline/skia/SkiaPipeline.cpp2
-rw-r--r--libs/hwui/platform/host/WebViewFunctorManager.cpp2
-rw-r--r--libs/hwui/platform/host/renderthread/RenderThread.cpp2
-rw-r--r--libs/hwui/renderthread/CanvasContext.cpp64
-rw-r--r--libs/hwui/renderthread/CanvasContext.h17
-rw-r--r--libs/hwui/renderthread/RenderProxy.cpp20
-rw-r--r--libs/hwui/renderthread/RenderProxy.h4
-rw-r--r--libs/hwui/renderthread/RenderThread.cpp60
-rw-r--r--libs/hwui/renderthread/RenderThread.h48
-rw-r--r--libs/hwui/renderthread/VulkanManager.cpp14
-rw-r--r--libs/hwui/renderthread/VulkanManager.h8
-rw-r--r--location/api/system-current.txt8
-rw-r--r--location/java/android/location/BeidouSatelliteEphemeris.java22
-rw-r--r--location/java/android/location/GalileoAssistance.java16
-rw-r--r--media/java/android/media/AudioDeviceVolumeManager.java238
-rw-r--r--media/java/android/media/AudioManager.java184
-rw-r--r--media/java/android/media/AudioSystem.java21
-rw-r--r--media/java/android/media/IAudioService.aidl5
-rw-r--r--media/java/android/media/MediaCodecInfo.java15
-rw-r--r--media/java/android/media/MediaRecorder.java2
-rw-r--r--media/java/android/media/audiopolicy/AudioVolumeGroupChangeHandler.java166
-rw-r--r--media/java/android/media/audiopolicy/IAudioVolumeChangeDispatcher.aidl31
-rw-r--r--media/java/android/media/flags/media_better_together.aconfig7
-rw-r--r--media/java/android/media/projection/TEST_MAPPING6
-rw-r--r--media/java/android/media/quality/MediaQualityContract.java38
-rw-r--r--media/tests/AudioPolicyTest/src/com/android/audiopolicytest/AudioVolumeGroupChangeHandlerTest.java211
-rw-r--r--media/tests/projection/Android.bp8
-rw-r--r--media/tests/projection/AndroidManifest.xml2
-rw-r--r--media/tests/projection/AndroidTest.xml9
-rw-r--r--media/tests/projection/TEST_MAPPING7
-rw-r--r--media/tests/projection/src/android/media/projection/MediaProjectionStoppingTest.java293
-rw-r--r--packages/CompanionDeviceManager/res/values-gl/strings.xml2
-rw-r--r--packages/CredentialManager/wear/AndroidManifest.xml6
-rw-r--r--packages/CredentialManager/wear/res/values/themes.xml24
-rw-r--r--packages/InputDevices/res/raw/keyboard_layout_hungarian.kcm8
-rw-r--r--packages/PackageInstaller/src/com/android/packageinstaller/PackageUtil.java32
-rw-r--r--packages/PackageInstaller/src/com/android/packageinstaller/v2/model/PackageUtil.kt20
-rw-r--r--packages/SettingsLib/AdaptiveIcon/Android.bp3
-rw-r--r--packages/SettingsLib/AdaptiveIcon/src/com/android/settingslib/widget/AdaptiveIconShapeDrawable.java3
-rw-r--r--packages/SettingsLib/AdaptiveIcon/src/com/android/settingslib/widget/AdaptiveOutlineDrawable.java2
-rw-r--r--packages/SettingsLib/ButtonPreference/src/com/android/settingslib/widget/ButtonPreference.java48
-rw-r--r--packages/SettingsLib/CollapsingToolbarBaseActivity/res/layout-v36/settingslib_expressive_collapsing_toolbar_content_layout.xml20
-rw-r--r--packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarAppCompatActivity.java24
-rw-r--r--packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarBaseActivity.java24
-rw-r--r--packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarDelegate.java41
-rw-r--r--packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/AndroidSecureSettings.java4
-rw-r--r--packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/DeviceStateAutoRotateSettingManager.kt5
-rw-r--r--packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/DeviceStateAutoRotateSettingManagerImpl.kt11
-rw-r--r--packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/DeviceStateAutoRotateSettingManagerProvider.kt53
-rw-r--r--packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/DeviceStateRotationLockSettingsManager.java71
-rw-r--r--packages/SettingsLib/SettingsTheme/res/layout/settingslib_expressive_collapsable_textview.xml3
-rw-r--r--packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/CollapsableTextView.kt1
-rw-r--r--packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/SettingsPreferenceGroupAdapter.kt17
-rw-r--r--packages/SettingsLib/Spa/build.gradle.kts6
-rw-r--r--packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GallerySpaEnvironment.kt2
-rw-r--r--packages/SettingsLib/Spa/gradle/libs.versions.toml4
-rw-r--r--packages/SettingsLib/Spa/spa/build.gradle.kts8
-rw-r--r--packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Category.kt37
-rw-r--r--packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/ui/CategoryTest.kt17
-rw-r--r--packages/SettingsLib/StatusBannerPreference/res/drawable/settingslib_expressive_icon_status_level_off.xml28
-rw-r--r--packages/SettingsLib/StatusBannerPreference/res/layout/settingslib_expressive_preference_statusbanner.xml21
-rw-r--r--packages/SettingsLib/StatusBannerPreference/res/values/attrs.xml3
-rw-r--r--packages/SettingsLib/StatusBannerPreference/res/values/colors.xml1
-rw-r--r--packages/SettingsLib/StatusBannerPreference/src/com/android/settingslib/widget/StatusBannerPreference.kt64
-rw-r--r--packages/SettingsLib/Tile/src/com/android/settingslib/drawer/Tile.java11
-rw-r--r--packages/SettingsLib/Tile/src/com/android/settingslib/drawer/TileUtils.java7
-rw-r--r--packages/SettingsLib/ValuePreference/Android.bp31
-rw-r--r--packages/SettingsLib/ValuePreference/AndroidManifest.xml23
-rw-r--r--packages/SettingsLib/ValuePreference/res/layout/settingslib_expressive_value_preference.xml45
-rw-r--r--packages/SettingsLib/ValuePreference/res/layout/settingslib_expressive_value_preference_text_frame.xml49
-rw-r--r--packages/SettingsLib/ValuePreference/res/values/styles.xml23
-rw-r--r--packages/SettingsLib/ValuePreference/src/com/android/settingslib/widget/ValuePreference.kt44
-rw-r--r--packages/SettingsLib/aconfig/settingslib.aconfig10
-rw-r--r--packages/SettingsLib/res/values-af/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-am/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-ar/strings.xml1
-rw-r--r--packages/SettingsLib/res/values-as/strings.xml10
-rw-r--r--packages/SettingsLib/res/values-az/strings.xml1
-rw-r--r--packages/SettingsLib/res/values-b+sr+Latn/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-be/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-bg/strings.xml3
-rw-r--r--packages/SettingsLib/res/values-bn/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-bs/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-ca/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-cs/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-da/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-de/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-el/strings.xml36
-rw-r--r--packages/SettingsLib/res/values-en-rAU/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-en-rCA/strings.xml1
-rw-r--r--packages/SettingsLib/res/values-en-rGB/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-en-rIN/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-es-rUS/strings.xml1
-rw-r--r--packages/SettingsLib/res/values-es/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-et/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-eu/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-fa/strings.xml1
-rw-r--r--packages/SettingsLib/res/values-fi/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-fr-rCA/strings.xml1
-rw-r--r--packages/SettingsLib/res/values-fr/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-gl/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-gu/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-hi/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-hr/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-hu/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-hy/strings.xml1
-rw-r--r--packages/SettingsLib/res/values-in/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-is/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-it/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-iw/strings.xml7
-rw-r--r--packages/SettingsLib/res/values-ja/strings.xml1
-rw-r--r--packages/SettingsLib/res/values-ka/strings.xml1
-rw-r--r--packages/SettingsLib/res/values-kk/strings.xml5
-rw-r--r--packages/SettingsLib/res/values-km/strings.xml3
-rw-r--r--packages/SettingsLib/res/values-kn/strings.xml1
-rw-r--r--packages/SettingsLib/res/values-ko/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-ky/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-lo/strings.xml1
-rw-r--r--packages/SettingsLib/res/values-lt/strings.xml1
-rw-r--r--packages/SettingsLib/res/values-lv/strings.xml5
-rw-r--r--packages/SettingsLib/res/values-mk/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-ml/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-mn/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-mr/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-ms/strings.xml1
-rw-r--r--packages/SettingsLib/res/values-my/strings.xml3
-rw-r--r--packages/SettingsLib/res/values-nb/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-ne/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-nl/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-or/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-pa/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-pl/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-pt-rBR/strings.xml5
-rw-r--r--packages/SettingsLib/res/values-pt-rPT/strings.xml3
-rw-r--r--packages/SettingsLib/res/values-pt/strings.xml5
-rw-r--r--packages/SettingsLib/res/values-ro/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-ru/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-si/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-sk/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-sl/strings.xml1
-rw-r--r--packages/SettingsLib/res/values-sq/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-sr/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-sv/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-sw/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-ta/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-te/strings.xml1
-rw-r--r--packages/SettingsLib/res/values-th/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-tl/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-tr/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-uk/strings.xml5
-rw-r--r--packages/SettingsLib/res/values-ur/strings.xml1
-rw-r--r--packages/SettingsLib/res/values-uz/strings.xml1
-rw-r--r--packages/SettingsLib/res/values-vi/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-zh-rCN/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-zh-rHK/strings.xml6
-rw-r--r--packages/SettingsLib/res/values-zh-rTW/strings.xml4
-rw-r--r--packages/SettingsLib/res/values-zu/strings.xml6
-rw-r--r--packages/SettingsLib/res/values/strings.xml2
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/AmbientVolumeUiController.java1
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java41
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java28
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java122
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/CsipDeviceManager.java5
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/LeAudioProfile.java33
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcast.java67
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/notification/data/repository/ZenModeRepository.kt25
-rw-r--r--packages/SettingsLib/tests/integ/src/com/android/settingslib/devicestate/DeviceStateAutoRotateSettingManagerImplTest.kt7
-rw-r--r--packages/SettingsLib/tests/integ/src/com/android/settingslib/devicestate/DeviceStateAutoRotateSettingManagerProviderTest.kt89
-rw-r--r--packages/SettingsLib/tests/integ/src/com/android/settingslib/devicestate/DeviceStateRotationLockSettingsManagerTest.java4
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothEventManagerTest.java29
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java52
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java105
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CsipDeviceManagerTest.java4
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/notification/data/repository/ZenModeRepositoryTest.kt10
-rw-r--r--packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java1
-rw-r--r--packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java22
-rw-r--r--packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java1
-rw-r--r--packages/SettingsProvider/test/src/com/android/providers/settings/SettingsHelperTest.java6
-rw-r--r--packages/Shell/AndroidManifest.xml4
-rw-r--r--packages/Shell/res/values-en-rCA-watch/strings.xml20
-rw-r--r--packages/StatementService/Android.bp4
-rw-r--r--packages/SystemUI/Android.bp4
-rw-r--r--packages/SystemUI/TEST_MAPPING20
-rw-r--r--packages/SystemUI/accessibility/accessibilitymenu/Android.bp1
-rw-r--r--packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuOverlayLayout.java13
-rw-r--r--packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuViewPager.java5
-rw-r--r--packages/SystemUI/aconfig/accessibility.aconfig10
-rw-r--r--packages/SystemUI/aconfig/desktop_users_and_accounts.aconfig9
-rw-r--r--packages/SystemUI/aconfig/systemui.aconfig107
-rw-r--r--packages/SystemUI/animation/src/com/android/systemui/animation/ActivityTransitionAnimator.kt40
-rw-r--r--packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewTransitionAnimatorController.kt16
-rw-r--r--packages/SystemUI/animation/src/com/android/systemui/animation/RemoteAnimationRunnerCompat.java15
-rw-r--r--packages/SystemUI/compose/core/src/com/android/compose/PlatformButtons.kt9
-rw-r--r--packages/SystemUI/compose/core/src/com/android/compose/animation/Expandable.kt41
-rw-r--r--packages/SystemUI/compose/core/src/com/android/compose/gesture/NestedDraggable.kt33
-rw-r--r--packages/SystemUI/compose/core/src/com/android/compose/gesture/NestedScrollController.kt12
-rw-r--r--packages/SystemUI/compose/core/src/com/android/compose/gesture/effect/OffsetOverscrollEffect.kt2
-rw-r--r--packages/SystemUI/compose/core/src/com/android/compose/theme/PlatformTheme.kt2
-rw-r--r--packages/SystemUI/compose/core/tests/src/com/android/compose/gesture/NestedDraggableTest.kt34
-rw-r--r--packages/SystemUI/compose/core/tests/src/com/android/compose/gesture/NestedScrollControllerTest.kt35
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PinInputDisplay.kt44
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt9
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContent.kt7
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/LockSection.kt4
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationScrimFlingBehavior.kt73
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt23
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/qs/footer/ui/compose/FooterActions.kt14
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/scene/session/ui/composable/Session.kt92
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/OverlayShade.kt13
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt78
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/ColumnVolumeSliders.kt16
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/VolumeSlider.kt149
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateContent.kt6
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt9
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt27
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt15
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/UserActionDistanceScopeImpl.kt2
-rw-r--r--packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/content/Content.kt15
-rw-r--r--packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_gesture_dragFullyClose.json6
-rw-r--r--packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_gesture_dragHalfClose.json6
-rw-r--r--packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_gesture_flingClose.json50
-rw-r--r--packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_gesture_dragFullyClose.json6
-rw-r--r--packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_gesture_dragHalfClose.json6
-rw-r--r--packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_gesture_flingClose.json44
-rw-r--r--packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/testing/ElementStateAccessTest.kt13
-rw-r--r--packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ComposedDigitalLayerController.kt11
-rw-r--r--packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt4
-rw-r--r--packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockController.kt15
-rw-r--r--packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockFaceController.kt20
-rw-r--r--packages/SystemUI/customization/src/com/android/systemui/shared/clocks/SimpleDigitalHandLayerController.kt11
-rw-r--r--packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/FlexClockView.kt4
-rw-r--r--packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/SimpleDigitalClockTextView.kt15
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityContainerTest.java4
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/MagnificationModeSwitchTest.java13
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/MagnificationSettingsControllerTest.java8
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/MirrorWindowControlTest.java16
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/ModeSwitchesControllerTest.java9
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/DragToInteractAnimationControllerTest.java2
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuListViewTouchHandlerTest.java2
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerControllerTest.java13
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegateTest.java2
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt3
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/AuthControllerTest.java13
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerTest.java4
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractorTest.kt15
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/communal/DeviceInactiveConditionTest.kt1
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalAutoOpenInteractorTest.kt62
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalSettingsInteractorTest.kt16
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/communal/posturing/domain/interactor/PosturingInteractorTest.kt218
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/display/data/repository/DisplayRepositoryTest.kt13
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/display/data/repository/DisplayScopeRepositoryInstanceProviderTest.kt (renamed from packages/SystemUI/multivalentTests/src/com/android/systemui/display/data/repository/DisplayScopeRepositoryImplTest.kt)53
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/display/data/repository/PerDisplayInstanceRepositoryImplTest.kt41
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayServiceTest.kt11
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutCustomizationModeRepositoryTest.kt82
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutHelperViewModelTest.kt50
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractorTest.kt1
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt23
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardKeyEventInteractorTest.kt24
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorTest.kt16
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModelTest.kt12
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/data/repository/MediaFilterRepositoryTest.kt12
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/ui/util/MediaDiffUtilTest.kt52
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/ui/viewmodel/MediaCarouselViewModelTest.kt14
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/mediaprojection/appselector/view/TaskPreviewSizeProviderTest.kt5
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/model/SysUiStateExtTest.kt3
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/navigationbar/views/NavigationBarTest.java4
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayActionsViewModelTest.kt9
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayContentViewModelTest.kt12
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/power/PowerUITest.java8
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qrcodescanner/controller/QRCodeScannerControllerTest.java12
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/domain/interactor/EditTilesListInteractorTest.kt15
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/domain/interactor/NewTilesAvailabilityInteractorTest.kt79
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/domain/interactor/TilesAvailabilityInteractorTest.kt2
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/compose/EditTileListStateTest.kt71
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/compose/selection/MutableSelectionStateTest.kt103
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/viewmodel/DetailsViewModelTest.kt41
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeViewModelTest.kt6
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractorImplTest.kt2
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/CastTileTest.java33
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/ModesDndTileTest.kt8
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/ModesTileTest.kt10
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/domain/actions/QSTileIntentUserInputHandlerTest.kt (renamed from packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserInputHandlerTest.kt)9
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/domain/interactor/DisabledByPolicyInteractorTest.kt (renamed from packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/interactor/DisabledByPolicyInteractorTest.kt)4
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/shared/logging/QSTileLoggerTest.kt (renamed from packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/logging/QSTileLoggerTest.kt)8
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/shared/model/QSTileConfigProviderTest.kt (renamed from packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfigProviderTest.kt)12
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/ui/analytics/QSTileAnalyticsTest.kt (renamed from packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/analytics/QSTileAnalyticsTest.kt)12
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/ui/viewmodel/QSTileViewModelImplTest.kt (renamed from packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelImplTest.kt)22
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/ui/viewmodel/QSTileViewModelTest.kt (renamed from packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelTest.kt)23
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/ui/viewmodel/QSTileViewModelUserInputTest.kt (renamed from packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelUserInputTest.kt)36
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/dialog/CastDetailsViewModelTest.kt77
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/airplane/domain/interactor/AirplaneModeTileDataInteractorTest.kt2
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/airplane/domain/interactor/AirplaneModeTileUserActionInteractorTest.kt8
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/airplane/ui/mapper/AirplaneModeTileMapperTest.kt (renamed from packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/AirplaneModeMapperTest.kt)15
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/alarm/domain/interactor/AlarmTileDataInteractorTest.kt2
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/alarm/domain/interactor/AlarmTileUserActionInteractorTest.kt6
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/alarm/ui/mapper/AlarmTileMapperTest.kt (renamed from packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/alarm/domain/AlarmTileMapperTest.kt)8
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/battery/doman/interactor/BatterySaverTileDataInteractorTest.kt2
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/battery/doman/interactor/BatterySaverTileUserActionInteractorTest.kt6
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/battery/ui/mapper/BatterySaverTileMapperTest.kt (renamed from packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/battery/ui/BatterySaverTileMapperTest.kt)8
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/interactor/ColorCorrectionTileDataInteractorTest.kt2
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/interactor/ColorCorrectionTileUserActionInteractorTest.kt6
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/colorcorrection/ui/mapper/ColorCorrectionTileMapperTest.kt (renamed from packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/ColorCorrectionTileMapperTest.kt)8
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/data/repository/CustomTileDefaultsRepositoryTest.kt (renamed from packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/CustomTileDefaultsRepositoryTest.kt)9
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/data/repository/CustomTilePackageUpdatesRepositoryTest.kt (renamed from packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/CustomTilePackageUpdatesRepositoryTest.kt)12
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/data/repository/CustomTileRepositoryTest.kt4
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileDataInteractorTest.kt4
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileInteractorTest.kt2
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileUserActionInteractorTest.kt14
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/ui/mapper/CustomTileMapperTest.kt (renamed from packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileMapperTest.kt)11
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/flashlight/domain/interactor/FlashlightTileDataInteractorTest.kt3
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/flashlight/domain/interactor/FlashlightTileUserActionInteractorTest.kt2
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/flashlight/ui/mapper/FlashlightMapperTest.kt (renamed from packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/flashlight/domain/FlashlightMapperTest.kt)6
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/fontscaling/domain/interactor/FontScalingTileDataInteractorTest.kt2
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/fontscaling/domain/interactor/FontScalingUserActionInteractorTest.kt8
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/fontscaling/ui/mapper/FontScalingTileMapperTest.kt (renamed from packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/fontscaling/domain/FontScalingTileMapperTest.kt)8
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/interactor/HearingDevicesTileDataInteractorTest.kt2
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/interactor/HearingDevicesTileUserActionInteractorTest.kt6
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/hearingdevices/ui/mapper/HearingDevicesTileMapperTest.kt (renamed from packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/HearingDevicesTileMapperTest.kt)8
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/internet/domain/interactor/InternetTileDataInteractorTest.kt4
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/internet/domain/interactor/InternetTileUserActionInteractorTest.kt8
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/internet/ui/mapper/InternetTileMapperTest.kt (renamed from packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/internet/domain/InternetTileMapperTest.kt)8
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/inversion/domain/interactor/ColorInversionTileDataInteractorTest.kt2
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/inversion/domain/interactor/ColorInversionUserActionInteractorTest.kt6
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/inversion/ui/mapper/ColorInversionTileMapperTest.kt (renamed from packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/inversion/domain/ColorInversionTileMapperTest.kt)8
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/irecording/domain/interactor/IssueRecordingDataInteractorTest.kt (renamed from packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingDataInteractorTest.kt)6
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/irecording/domain/interactor/IssueRecordingUserActionInteractorTest.kt (renamed from packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingUserActionInteractorTest.kt)9
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/irecording/ui/mapper/IssueRecordingMapperTest.kt (renamed from packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingMapperTest.kt)11
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/location/domain/interactor/LocationTileDataInteractorTest.kt (renamed from packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/location/interactor/LocationTileDataInteractorTest.kt)7
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/location/domain/interactor/LocationTileUserActionInteractorTest.kt (renamed from packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/location/interactor/LocationTileUserActionInteractorTest.kt)11
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/location/ui/mapper/LocationTileMapperTest.kt (renamed from packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/location/domain/LocationTileMapperTest.kt)6
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesDndTileDataInteractorTest.kt2
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesDndTileUserActionInteractorTest.kt6
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileDataInteractorTest.kt2
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileUserActionInteractorTest.kt6
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/ui/ModesDndTileMapperTest.kt6
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/ui/mapper/ModesTileMapperTest.kt (renamed from packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/ui/ModesTileMapperTest.kt)10
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/night/domain/interactor/NightDisplayTileUserActionInteractorTest.kt6
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/night/ui/mapper/NightDisplayTileMapperTest.kt (renamed from packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/night/ui/NightDisplayTileMapperTest.kt)10
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/notes/domain/interactor/NotesTileDataInteractorTest.kt2
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/notes/domain/interactor/NotesTileUserActionInteractorTest.kt6
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/notes/ui/mapper/NotesTileMapperTest.kt (renamed from packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/notes/domain/NotesTileMapperTest.kt)8
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/onehanded/domain/interactor/OneHandedModeTileDataInteractorTest.kt2
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/onehanded/domain/interactor/OneHandedModeTileUserActionInteractorTest.kt12
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/onehanded/ui/mapper/OneHandedModeTileMapperTest.kt (renamed from packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/onehanded/ui/OneHandedModeTileMapperTest.kt)8
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/qr/domain/interactor/QRCodeScannerTileDataInteractorTest.kt2
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/qr/domain/interactor/QRCodeScannerTileUserActionInteractorTest.kt6
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/qr/ui/mapper/QRCodeScannerTileMapperTest.kt (renamed from packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/qr/ui/QRCodeScannerTileMapperTest.kt)20
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/reducebrightness/domain/interactor/ReduceBrightColorsTileDataInteractorTest.kt2
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/reducebrightness/domain/interactor/ReduceBrightColorsTileUserActionInteractorTest.kt6
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/reducebrightness/ui/mapper/ReduceBrightColorsTileMapperTest.kt (renamed from packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/reducebrightness/ui/ReduceBrightColorsTileMapperTest.kt)8
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/rotation/domain/interactor/RotationLockTileDataInteractorTest.kt2
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/rotation/domain/interactor/RotationLockTileUserActionInteractorTest.kt21
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/rotation/ui/mapper/RotationLockTileMapperTest.kt4
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/saver/domain/interactor/DataSaverTileDataInteractorTest.kt2
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/saver/domain/interactor/DataSaverTileUserActionInteractorTest.kt6
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/saver/ui/mapper/DataSaverTileMapperTest.kt (renamed from packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/saver/domain/DataSaverTileMapperTest.kt)8
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/interactor/ScreenRecordTileDataInteractorTest.kt2
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/interactor/ScreenRecordTileUserActionInteractorTest.kt2
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/screenrecord/ui/mapper/ScreenRecordTileMapperTest.kt (renamed from packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/screenrecord/ui/ScreenRecordTileMapperTest.kt)8
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/sensorprivacy/domain/interactor/SensorPrivacyToggleTileDataInteractorTest.kt3
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/sensorprivacy/domain/interactor/SensorPrivacyToggleTileUserActionInteractorTest.kt7
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/sensorprivacy/ui/mapper/SensorPrivacyToggleTileMapperTest.kt (renamed from packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/sensorprivacy/ui/SensorPrivacyToggleTileMapperTest.kt)13
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/uimodenight/domain/interactor/UiModeNightTileDataInteractorTest.kt (renamed from packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/uimodenight/domain/UiModeNightTileDataInteractorTest.kt)11
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/uimodenight/domain/interactor/UiModeNightTileUserActionInteractorTest.kt (renamed from packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/uimodenight/domain/UiModeNightTileUserActionInteractorTest.kt)13
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/uimodenight/ui/mapper/UiModeNightTileMapperTest.kt (renamed from packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/uimodenight/domain/UiModeNightTileMapperTest.kt)6
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/work/domain/interactor/WorkModeTileDataInteractorTest.kt2
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/work/domain/interactor/WorkModeTileUserActionInteractorTest.kt12
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/work/ui/mapper/WorkModeTileMapperTest.kt (renamed from packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/work/ui/WorkModeTileMapperTest.kt)8
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsContainerViewModelTest.kt12
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayActionsViewModelTest.kt10
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt7
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerSwipeDetectorTest.kt24
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java5
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java3
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/shade/QuickSettingsControllerImplBaseTest.java9
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ShadeControllerSceneImplTest.kt37
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/shade/carrier/ShadeCarrierGroupControllerTest.java9
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/CommandQueueTest.java32
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationGroupingUtilTest.kt64
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt43
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModelTest.kt47
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/viewmodel/CastToOtherDeviceChipViewModelTest.kt15
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/domain/interactor/SingleNotificationChipInteractorTest.kt15
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/domain/interactor/StatusBarNotificationChipsInteractorTest.kt153
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModelTest.kt278
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/screenrecord/domain/interactor/ScreenRecordChipInteractorTest.kt56
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/screenrecord/ui/viewmodel/ScreenRecordChipViewModelTest.kt6
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/sharetoapp/ui/viewmodel/ShareToAppChipViewModelTest.kt9
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipViewModelTest.kt16
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelTest.kt4
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsWithNotifsViewModelTest.kt123
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/AssistantFeedbackControllerTest.java54
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/BundleEntryAdapterTest.kt15
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryAdapterTest.kt186
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/ColorizedFgsCoordinatorTest.kt98
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/GutsCoordinatorTest.kt64
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.kt18
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/domain/interactor/ActiveNotificationsInteractorTest.kt8
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/domain/interactor/RenderNotificationsListInteractorTest.kt42
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/icon/domain/interactor/NotificationIconsInteractorTest.kt6
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractorImplTest.kt297
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/promoted/domain/interactor/PromotedNotificationsInteractorTest.kt541
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowControllerTest.kt3
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/FeedbackInfoTest.java35
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java30
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.kt78
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.kt75
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationMenuRowTest.java49
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImplTest.kt18
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java5
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/PartialConversationInfoTest.java45
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/PromotedNotificationInfoTest.java29
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/MagneticNotificationRowManagerImplTest.kt70
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelperTest.java16
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt2
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java36
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.kt2
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallbackTest.java9
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt4
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/domain/interactor/OngoingCallInteractorTest.kt6
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ui/IconManagerTest.kt9
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ui/StatusBarIconControllerTest.java30
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/CarrierMergedConnectionRepositoryKairosAdapterTest.kt70
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/CarrierMergedConnectionRepositoryTest.kt86
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractorKairosAdapterTest.kt141
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractorTest.kt55
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorKairosAdapterTest.kt223
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorTest.kt)67
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/LocationBasedMobileIconViewModelKairosTest.kt223
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModelKairosTest.kt1444
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModelKairosTest.kt525
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/StackedMobileIconViewModelKairosTest.kt89
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/StackedMobileIconViewModelTest.kt4
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/topwindoweffects/TopLevelWindowEffectsTest.kt11
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/DisplaySwitchLatencyTrackerTest.kt47
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/user/data/repository/UserRepositoryImplTest.kt155
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/user/domain/interactor/UserSwitcherInteractorTest.kt86
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/user/ui/viewmodel/StatusBarUserChipViewModelTest.kt21
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelTest.kt22
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogVisibilityInteractorTest.kt1
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/volume/dialog/ui/viewmodel/VolumeDialogPluginViewModelTest.kt82
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioSharingStreamSliderViewModelTest.kt9
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioStreamSliderViewModelTest.kt12
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/wallpapers/ImageWallpaperTest.java7
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/window/ui/viewmodel/WindowRootViewModelTest.kt2
-rw-r--r--packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockAnimations.kt3
-rw-r--r--packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockEvents.kt3
-rw-r--r--packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockLogger.kt5
-rw-r--r--packages/SystemUI/res-keyguard/values-bn/strings.xml4
-rw-r--r--packages/SystemUI/res/color/brightness_slider_track.xml19
-rw-r--r--packages/SystemUI/res/drawable/brightness_progress_drawable.xml2
-rw-r--r--packages/SystemUI/res/drawable/hearing_device_ambient_expand_icon_background.xml21
-rw-r--r--packages/SystemUI/res/drawable/hearing_device_ambient_icon_background.xml27
-rw-r--r--packages/SystemUI/res/drawable/rear_display_dialog_seekbar.xml4
-rw-r--r--packages/SystemUI/res/drawable/unpin_icon.xml10
-rw-r--r--packages/SystemUI/res/drawable/vector_drawable_progress_indeterminate_horizontal_trimmed.xml2
-rw-r--r--packages/SystemUI/res/layout/activity_rear_display_enabled.xml10
-rw-r--r--packages/SystemUI/res/layout/biometric_prompt_one_pane_layout.xml3
-rw-r--r--packages/SystemUI/res/layout/biometric_prompt_two_pane_layout.xml3
-rw-r--r--packages/SystemUI/res/layout/hearing_device_ambient_volume_layout.xml6
-rw-r--r--packages/SystemUI/res/layout/promoted_permission_guts.xml73
-rw-r--r--packages/SystemUI/res/layout/volume_dialog.xml7
-rw-r--r--packages/SystemUI/res/layout/volume_dialog_slider_floating.xml4
-rw-r--r--packages/SystemUI/res/layout/volume_dialog_top_section.xml6
-rw-r--r--packages/SystemUI/res/values-af/strings.xml20
-rw-r--r--packages/SystemUI/res/values-af/tiles_states_strings.xml8
-rw-r--r--packages/SystemUI/res/values-am/strings.xml20
-rw-r--r--packages/SystemUI/res/values-am/tiles_states_strings.xml8
-rw-r--r--packages/SystemUI/res/values-ar/strings.xml19
-rw-r--r--packages/SystemUI/res/values-ar/tiles_states_strings.xml8
-rw-r--r--packages/SystemUI/res/values-as/strings.xml20
-rw-r--r--packages/SystemUI/res/values-as/tiles_states_strings.xml8
-rw-r--r--packages/SystemUI/res/values-az/strings.xml19
-rw-r--r--packages/SystemUI/res/values-az/tiles_states_strings.xml8
-rw-r--r--packages/SystemUI/res/values-b+sr+Latn/strings.xml19
-rw-r--r--packages/SystemUI/res/values-b+sr+Latn/tiles_states_strings.xml8
-rw-r--r--packages/SystemUI/res/values-be/strings.xml19
-rw-r--r--packages/SystemUI/res/values-be/tiles_states_strings.xml8
-rw-r--r--packages/SystemUI/res/values-bg/strings.xml19
-rw-r--r--packages/SystemUI/res/values-bg/tiles_states_strings.xml8
-rw-r--r--packages/SystemUI/res/values-bn/strings.xml20
-rw-r--r--packages/SystemUI/res/values-bn/tiles_states_strings.xml8
-rw-r--r--packages/SystemUI/res/values-bs/strings.xml20
-rw-r--r--packages/SystemUI/res/values-bs/tiles_states_strings.xml8
-rw-r--r--packages/SystemUI/res/values-ca/strings.xml20
-rw-r--r--packages/SystemUI/res/values-ca/tiles_states_strings.xml8
-rw-r--r--packages/SystemUI/res/values-cs/strings.xml20
-rw-r--r--packages/SystemUI/res/values-cs/tiles_states_strings.xml8
-rw-r--r--packages/SystemUI/res/values-da/strings.xml22
-rw-r--r--packages/SystemUI/res/values-da/tiles_states_strings.xml8
-rw-r--r--packages/SystemUI/res/values-de/strings.xml20
-rw-r--r--packages/SystemUI/res/values-de/tiles_states_strings.xml8
-rw-r--r--packages/SystemUI/res/values-el/strings.xml31
-rw-r--r--packages/SystemUI/res/values-el/tiles_states_strings.xml8
-rw-r--r--packages/SystemUI/res/values-en-rAU/strings.xml20
-rw-r--r--packages/SystemUI/res/values-en-rAU/tiles_states_strings.xml8
-rw-r--r--packages/SystemUI/res/values-en-rCA/strings.xml11
-rw-r--r--packages/SystemUI/res/values-en-rGB/strings.xml20
-rw-r--r--packages/SystemUI/res/values-en-rGB/tiles_states_strings.xml8
-rw-r--r--packages/SystemUI/res/values-en-rIN/strings.xml20
-rw-r--r--packages/SystemUI/res/values-en-rIN/tiles_states_strings.xml8
-rw-r--r--packages/SystemUI/res/values-es-rUS/strings.xml20
-rw-r--r--packages/SystemUI/res/values-es-rUS/tiles_states_strings.xml8
-rw-r--r--packages/SystemUI/res/values-es/strings.xml20
-rw-r--r--packages/SystemUI/res/values-es/tiles_states_strings.xml8
-rw-r--r--packages/SystemUI/res/values-et/strings.xml20
-rw-r--r--packages/SystemUI/res/values-et/tiles_states_strings.xml8
-rw-r--r--packages/SystemUI/res/values-eu/strings.xml20
-rw-r--r--packages/SystemUI/res/values-eu/tiles_states_strings.xml8
-rw-r--r--packages/SystemUI/res/values-fa/strings.xml19
-rw-r--r--packages/SystemUI/res/values-fa/tiles_states_strings.xml8
-rw-r--r--packages/SystemUI/res/values-fi/strings.xml20
-rw-r--r--packages/SystemUI/res/values-fi/tiles_states_strings.xml8
-rw-r--r--packages/SystemUI/res/values-fr-rCA/strings.xml19
-rw-r--r--packages/SystemUI/res/values-fr-rCA/tiles_states_strings.xml8
-rw-r--r--packages/SystemUI/res/values-fr/strings.xml20
-rw-r--r--packages/SystemUI/res/values-fr/tiles_states_strings.xml8
-rw-r--r--packages/SystemUI/res/values-gl/strings.xml20
-rw-r--r--packages/SystemUI/res/values-gl/tiles_states_strings.xml8
-rw-r--r--packages/SystemUI/res/values-gu/strings.xml31
-rw-r--r--packages/SystemUI/res/values-gu/tiles_states_strings.xml8
-rw-r--r--packages/SystemUI/res/values-hi/strings.xml20
-rw-r--r--packages/SystemUI/res/values-hi/tiles_states_strings.xml8
-rw-r--r--packages/SystemUI/res/values-hr/strings.xml20
-rw-r--r--packages/SystemUI/res/values-hr/tiles_states_strings.xml8
-rw-r--r--packages/SystemUI/res/values-hu/strings.xml20
-rw-r--r--packages/SystemUI/res/values-hu/tiles_states_strings.xml8
-rw-r--r--packages/SystemUI/res/values-hy/strings.xml20
-rw-r--r--packages/SystemUI/res/values-hy/tiles_states_strings.xml8
-rw-r--r--packages/SystemUI/res/values-in/strings.xml20
-rw-r--r--packages/SystemUI/res/values-in/tiles_states_strings.xml8
-rw-r--r--packages/SystemUI/res/values-is/strings.xml20
-rw-r--r--packages/SystemUI/res/values-is/tiles_states_strings.xml8
-rw-r--r--packages/SystemUI/res/values-it/strings.xml19
-rw-r--r--packages/SystemUI/res/values-it/tiles_states_strings.xml8
-rw-r--r--packages/SystemUI/res/values-iw/strings.xml19
-rw-r--r--packages/SystemUI/res/values-iw/tiles_states_strings.xml8
-rw-r--r--packages/SystemUI/res/values-ja/strings.xml19
-rw-r--r--packages/SystemUI/res/values-ja/tiles_states_strings.xml8
-rw-r--r--packages/SystemUI/res/values-ka/strings.xml21
-rw-r--r--packages/SystemUI/res/values-ka/tiles_states_strings.xml8
-rw-r--r--packages/SystemUI/res/values-kk/strings.xml20
-rw-r--r--packages/SystemUI/res/values-kk/tiles_states_strings.xml8
-rw-r--r--packages/SystemUI/res/values-km/strings.xml19
-rw-r--r--packages/SystemUI/res/values-km/tiles_states_strings.xml8
-rw-r--r--packages/SystemUI/res/values-kn/strings.xml19
-rw-r--r--packages/SystemUI/res/values-kn/tiles_states_strings.xml8
-rw-r--r--packages/SystemUI/res/values-ko/strings.xml20
-rw-r--r--packages/SystemUI/res/values-ko/tiles_states_strings.xml8
-rw-r--r--packages/SystemUI/res/values-ky/strings.xml19
-rw-r--r--packages/SystemUI/res/values-ky/tiles_states_strings.xml8
-rw-r--r--packages/SystemUI/res/values-lo/strings.xml19
-rw-r--r--packages/SystemUI/res/values-lo/tiles_states_strings.xml8
-rw-r--r--packages/SystemUI/res/values-lt/strings.xml20
-rw-r--r--packages/SystemUI/res/values-lt/tiles_states_strings.xml8
-rw-r--r--packages/SystemUI/res/values-lv/strings.xml20
-rw-r--r--packages/SystemUI/res/values-lv/tiles_states_strings.xml8
-rw-r--r--packages/SystemUI/res/values-mk/strings.xml19
-rw-r--r--packages/SystemUI/res/values-mk/tiles_states_strings.xml8
-rw-r--r--packages/SystemUI/res/values-ml/strings.xml19
-rw-r--r--packages/SystemUI/res/values-ml/tiles_states_strings.xml8
-rw-r--r--packages/SystemUI/res/values-mn/strings.xml20
-rw-r--r--packages/SystemUI/res/values-mn/tiles_states_strings.xml8
-rw-r--r--packages/SystemUI/res/values-mr/strings.xml19
-rw-r--r--packages/SystemUI/res/values-mr/tiles_states_strings.xml8
-rw-r--r--packages/SystemUI/res/values-ms/strings.xml19
-rw-r--r--packages/SystemUI/res/values-ms/tiles_states_strings.xml8
-rw-r--r--packages/SystemUI/res/values-my/strings.xml20
-rw-r--r--packages/SystemUI/res/values-my/tiles_states_strings.xml8
-rw-r--r--packages/SystemUI/res/values-nb/strings.xml20
-rw-r--r--packages/SystemUI/res/values-nb/tiles_states_strings.xml8
-rw-r--r--packages/SystemUI/res/values-ne/strings.xml20
-rw-r--r--packages/SystemUI/res/values-ne/tiles_states_strings.xml8
-rw-r--r--packages/SystemUI/res/values-nl/strings.xml20
-rw-r--r--packages/SystemUI/res/values-nl/tiles_states_strings.xml8
-rw-r--r--packages/SystemUI/res/values-or/strings.xml21
-rw-r--r--packages/SystemUI/res/values-or/tiles_states_strings.xml8
-rw-r--r--packages/SystemUI/res/values-pa/strings.xml19
-rw-r--r--packages/SystemUI/res/values-pa/tiles_states_strings.xml8
-rw-r--r--packages/SystemUI/res/values-pl/strings.xml20
-rw-r--r--packages/SystemUI/res/values-pl/tiles_states_strings.xml8
-rw-r--r--packages/SystemUI/res/values-pt-rBR/strings.xml20
-rw-r--r--packages/SystemUI/res/values-pt-rBR/tiles_states_strings.xml8
-rw-r--r--packages/SystemUI/res/values-pt-rPT/strings.xml19
-rw-r--r--packages/SystemUI/res/values-pt-rPT/tiles_states_strings.xml8
-rw-r--r--packages/SystemUI/res/values-pt/strings.xml20
-rw-r--r--packages/SystemUI/res/values-pt/tiles_states_strings.xml8
-rw-r--r--packages/SystemUI/res/values-ro/strings.xml20
-rw-r--r--packages/SystemUI/res/values-ro/tiles_states_strings.xml8
-rw-r--r--packages/SystemUI/res/values-ru/strings.xml19
-rw-r--r--packages/SystemUI/res/values-ru/tiles_states_strings.xml8
-rw-r--r--packages/SystemUI/res/values-si/strings.xml19
-rw-r--r--packages/SystemUI/res/values-si/tiles_states_strings.xml8
-rw-r--r--packages/SystemUI/res/values-sk/strings.xml20
-rw-r--r--packages/SystemUI/res/values-sk/tiles_states_strings.xml8
-rw-r--r--packages/SystemUI/res/values-sl/strings.xml19
-rw-r--r--packages/SystemUI/res/values-sl/tiles_states_strings.xml8
-rw-r--r--packages/SystemUI/res/values-sq/strings.xml20
-rw-r--r--packages/SystemUI/res/values-sq/tiles_states_strings.xml8
-rw-r--r--packages/SystemUI/res/values-sr/strings.xml19
-rw-r--r--packages/SystemUI/res/values-sr/tiles_states_strings.xml8
-rw-r--r--packages/SystemUI/res/values-sv/strings.xml20
-rw-r--r--packages/SystemUI/res/values-sv/tiles_states_strings.xml8
-rw-r--r--packages/SystemUI/res/values-sw/strings.xml20
-rw-r--r--packages/SystemUI/res/values-sw/tiles_states_strings.xml8
-rw-r--r--packages/SystemUI/res/values-ta/strings.xml20
-rw-r--r--packages/SystemUI/res/values-ta/tiles_states_strings.xml8
-rw-r--r--packages/SystemUI/res/values-te/strings.xml19
-rw-r--r--packages/SystemUI/res/values-te/tiles_states_strings.xml8
-rw-r--r--packages/SystemUI/res/values-th/strings.xml19
-rw-r--r--packages/SystemUI/res/values-th/tiles_states_strings.xml8
-rw-r--r--packages/SystemUI/res/values-tl/strings.xml19
-rw-r--r--packages/SystemUI/res/values-tl/tiles_states_strings.xml8
-rw-r--r--packages/SystemUI/res/values-tr/strings.xml20
-rw-r--r--packages/SystemUI/res/values-tr/tiles_states_strings.xml8
-rw-r--r--packages/SystemUI/res/values-uk/strings.xml20
-rw-r--r--packages/SystemUI/res/values-uk/tiles_states_strings.xml8
-rw-r--r--packages/SystemUI/res/values-ur/strings.xml20
-rw-r--r--packages/SystemUI/res/values-ur/tiles_states_strings.xml8
-rw-r--r--packages/SystemUI/res/values-uz/strings.xml19
-rw-r--r--packages/SystemUI/res/values-uz/tiles_states_strings.xml8
-rw-r--r--packages/SystemUI/res/values-vi/strings.xml20
-rw-r--r--packages/SystemUI/res/values-vi/tiles_states_strings.xml8
-rw-r--r--packages/SystemUI/res/values-zh-rCN/strings.xml22
-rw-r--r--packages/SystemUI/res/values-zh-rCN/tiles_states_strings.xml8
-rw-r--r--packages/SystemUI/res/values-zh-rHK/strings.xml20
-rw-r--r--packages/SystemUI/res/values-zh-rHK/tiles_states_strings.xml8
-rw-r--r--packages/SystemUI/res/values-zh-rTW/strings.xml20
-rw-r--r--packages/SystemUI/res/values-zh-rTW/tiles_states_strings.xml8
-rw-r--r--packages/SystemUI/res/values-zu/strings.xml20
-rw-r--r--packages/SystemUI/res/values-zu/tiles_states_strings.xml8
-rw-r--r--packages/SystemUI/res/values/colors.xml3
-rw-r--r--packages/SystemUI/res/values/dimens.xml21
-rw-r--r--packages/SystemUI/res/values/strings.xml20
-rw-r--r--packages/SystemUI/res/values/styles.xml3
-rw-r--r--packages/SystemUI/shared/biometrics/Android.bp3
-rw-r--r--packages/SystemUI/shared/biometrics/src/com/android/systemui/biometrics/Utils.kt8
-rw-r--r--packages/SystemUI/shared/res/values/bools.xml3
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/PreviewPositionHelper.java15
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java3
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/rotation/FloatingRotationButton.java11
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java10
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardViewController.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/ScreenDecorations.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/SwipeHelper.java21
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/MagnificationImpl.java45
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/MagnificationModeSwitch.java23
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/MagnificationSettingsController.java13
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/MirrorWindowControl.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/ModeSwitchesController.java19
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/SimpleMirrorWindowControl.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationSettings.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuController.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/DragToInteractView.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewAppearance.java15
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayer.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerController.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegate.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/qs/QSAccessibilityModule.kt26
-rw-r--r--packages/SystemUI/src/com/android/systemui/ambient/touch/scrim/BouncerScrimController.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/assist/AssistDisclosure.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/assist/AssistManager.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/assist/ui/DefaultUiController.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/battery/BatterySaverModule.kt14
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java13
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java16
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java22
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewSizeBinder.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractor.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/charging/WiredChargingRippleController.kt10
-rw-r--r--packages/SystemUI/src/com/android/systemui/charging/WirelessChargingAnimation.java29
-rw-r--r--packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayWindow.java18
-rw-r--r--packages/SystemUI/src/com/android/systemui/clipboardoverlay/dagger/ClipboardOverlayModule.java23
-rw-r--r--packages/SystemUI/src/com/android/systemui/common/domain/interactor/SysUIStateDisplaysInteractor.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/common/shared/model/Icon.kt34
-rw-r--r--packages/SystemUI/src/com/android/systemui/common/ui/compose/gestures/EagerTap.kt91
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/DeviceInactiveCondition.kt11
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/DevicePosturingListener.kt32
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalSettingsRepository.kt36
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalAutoOpenInteractor.kt26
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSettingsInteractor.kt9
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/posturing/data/model/PositionState.kt48
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/posturing/data/repository/NoOpPosturingRepository.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/posturing/data/repository/PosturingRepository.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/posturing/domain/interactor/PosturingInteractor.kt202
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/posturing/shared/model/PosturedState.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/shared/model/WhenToStartHub.kt24
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalLockIconViewModel.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/dagger/ControlsModule.kt9
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java50
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/PerDisplayRepositoriesModule.kt9
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/deviceentry/ui/view/UdfpsAccessibilityOverlay.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/deviceentry/ui/viewmodel/UdfpsAccessibilityOverlayViewModel.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/display/DisplayModule.kt32
-rw-r--r--packages/SystemUI/src/com/android/systemui/display/dagger/PerDisplayCommonModule.kt65
-rw-r--r--packages/SystemUI/src/com/android/systemui/display/dagger/SystemUIDisplaySubcomponent.kt55
-rw-r--r--packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayComponentRepository.kt58
-rw-r--r--packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayRepository.kt94
-rw-r--r--packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayScopeRepository.kt64
-rw-r--r--packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayWindowPropertiesRepository.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/display/data/repository/DisplaysWithDecorationsRepository.kt103
-rw-r--r--packages/SystemUI/src/com/android/systemui/display/data/repository/FakePerDisplayRepository.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/display/data/repository/PerDisplayRepoDumpHelper.kt1
-rw-r--r--packages/SystemUI/src/com/android/systemui/display/data/repository/PerDisplayRepository.kt223
-rw-r--r--packages/SystemUI/src/com/android/systemui/display/data/repository/PerDisplayStore.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/doze/DozeUi.java19
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyboard/docking/binder/KeyboardDockingIndicationViewBinder.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutHelperCustomizationModeRepository.kt38
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutHelperCustomizationModeInteractor.kt32
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/ShortcutHelperDialogStarter.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/ShortcutHelper.kt68
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/model/ShortcutsUiState.kt1
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutHelperViewModel.kt19
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java80
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/KeyguardWmReorderAtmsCalls.kt53
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/KeyguardWmStateRefactor.kt14
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractor.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt17
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardKeyEventInteractor.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt46
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractor.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt71
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/DeviceEntryIconViewBinder.kt50
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/view/SideFpsProgressBar.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySection.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerUdfpsIconViewModel.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModel.kt9
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToGlanceableHubTransitionViewModel.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToOccludedTransitionViewModel.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModel.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToOccludedTransitionViewModel.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModel.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/log/dagger/QSTilesVerboseLog.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/lowlightclock/DirectBootCondition.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/lowlightclock/ForceLowLightCondition.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/lowlightclock/LowLightCondition.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/lowlightclock/ScreenSaverEnabledCondition.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataFilterImpl.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/shared/model/MediaDataLoadingModel.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselController.kt96
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/ui/util/MediaViewModelCallback.kt25
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/ui/util/MediaViewModelListUpdateCallback.kt14
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaCarouselViewModel.kt38
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaCommonViewModel.kt38
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaControlViewModel.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapterBase.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapterLegacy.java17
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/dialog/MediaSwitchingController.java18
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/remedia/ui/compose/DismissibleHorizontalPager.kt160
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/remedia/ui/compose/Media.kt191
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/remedia/ui/compose/SwipeToDismiss.kt115
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/remedia/ui/compose/SwipeToReveal.kt264
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/remedia/ui/viewmodel/MediaSettingsButtonViewModel.kt21
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/remedia/ui/viewmodel/MediaViewModel.kt15
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/MediaProjectionTaskView.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/TaskPreviewSizeProvider.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/mediaprojection/permission/BaseMediaProjectionPermissionViewBinder.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/model/SceneContainerPlugin.kt25
-rw-r--r--packages/SystemUI/src/com/android/systemui/model/SysUiState.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/model/SysUiStateExt.kt14
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarModule.java25
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/views/NavigationBar.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/views/NavigationBarView.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInitializer.kt24
-rw-r--r--packages/SystemUI/src/com/android/systemui/notetask/NoteTaskModule.kt14
-rw-r--r--packages/SystemUI/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayActionsViewModel.kt10
-rw-r--r--packages/SystemUI/src/com/android/systemui/power/InattentiveSleepWarningView.java15
-rw-r--r--packages/SystemUI/src/com/android/systemui/power/PowerUI.java14
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt140
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/composefragment/ui/NotificationScrimClip.kt67
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/composefragment/ui/NotificationScrimClipParams.kt26
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModel.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/dagger/QSModuleBase.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/EditTilesListInteractor.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/NewTilesAvailabilityInteractor.kt35
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/DragAndDropState.kt14
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/EditTileListState.kt60
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/QuickQuickSettings.kt1
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/TileDetails.kt44
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/CommonTile.kt62
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/EditTile.kt251
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/InfiniteGridLayout.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/Tile.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/selection/MutableSelectionState.kt146
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/selection/Selection.kt74
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/DetailsViewModel.kt10
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/EditTileViewModel.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/pipeline/dagger/QSPipelineModule.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractor.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java54
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/ModesDndTile.kt11
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/ModesTile.kt16
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/NotesTile.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/base/domain/actions/QSTileIntentUserInputHandler.kt (renamed from packages/SystemUI/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserInputHandler.kt)16
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/base/domain/interactor/DisabledByPolicyInteractor.kt (renamed from packages/SystemUI/src/com/android/systemui/qs/tiles/base/interactor/DisabledByPolicyInteractor.kt)18
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/base/domain/interactor/QSTileDataInteractor.kt (renamed from packages/SystemUI/src/com/android/systemui/qs/tiles/base/interactor/QSTileDataInteractor.kt)14
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/base/domain/interactor/QSTileUserActionInteractor.kt (renamed from packages/SystemUI/src/com/android/systemui/qs/tiles/base/interactor/QSTileUserActionInteractor.kt)6
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/base/domain/model/DataUpdateTrigger.kt (renamed from packages/SystemUI/src/com/android/systemui/qs/tiles/base/interactor/DataUpdateTrigger.kt)4
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/base/domain/model/QSTileInput.kt (renamed from packages/SystemUI/src/com/android/systemui/qs/tiles/base/interactor/QSTileInput.kt)12
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/base/shared/logging/QSTileLogger.kt (renamed from packages/SystemUI/src/com/android/systemui/qs/tiles/base/logging/QSTileLogger.kt)55
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/base/shared/model/QSTileConfig.kt (renamed from packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfig.kt)4
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/base/shared/model/QSTileConfigProvider.kt (renamed from packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfigProvider.kt)6
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/base/shared/model/QSTileCoroutineScopeFactory.kt (renamed from packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileCoroutineScopeFactory.kt)2
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/base/shared/model/QSTileScope.kt (renamed from packages/SystemUI/src/com/android/systemui/qs/tiles/impl/di/QSTileScope.kt)2
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/base/shared/model/QSTileState.kt (renamed from packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileState.kt)4
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/base/shared/model/QSTileUserAction.kt (renamed from packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileUserAction.kt)4
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/base/ui/analytics/QSTileAnalytics.kt (renamed from packages/SystemUI/src/com/android/systemui/qs/tiles/base/analytics/QSTileAnalytics.kt)12
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/base/ui/model/NewQSTileFactory.kt (renamed from packages/SystemUI/src/com/android/systemui/qs/tiles/di/NewQSTileFactory.kt)17
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/base/ui/model/QSTileComponent.kt (renamed from packages/SystemUI/src/com/android/systemui/qs/tiles/impl/di/QSTileComponent.kt)11
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/base/ui/model/QSTileDataToStateMapper.kt (renamed from packages/SystemUI/src/com/android/systemui/qs/tiles/base/interactor/QSTileDataToStateMapper.kt)8
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/base/ui/model/QSTilesModule.kt (renamed from packages/SystemUI/src/com/android/systemui/qs/tiles/di/QSTilesModule.kt)27
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/base/ui/viewmodel/QSTileViewModel.kt (renamed from packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModel.kt)7
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/base/ui/viewmodel/QSTileViewModelAdapter.kt (renamed from packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelAdapter.kt)8
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/base/ui/viewmodel/QSTileViewModelFactory.kt (renamed from packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelFactory.kt)27
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/base/ui/viewmodel/QSTileViewModelImpl.kt (renamed from packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelImpl.kt)30
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/base/ui/viewmodel/StubQSTileViewModel.kt (renamed from packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/StubQSTileViewModel.kt)7
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/CastDetailsContent.kt53
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/CastDetailsViewModel.kt78
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDetailsContentController.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDetailsViewModel.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/airplane/domain/interactor/AirplaneModeTileDataInteractor.kt11
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/airplane/domain/interactor/AirplaneModeTileUserActionInteractor.kt10
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/airplane/ui/mapper/AirplaneModeTileMapper.kt (renamed from packages/SystemUI/src/com/android/systemui/qs/tiles/impl/airplane/domain/AirplaneModeMapper.kt)12
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/alarm/domain/interactor/AlarmTileDataInteractor.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/alarm/domain/interactor/AlarmTileUserActionInteractor.kt15
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/alarm/ui/mapper/AlarmTileMapper.kt (renamed from packages/SystemUI/src/com/android/systemui/qs/tiles/impl/alarm/domain/AlarmTileMapper.kt)10
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/battery/domain/interactor/BatterySaverTileDataInteractor.kt11
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/battery/domain/interactor/BatterySaverTileUserActionInteractor.kt12
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/battery/ui/mapper/BatterySaverTileMapper.kt (renamed from packages/SystemUI/src/com/android/systemui/qs/tiles/impl/battery/ui/BatterySaverTileMapper.kt)8
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/interactor/ColorCorrectionTileDataInteractor.kt12
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/interactor/ColorCorrectionUserActionInteractor.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/colorcorrection/ui/mapper/ColorCorrectionTileMapper.kt (renamed from packages/SystemUI/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/ColorCorrectionTileMapper.kt)8
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/data/model/CustomTileDefaults.kt (renamed from packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/data/entity/CustomTileDefaults.kt)8
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/data/repository/CustomTileDefaultsRepository.kt20
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/data/repository/CustomTilePackageUpdatesRepository.kt9
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/data/repository/CustomTileRepository.kt31
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileDataInteractor.kt12
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileInteractor.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileServiceInteractor.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileUserActionInteractor.kt25
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/model/CustomTileDataModel.kt (renamed from packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/entity/CustomTileDataModel.kt)2
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/shared/model/QSTileConfigModule.kt (renamed from packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/QSTileConfigModule.kt)4
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/shared/model/TileExt.kt (renamed from packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/commons/TileExt.kt)2
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/ui/mapper/CustomTileMapper.kt (renamed from packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/CustomTileMapper.kt)17
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/ui/model/CustomTileComponent.kt (renamed from packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/CustomTileComponent.kt)11
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/ui/model/CustomTileModule.kt (renamed from packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/CustomTileModule.kt)18
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/flashlight/domain/interactor/FlashlightTileDataInteractor.kt15
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/flashlight/domain/interactor/FlashlightTileUserActionInteractor.kt11
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/flashlight/ui/mapper/FlashlightMapper.kt (renamed from packages/SystemUI/src/com/android/systemui/qs/tiles/impl/flashlight/domain/FlashlightMapper.kt)8
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/fontscaling/domain/interactor/FontScalingTileDataInteractor.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/fontscaling/domain/interactor/FontScalingTileUserActionInteractor.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/fontscaling/ui/mapper/FontScalingTileMapper.kt (renamed from packages/SystemUI/src/com/android/systemui/qs/tiles/impl/fontscaling/domain/FontScalingTileMapper.kt)8
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/interactor/HearingDevicesTileDataInteractor.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/interactor/HearingDevicesTileUserActionInteractor.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/hearingdevices/ui/mapper/HearingDevicesTileMapper.kt (renamed from packages/SystemUI/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/HearingDevicesTileMapper.kt)8
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/internet/domain/interactor/InternetTileDataInteractor.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/internet/domain/interactor/InternetTileUserActionInteractor.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/internet/ui/mapper/InternetTileMapper.kt (renamed from packages/SystemUI/src/com/android/systemui/qs/tiles/impl/internet/domain/InternetTileMapper.kt)8
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/inversion/domain/interactor/ColorInversionTileDataInteractor.kt12
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/inversion/domain/interactor/ColorInversionUserActionInteractor.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/inversion/ui/mapper/ColorInversionTileMapper.kt (renamed from packages/SystemUI/src/com/android/systemui/qs/tiles/impl/inversion/domain/ColorInversionTileMapper.kt)8
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/irecording/data/model/IssueRecordingModel.kt (renamed from packages/SystemUI/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingModel.kt)2
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/irecording/domain/interactor/IssueRecordingDataInteractor.kt (renamed from packages/SystemUI/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingDataInteractor.kt)9
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/irecording/domain/interactor/IssueRecordingUserActionInteractor.kt (renamed from packages/SystemUI/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingUserActionInteractor.kt)15
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/irecording/ui/mapper/IssueRecordingMapper.kt (renamed from packages/SystemUI/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingMapper.kt)11
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/location/domain/interactor/LocationTileDataInteractor.kt11
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/location/domain/interactor/LocationTileUserActionInteractor.kt12
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/location/ui/mapper/LocationTileMapper.kt (renamed from packages/SystemUI/src/com/android/systemui/qs/tiles/impl/location/domain/LocationTileMapper.kt)8
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesDndTileDataInteractor.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesDndTileUserActionInteractor.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileDataInteractor.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileUserActionInteractor.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/ui/ModesDndTileMapper.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/ui/mapper/ModesTileMapper.kt (renamed from packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/ui/ModesTileMapper.kt)8
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/night/domain/interactor/NightDisplayTileDataInteractor.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/night/domain/interactor/NightDisplayTileUserActionInteractor.kt12
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/night/ui/mapper/NightDisplayTileMapper.kt (renamed from packages/SystemUI/src/com/android/systemui/qs/tiles/impl/night/ui/NightDisplayTileMapper.kt)12
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/notes/domain/interactor/NotesTileDataInteractor.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/notes/domain/interactor/NotesTileUserActionInteractor.kt11
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/notes/ui/mapper/NotesTileMapper.kt (renamed from packages/SystemUI/src/com/android/systemui/qs/tiles/impl/notes/domain/NotesTileMapper.kt)8
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/onehanded/domain/OneHandedModeTileDataInteractor.kt12
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/onehanded/domain/OneHandedModeTileUserActionInteractor.kt15
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/onehanded/ui/mapper/OneHandedModeTileMapper.kt (renamed from packages/SystemUI/src/com/android/systemui/qs/tiles/impl/onehanded/ui/OneHandedModeTileMapper.kt)8
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/qr/domain/interactor/QRCodeScannerTileDataInteractor.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/qr/domain/interactor/QRCodeScannerTileUserActionInteractor.kt15
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/qr/ui/mapper/QRCodeScannerTileMapper.kt (renamed from packages/SystemUI/src/com/android/systemui/qs/tiles/impl/qr/ui/QRCodeScannerTileMapper.kt)8
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/qr/ui/model/QRCodeScannerModule.kt (renamed from packages/SystemUI/src/com/android/systemui/qrcodescanner/dagger/QRCodeScannerModule.kt)20
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/reducebrightness/domain/interactor/ReduceBrightColorsTileDataInteractor.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/reducebrightness/domain/interactor/ReduceBrightColorsTileUserActionInteractor.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/reducebrightness/ui/mapper/ReduceBrightColorsTileMapper.kt (renamed from packages/SystemUI/src/com/android/systemui/qs/tiles/impl/reducebrightness/ui/ReduceBrightColorsTileMapper.kt)8
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/rotation/domain/interactor/RotationLockTileDataInteractor.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/rotation/domain/interactor/RotationLockTileUserActionInteractor.kt10
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/rotation/ui/mapper/RotationLockTileMapper.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/saver/domain/interactor/DataSaverTileDataInteractor.kt11
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/saver/domain/interactor/DataSaverTileUserActionInteractor.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/saver/ui/mapper/DataSaverTileMapper.kt (renamed from packages/SystemUI/src/com/android/systemui/qs/tiles/impl/saver/domain/DataSaverTileMapper.kt)8
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/interactor/ScreenRecordTileDataInteractor.kt11
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/interactor/ScreenRecordTileUserActionInteractor.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/ui/mapper/ScreenRecordTileMapper.kt (renamed from packages/SystemUI/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/ui/ScreenRecordTileMapper.kt)8
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/sensorprivacy/domain/interactor/SensorPrivacyToggleTileDataInteractor.kt (renamed from packages/SystemUI/src/com/android/systemui/qs/tiles/impl/sensorprivacy/SensorPrivacyToggleTileDataInteractor.kt)16
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/sensorprivacy/domain/interactor/SensorPrivacyToggleTileUserActionInteractor.kt (renamed from packages/SystemUI/src/com/android/systemui/qs/tiles/impl/sensorprivacy/domain/SensorPrivacyToggleTileUserActionInteractor.kt)12
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/sensorprivacy/ui/mapper/SensorPrivacyToggleTileMapper.kt (renamed from packages/SystemUI/src/com/android/systemui/qs/tiles/impl/sensorprivacy/ui/SensorPrivacyToggleTileMapper.kt)11
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/sensorprivacy/ui/model/SensorPrivacyTileResources.kt (renamed from packages/SystemUI/src/com/android/systemui/qs/tiles/impl/sensorprivacy/ui/SensorPrivacyTileResources.kt)5
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/uimodenight/domain/interactor/UiModeNightTileDataInteractor.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/uimodenight/domain/interactor/UiModeNightTileUserActionInteractor.kt12
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/uimodenight/domain/interactor/model/UiModeNightTileModel.kt (renamed from packages/SystemUI/src/com/android/systemui/qs/tiles/impl/uimodenight/domain/model/UiModeNightTileModel.kt)4
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/uimodenight/ui/mapper/UiModeNightTileMapper.kt (renamed from packages/SystemUI/src/com/android/systemui/qs/tiles/impl/uimodenight/domain/UiModeNightTileMapper.kt)12
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/work/domain/interactor/WorkModeTileDataInteractor.kt11
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/work/domain/interactor/WorkModeTileUserActionInteractor.kt10
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/impl/work/ui/mapper/WorkModeTileMapper.kt (renamed from packages/SystemUI/src/com/android/systemui/qs/tiles/impl/work/ui/WorkModeTileMapper.kt)8
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsContainerViewModel.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayActionsViewModel.kt11
-rw-r--r--packages/SystemUI/src/com/android/systemui/reardisplay/RearDisplayCoreStartable.kt13
-rw-r--r--packages/SystemUI/src/com/android/systemui/reardisplay/RearDisplayInnerDialogDelegate.kt26
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/LauncherProxyService.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java16
-rw-r--r--packages/SystemUI/src/com/android/systemui/recordissue/RecordIssueModule.kt22
-rw-r--r--packages/SystemUI/src/com/android/systemui/rotationlock/RotationLockNewModule.kt12
-rw-r--r--packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt26
-rw-r--r--packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerSwipeDetector.kt33
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordModule.kt14
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotWindow.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/scrim/ScrimView.java14
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/QuickSettingsControllerImpl.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/ShadeControllerSceneImpl.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/ShadeDisplayAwareModule.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/carrier/ShadeCarrierGroupController.java178
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImpl.kt36
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModel.kt17
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeUserActions.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/BlurUtils.kt85
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java43
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/ImmersiveModeConfirmation.java38
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutListSearch.java26
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java23
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutsReceiver.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationGroupingUtil.java29
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt49
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModel.kt39
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/viewmodel/CastToOtherDeviceChipViewModel.kt36
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/chips/mediaprojection/ui/view/EndMediaProjectionDialogHelper.kt16
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/domain/interactor/SingleNotificationChipInteractor.kt1
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/domain/interactor/StatusBarNotificationChipsInteractor.kt13
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/domain/model/NotificationChipModel.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModel.kt113
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/chips/screenrecord/domain/interactor/ScreenRecordChipInteractor.kt24
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/chips/screenrecord/domain/model/ScreenRecordChipModel.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/chips/screenrecord/ui/viewmodel/ScreenRecordChipViewModel.kt22
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/chips/sharetoapp/ui/viewmodel/ShareToAppChipViewModel.kt35
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/compose/OngoingActivityChip.kt54
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/model/ColorsModel.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/model/OngoingActivityChipModel.kt32
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipViewModel.kt12
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModel.kt16
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/chips/uievents/StatusBarChipUiEvent.kt45
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/chips/uievents/StatusBarChipsUiEventLogger.kt122
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/connectivity/ConnectivityModule.kt26
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/core/MultiDisplayStatusBarOrchestratorStore.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/data/repository/LightBarControllerStore.kt12
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/data/repository/PrivacyDotViewControllerStore.kt11
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/data/repository/PrivacyDotWindowControllerStore.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarPerDisplayConfigurationStateRepository.kt9
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotWindowController.kt10
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/AssistantFeedbackController.java17
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClicker.java34
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/OWNERS1
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/BundleEntry.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/BundleEntryAdapter.kt65
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/EntryAdapter.java47
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/EntryAdapterFactoryImpl.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java15
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntryAdapter.kt60
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ColorizedFgsCoordinator.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/GroupCountCoordinator.kt15
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/GutsCoordinator.kt119
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.kt87
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RowAppearanceCoordinator.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/OnUserInteractionCallbackImpl.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NotifGutsViewListener.kt11
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/RenderNotificationListInteractor.kt29
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterView.java34
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/viewbinder/FooterViewBinder.kt9
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterViewModel.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerStatusBarViewBinder.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/AODPromotedNotification.kt16
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractor.kt108
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationLogger.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/domain/interactor/AODPromotedNotificationInteractor.kt11
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/domain/interactor/PromotedNotificationsInteractor.kt180
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/shared/model/PromotedNotificationContentModel.kt81
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java78
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableOutlineView.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java25
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FeedbackInfo.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridConversationNotificationView.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/MagicActionBackgroundDrawable.kt62
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java21
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java48
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java128
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java33
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java69
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImpl.kt14
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PartialConversationInfo.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PromotedNotificationInfo.java19
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PromotedPermissionGutsContent.java173
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/icon/NotificationIconStyleProvider.kt12
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/ActiveNotificationModel.kt10
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MagneticNotificationRowManager.kt20
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MagneticNotificationRowManagerImpl.kt64
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MagneticRowListener.kt13
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java14
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java47
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/view/NotificationStatsLoggerImpl.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModel.kt18
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java37
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java80
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java17
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt16
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java15
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java40
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/domain/interactor/OngoingCallInteractor.kt1
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/shared/model/OngoingCallModel.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/ui/DarkIconManager.java30
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/ui/IconManager.java79
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/ui/TintedIconManager.java35
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepositoryKairos.kt19
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryKairosImpl.kt15
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractor.kt30
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractorKairos.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorKairosAdapter.kt44
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/MobileUiAdapterKairos.kt95
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/MobileViewLogger.kt39
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/StackedMobileBindableIcon.kt18
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/binder/MobileIconBinder.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/binder/MobileIconBinderKairos.kt290
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/binder/ShadeCarrierBinder.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/binder/ShadeCarrierBinderKairos.kt42
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/binder/StackedMobileIconBinder.kt23
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/view/ModernShadeCarrierGroupMobileView.kt60
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/view/ModernStatusBarMobileView.kt62
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/LocationBasedMobileViewModelKairos.kt70
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModelKairos.kt372
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModelKairos.kt202
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/StackedMobileIconViewModel.kt30
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/StackedMobileIconViewModelKairos.kt92
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/util/FakeMobileMappingsProxy.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModel.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/BaseUserSwitcherAdapter.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingController.java19
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/PolicyModule.kt36
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java39
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowController.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowControllerImpl.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowControllerStore.kt10
-rw-r--r--packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayController.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinator.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/topwindoweffects/TopLevelWindowEffects.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/unfold/DisplaySwitchLatencyLogger.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/unfold/DisplaySwitchLatencyTracker.kt23
-rw-r--r--packages/SystemUI/src/com/android/systemui/unfold/data/repository/ScreenTimeoutPolicyRepository.kt61
-rw-r--r--packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepository.kt15
-rw-r--r--packages/SystemUI/src/com/android/systemui/user/data/source/UserRecord.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserSwitcherInteractor.kt22
-rw-r--r--packages/SystemUI/src/com/android/systemui/user/legacyhelper/data/LegacyUserDataHelper.kt14
-rw-r--r--packages/SystemUI/src/com/android/systemui/user/legacyhelper/ui/LegacyUserUiHelper.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/user/shared/model/UserActionModel.kt1
-rw-r--r--packages/SystemUI/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModel.kt10
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/AlarmTimeout.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/ConvenienceExtensions.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/composable/kairos/ActivatedKairosSpec.kt60
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/composable/kairos/HydratedComposeStateOf.kt31
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/composable/kairos/RememberKairosActivatable.kt38
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/composable/kairos/ToComposeState.kt36
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/display/DisplayHelper.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/dialog/VolumeDialog.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/dialog/VolumeDialogPlugin.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogVisibilityInteractor.kt1
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/util/RingerDrawerConstraintsUtils.kt10
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSliderViewBinder.kt77
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/compose/VolumeDialogSliderTrack.kt371
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderIconProvider.kt28
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderStateModel.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderViewModel.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/dialog/ui/viewmodel/VolumeDialogPluginViewModel.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioSharingStreamSliderViewModel.kt40
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioStreamSliderViewModel.kt143
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/CastVolumeSliderViewModel.kt26
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/SliderState.kt16
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/ui/compose/slider/Slider.kt (renamed from packages/SystemUI/src/com/android/systemui/volume/ui/slider/Slider.kt)43
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/ui/compose/slider/SliderIcon.kt45
-rw-r--r--packages/SystemUI/src/com/android/systemui/wallet/dagger/WalletModule.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/wallpapers/ImageWallpaper.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/window/data/repository/NoopWindowRootViewBlurRepository.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/window/data/repository/WindowRootViewBlurRepository.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/window/domain/interactor/WindowRootViewBlurInteractor.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/window/ui/WindowRootViewBinder.kt9
-rw-r--r--packages/SystemUI/src/com/android/systemui/window/ui/viewmodel/WindowRootViewModel.kt27
-rw-r--r--packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java23
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java14
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/accessibility/IMagnificationConnectionTest.java7
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationTest.java7
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java11
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java3
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationSettingsTest.java11
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuControllerTest.java32
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewAppearanceTest.java21
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/animation/ActivityTransitionAnimatorTest.kt6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt10
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/charging/WiredChargingRippleControllerTest.kt11
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java9
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTestKt.kt66
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySectionTest.kt3
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaSwitchingControllerTest.java90
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/FakeMediaTttChipControllerReceiver.kt6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt12
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderCoordinatorTest.kt14
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/navigationbar/gestural/BackPanelControllerTest.kt5
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskInitializerTest.kt39
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/panels/ui/compose/DragAndDropTest.kt70
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/panels/ui/compose/EditModeTest.kt47
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/panels/ui/compose/ResizingTest.kt2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDetailsContentControllerTest.java8
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/reardisplay/RearDisplayCoreStartableTest.kt6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/reardisplay/RearDisplayInnerDialogDelegateTest.kt39
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyboardShortcutListSearchTest.java10
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyboardShortcutsReceiverTest.java5
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyboardShortcutsTest.java16
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.kt74
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java64
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerWithScenesTest.kt89
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java5
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java18
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt45
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java55
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionKairosAdapterTelephonySmokeTests.kt69
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryKairosAdapterTest.kt72
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryTest.kt127
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionTelephonySmokeTests.kt73
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryKairosAdapterTest.kt126
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt727
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingControllerTest.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayControllerTest.kt9
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinatorTest.kt6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/utils/WindowManagerProviderImplTest.kt53
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java7
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java29
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCaseExt.kt41
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/communal/posturing/data/repository/FakePosturingRepository.kt16
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/communal/posturing/domain/interactor/PosturingInteractorKosmos.kt23
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/DisplayScopeRepositoryKosmos.kt4
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/FakeDisplayRepository.kt5
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/FakeDisplayScopeRepository.kt9
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/PerDisplayStoreKosmos.kt30
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/keyboard/shortcut/KeyboardShortcutHelperKosmos.kt11
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractorKosmos.kt1
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/GeneralKosmos.kt4
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt2
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/ui/viewmodel/MediaControlViewModelKosmos.kt5
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/EditTilesListInteractorKosmos.kt2
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/FakeTileAvailabilityInteractor.kt7
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/NewTilesAvailabilityInteractorKosmos.kt17
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/DetailsViewModelKosmos.kt8
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractorKosmos.kt2
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/domain/actions/FakeQSTileIntentUserInputHandler.kt (renamed from packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/actions/FakeQSTileIntentUserInputHandler.kt)9
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/domain/actions/QSTileIntentUserInputHandlerKosmos.kt (renamed from packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserInputHandlerKosmos.kt)2
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/domain/actions/QSTileIntentUserInputHandlerSubject.kt (renamed from packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserInputHandlerSubject.kt)8
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/domain/interactor/DisabledByPolicyInteractorKosmos.kt (renamed from packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/interactor/DisabledByPolicyInteractorKosmos.kt)2
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/domain/interactor/FakeDisabledByPolicyInteractor.kt (renamed from packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/interactor/FakeDisabledByPolicyInteractor.kt)4
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/domain/interactor/FakeQSTileDataInteractor.kt (renamed from packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/interactor/FakeQSTileDataInteractor.kt)6
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/domain/interactor/FakeQSTileUserActionInteractor.kt (renamed from packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/interactor/FakeQSTileUserActionInteractor.kt)5
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/domain/model/QSTileInputTestKtx.kt (renamed from packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/interactor/QSTileInputTestKtx.kt)6
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/shared/model/FakeQSTileConfigProvider.kt (renamed from packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/viewmodel/FakeQSTileConfigProvider.kt)4
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/shared/model/QSTileConfigProviderKosmos.kt (renamed from packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfigProviderKosmos.kt)4
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/shared/model/QSTileConfigTestBuilder.kt (renamed from packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfigTestBuilder.kt)14
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/ui/analytics/QSTileAnalyticsKosmos.kt (renamed from packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/analytics/QSTileAnalyticsKosmos.kt)2
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/ui/model/NewQSTileFactoryKosmos.kt (renamed from packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/di/NewQSTileFactoryKosmos.kt)20
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/ui/model/QSTileStateSubject.kt (renamed from packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/custom/QSTileStateSubject.kt)10
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/ui/viewmodel/QSTileViewModelAdapterKosmos.kt (renamed from packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelAdapterKosmos.kt)4
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/custom/CustomTileKosmos.kt9
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/custom/data/repository/FakeCustomTileDefaultsRepository.kt6
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/custom/data/repository/FakeCustomTileRepository.kt9
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileUserActionInteractorKosmos.kt2
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/qr/QRCodeScannerTileKosmos.kt14
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/uimodenight/UiModeNightTileModelHelper.kt6
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/startable/SceneContainerStartableKosmos.kt2
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModelKosmos.kt6
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModelKosmos.kt2
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/viewmodel/CastToOtherDeviceChipViewModelKosmos.kt3
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/screenrecord/ui/viewmodel/ScreenRecordChipViewModelKosmos.kt2
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/sharetoapp/ui/viewmodel/ShareToAppChipViewModelKosmos.kt2
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/uievents/StatusBarChipsUiEventLoggerKosmos.kt23
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/PrivacyDotWindowControllerStoreKosmos.kt10
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/events/PrivacyDotWindowControllerKosmos.kt4
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/collection/NotificationEntryBuilderKosmos.kt (renamed from packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/NotificationEntryBuilderKosmos.kt)69
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/collection/NotificationEntryKosmos.kt23
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorKosmos.kt3
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/collection/provider/HighPriorityProviderKosmos.kt22
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/data/model/ActiveNotificationModelBuilder.kt11
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterViewModelKosmos.kt2
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/promoted/FakePromotedNotificationContentExtractor.kt10
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractorKosmos.kt4
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/promoted/domain/interactor/PromotedNotificationsInteractorKosmos.kt4
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/promoted/shared/model/PromotedNotificationContentBuilder.kt33
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/EntryAdapterFactoryKosmos.kt8
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowBuilder.kt3
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/icon/AppIconProviderKosmos.kt3
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/icon/NotificationIconStyleProviderKosmos.kt4
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/ongoingcall/shared/model/OngoingCallTestHelper.kt12
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/ui/TintedIconManagerKosmos.kt22
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/ui/MobileUiAdapterKairosKosmos.kt34
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModelKairosKosmos.kt38
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/StackedMobileIconViewModelKairosKosmos.kt8
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/StackedMobileIconViewModelKosmos.kt7
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModelKosmos.kt2
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/FakeStatusBarWindowControllerFactory.kt4
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/StatusBarWindowControllerKosmos.kt4
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/StatusBarWindowControllerStoreKosmos.kt11
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/user/domain/interactor/UserSwitcherInteractorKosmos.kt1
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeSettings.kt31
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/utils/windowmanager/FakeWindowManagerProvider.kt28
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogCsdWarningInteractorKosmos.kt (renamed from packages/SystemUI/tests/utils/src/com/android/app/viewcapture/ViewCaptureAwareWindowManagerKosmos.kt)22
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderIconProviderKosmos.kt2
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/ui/viewmodel/VolumeDialogPluginViewModelKosmos.kt41
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioSharingStreamSliderViewModelKosmos.kt4
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioStreamSliderViewModelKosmos.kt2
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/CastVolumeSliderViewModelKosmos.kt2
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/window/data/repository/WindowRootViewBlurRepositoryKosmos.kt2
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/window/ui/viewmodel/WindowRootViewModelKosmos.kt4
-rw-r--r--packages/SystemUI/utils/Android.bp2
-rw-r--r--packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/Graph.kt1
-rw-r--r--packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/MuxDeferred.kt27
-rw-r--r--packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/MuxPrompt.kt18
-rw-r--r--packages/SystemUI/utils/src/com/android/systemui/utils/windowmanager/WindowManagerProvider.kt56
-rw-r--r--packages/SystemUI/utils/src/com/android/systemui/utils/windowmanager/WindowManagerProviderImpl.kt28
-rw-r--r--packages/SystemUI/utils/src/com/android/systemui/utils/windowmanager/WindowManagerUtils.kt49
-rw-r--r--packages/WallpaperBackup/Android.bp2
-rw-r--r--packages/WallpaperBackup/src/com/android/wallpaperbackup/WallpaperBackupAgent.java124
-rw-r--r--packages/WallpaperBackup/src/com/android/wallpaperbackup/WallpaperEventLogger.java49
-rw-r--r--packages/WallpaperBackup/test/src/com/android/wallpaperbackup/WallpaperBackupAgentTest.java229
-rw-r--r--packages/WallpaperBackup/test/src/com/android/wallpaperbackup/WallpaperEventLoggerTest.java90
-rwxr-xr-xravenwood/scripts/ravenwood-stats-collector.sh53
-rw-r--r--ravenwood/tools/hoststubgen/Android.bp1
-rw-r--r--ravenwood/tools/hoststubgen/README.md59
-rw-r--r--ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/HostStubGenStats.kt33
-rw-r--r--ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/Utils.kt35
-rw-r--r--ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/asm/ClassNodes.kt96
-rw-r--r--ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/dumper/ApiDumper.kt62
-rw-r--r--ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/filters/ClassWidePolicyPropagatingFilter.kt2
-rw-r--r--ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/filters/FilterPolicy.kt7
-rw-r--r--ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/filters/FilterPolicyWithReason.kt50
-rw-r--r--ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/filters/ImplicitOutputFilter.kt28
-rw-r--r--ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/filters/KeepNativeFilter.kt5
-rw-r--r--ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/filters/TextFileFilterPolicyParser.kt12
-rw-r--r--ravenwood/tools/hoststubgen/src/com/android/hoststubgen/HostStubGen.kt98
-rw-r--r--ravenwood/tools/hoststubgen/test-tiny-framework/README.md21
-rwxr-xr-xravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework-dump-test.py2
-rw-r--r--ravenwood/tools/ravenizer/src/com/android/platform/test/ravenwood/ravenizer/Ravenizer.kt115
-rw-r--r--services/Android.bp13
-rw-r--r--services/accessibility/accessibility.aconfig10
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java15
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java52
-rw-r--r--services/accessibility/java/com/android/server/accessibility/HearingDevicePhoneCallNotificationController.java56
-rw-r--r--services/accessibility/java/com/android/server/accessibility/autoclick/AutoclickController.java17
-rw-r--r--services/accessibility/java/com/android/server/accessibility/gestures/GestureMatcher.java23
-rw-r--r--services/art-profile-extra1
-rw-r--r--services/core/Android.bp1
-rw-r--r--services/core/java/com/android/server/adb/AdbDebuggingManager.java48
-rw-r--r--services/core/java/com/android/server/adb/AdbService.java83
-rw-r--r--services/core/java/com/android/server/am/ActiveServices.java7
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java5
-rw-r--r--services/core/java/com/android/server/am/AppProfiler.java2
-rw-r--r--services/core/java/com/android/server/am/AppStartInfoTracker.java117
-rw-r--r--services/core/java/com/android/server/am/BatteryStatsService.java50
-rw-r--r--services/core/java/com/android/server/am/OomAdjuster.java11
-rw-r--r--services/core/java/com/android/server/am/ProcessList.java102
-rw-r--r--services/core/java/com/android/server/am/SettingsToPropertiesMapper.java120
-rw-r--r--services/core/java/com/android/server/appop/AppOpsService.java70
-rw-r--r--services/core/java/com/android/server/appop/AppOpsUidStateTracker.java13
-rw-r--r--services/core/java/com/android/server/appop/AppOpsUidStateTrackerImpl.java37
-rw-r--r--services/core/java/com/android/server/audio/AudioServerPermissionProvider.java32
-rw-r--r--services/core/java/com/android/server/audio/AudioService.java69
-rw-r--r--services/core/java/com/android/server/audio/AudioSystemAdapter.java24
-rw-r--r--services/core/java/com/android/server/audio/AudioVolumeChangeHandler.java103
-rw-r--r--services/core/java/com/android/server/display/DisplayManagerService.java109
-rw-r--r--services/core/java/com/android/server/display/DisplayTopologyCoordinator.java26
-rw-r--r--services/core/java/com/android/server/display/OverlayDisplayAdapter.java26
-rw-r--r--services/core/java/com/android/server/display/OverlayDisplayWindow.java11
-rw-r--r--services/core/java/com/android/server/display/mode/DisplayModeDirector.java29
-rw-r--r--services/core/java/com/android/server/display/mode/ModeChangeObserver.java8
-rw-r--r--services/core/java/com/android/server/hdmi/AudioDeviceVolumeManagerWrapper.java17
-rw-r--r--services/core/java/com/android/server/hdmi/AudioManagerWrapper.java12
-rw-r--r--services/core/java/com/android/server/hdmi/DefaultAudioDeviceVolumeManagerWrapper.java25
-rw-r--r--services/core/java/com/android/server/hdmi/DefaultAudioManagerWrapper.java12
-rw-r--r--services/core/java/com/android/server/hdmi/DeviceSelectActionFromTv.java6
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java2
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiControlService.java35
-rw-r--r--services/core/java/com/android/server/input/AppLaunchShortcutManager.java2
-rw-r--r--services/core/java/com/android/server/input/InputGestureManager.java9
-rw-r--r--services/core/java/com/android/server/input/InputManagerService.java140
-rw-r--r--services/core/java/com/android/server/input/KeyGestureController.java179
-rw-r--r--services/core/java/com/android/server/inputmethod/InputMethodManagerService.java39
-rw-r--r--services/core/java/com/android/server/location/contexthub/ContextHubEndpointBroker.java155
-rw-r--r--services/core/java/com/android/server/location/contexthub/ContextHubEndpointManager.java62
-rw-r--r--services/core/java/com/android/server/location/gnss/hal/GnssNative.java1
-rw-r--r--services/core/java/com/android/server/locksettings/Android.bp11
-rw-r--r--services/core/java/com/android/server/locksettings/LockSettingsService.java4
-rw-r--r--services/core/java/com/android/server/locksettings/RebootEscrowManager.java66
-rw-r--r--services/core/java/com/android/server/locksettings/flags.aconfig9
-rw-r--r--services/core/java/com/android/server/media/MediaSessionService.java34
-rw-r--r--services/core/java/com/android/server/media/projection/Android.bp21
-rw-r--r--services/core/java/com/android/server/media/projection/TEST_MAPPING28
-rw-r--r--services/core/java/com/android/server/media/quality/MediaQualityService.java2
-rw-r--r--services/core/java/com/android/server/media/quality/MediaQualityUtils.java258
-rw-r--r--services/core/java/com/android/server/notification/NotificationManagerInternal.java7
-rw-r--r--services/core/java/com/android/server/notification/NotificationManagerService.java31
-rw-r--r--services/core/java/com/android/server/om/OverlayManagerService.java4
-rw-r--r--services/core/java/com/android/server/pm/ComputerEngine.java3
-rw-r--r--services/core/java/com/android/server/pm/DeletePackageHelper.java2
-rw-r--r--services/core/java/com/android/server/pm/InstallPackageHelper.java3
-rw-r--r--services/core/java/com/android/server/pm/InstallRequest.java3
-rw-r--r--services/core/java/com/android/server/pm/LauncherAppsService.java35
-rw-r--r--services/core/java/com/android/server/pm/UserManagerInternal.java28
-rw-r--r--services/core/java/com/android/server/pm/UserManagerService.java32
-rw-r--r--services/core/java/com/android/server/pm/permission/AccessCheckDelegate.java281
-rw-r--r--services/core/java/com/android/server/policy/PhoneWindowManager.java206
-rw-r--r--services/core/java/com/android/server/policy/WindowWakeUpPolicy.java10
-rw-r--r--services/core/java/com/android/server/policy/WindowWakeUpPolicyInternal.java6
-rw-r--r--services/core/java/com/android/server/power/PowerManagerService.java4
-rw-r--r--services/core/java/com/android/server/power/ShutdownThread.java4
-rw-r--r--services/core/java/com/android/server/power/ThermalManagerService.java47
-rw-r--r--services/core/java/com/android/server/power/stats/BatteryExternalStatsWorker.java9
-rw-r--r--services/core/java/com/android/server/power/stats/BatteryHistoryStepDetailsProvider.java276
-rw-r--r--services/core/java/com/android/server/power/stats/BatteryStatsImpl.java217
-rw-r--r--services/core/java/com/android/server/security/advancedprotection/AdvancedProtectionService.java102
-rw-r--r--services/core/java/com/android/server/security/advancedprotection/AdvancedProtectionShellCommand.java26
-rw-r--r--services/core/java/com/android/server/security/advancedprotection/features/AdvancedProtectionProvider.java4
-rw-r--r--services/core/java/com/android/server/security/advancedprotection/features/DisallowWepAdvancedProtectionProvider.java31
-rw-r--r--services/core/java/com/android/server/statusbar/StatusBarManagerService.java91
-rw-r--r--services/core/java/com/android/server/wallpaper/WallpaperCropper.java2
-rw-r--r--services/core/java/com/android/server/wallpaper/WallpaperManagerInternal.java3
-rw-r--r--services/core/java/com/android/server/wallpaper/WallpaperManagerService.java69
-rw-r--r--services/core/java/com/android/server/wm/AbsAppSnapshotController.java22
-rw-r--r--services/core/java/com/android/server/wm/ActivityRecord.java56
-rw-r--r--services/core/java/com/android/server/wm/ActivitySnapshotCache.java1
-rw-r--r--services/core/java/com/android/server/wm/ActivityStartInterceptor.java6
-rw-r--r--services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java11
-rw-r--r--services/core/java/com/android/server/wm/ActivityTaskManagerService.java36
-rw-r--r--services/core/java/com/android/server/wm/ActivityTaskSupervisor.java10
-rw-r--r--services/core/java/com/android/server/wm/AppCompatCameraOverrides.java3
-rw-r--r--services/core/java/com/android/server/wm/AppCompatCameraPolicy.java4
-rw-r--r--services/core/java/com/android/server/wm/AppCompatSafeRegionPolicy.java2
-rw-r--r--services/core/java/com/android/server/wm/AsyncRotationController.java42
-rw-r--r--services/core/java/com/android/server/wm/BackNavigationController.java30
-rw-r--r--services/core/java/com/android/server/wm/BackgroundActivityStartCallback.java13
-rw-r--r--services/core/java/com/android/server/wm/BackgroundActivityStartController.java8
-rw-r--r--services/core/java/com/android/server/wm/BackgroundLaunchProcessController.java42
-rw-r--r--services/core/java/com/android/server/wm/DesktopAppCompatAspectRatioPolicy.java9
-rw-r--r--services/core/java/com/android/server/wm/DesktopModeBoundsCalculator.java43
-rw-r--r--services/core/java/com/android/server/wm/DeviceStateAutoRotateSettingIssueLogger.java42
-rw-r--r--services/core/java/com/android/server/wm/Dimmer.java5
-rw-r--r--services/core/java/com/android/server/wm/DisplayArea.java6
-rw-r--r--services/core/java/com/android/server/wm/DisplayPolicy.java6
-rw-r--r--services/core/java/com/android/server/wm/OWNERS2
-rw-r--r--services/core/java/com/android/server/wm/PointerEventDispatcher.java2
-rw-r--r--services/core/java/com/android/server/wm/SnapshotCache.java8
-rw-r--r--services/core/java/com/android/server/wm/SnapshotController.java36
-rw-r--r--services/core/java/com/android/server/wm/SnapshotPersistQueue.java81
-rw-r--r--services/core/java/com/android/server/wm/Task.java197
-rw-r--r--services/core/java/com/android/server/wm/TaskDisplayArea.java30
-rw-r--r--services/core/java/com/android/server/wm/TaskFpsCallbackController.java5
-rw-r--r--services/core/java/com/android/server/wm/TaskSnapshotCache.java1
-rw-r--r--services/core/java/com/android/server/wm/Transition.java4
-rw-r--r--services/core/java/com/android/server/wm/TransitionController.java12
-rw-r--r--services/core/java/com/android/server/wm/WindowAnimator.java34
-rw-r--r--services/core/java/com/android/server/wm/WindowContainer.java33
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java91
-rw-r--r--services/core/java/com/android/server/wm/WindowProcessController.java30
-rw-r--r--services/core/java/com/android/server/wm/WindowState.java30
-rw-r--r--services/core/jni/com_android_server_input_InputManagerService.cpp5
-rw-r--r--services/credentials/java/com/android/server/credentials/MetricUtilities.java8
-rw-r--r--services/credentials/java/com/android/server/credentials/PrepareGetRequestSession.java10
-rw-r--r--services/credentials/java/com/android/server/credentials/metrics/InitialPhaseMetric.java16
-rw-r--r--services/credentials/java/com/android/server/credentials/metrics/RequestSessionMetric.java16
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java30
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/PolicyDefinition.java5
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/StringSetIntersection.java59
-rw-r--r--services/java/com/android/server/SystemServer.java31
-rw-r--r--services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPolicy.kt7
-rw-r--r--services/supervision/java/com/android/server/supervision/SupervisionService.java25
-rw-r--r--services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/UserDataPreparerTest.java26
-rw-r--r--services/tests/displayservicetests/Android.bp1
-rw-r--r--services/tests/displayservicetests/AndroidManifest.xml1
-rw-r--r--services/tests/displayservicetests/AndroidTest.xml1
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/DisplayEventDeliveryTest.java244
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/EventDeliveryTestBase.java259
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/TopologyUpdateDeliveryTest.java243
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/mode/AppRequestObserverTest.kt33
-rw-r--r--services/tests/displayservicetests/src/com/android/server/display/mode/ModeChangeObserverTest.kt187
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/am/ApplicationStartInfoTest.java64
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java51
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/am/ProcessObserverTest.java6
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/appop/AppOpsUidStateTrackerTest.java247
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/appop/AppOpsUidStateTrackerTransitionCallbackTest.java260
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java29
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java18
-rw-r--r--services/tests/powerstatstests/src/com/android/server/power/stats/BatteryExternalStatsWorkerTest.java109
-rw-r--r--services/tests/powerstatstests/src/com/android/server/power/stats/BatteryHistoryStepDetailsProviderTest.java163
-rw-r--r--services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsHistoryIteratorTest.java63
-rw-r--r--services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsHistoryTest.java47
-rw-r--r--services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsProviderTest.java2
-rw-r--r--services/tests/powerstatstests/src/com/android/server/power/stats/MockBatteryStatsImpl.java4
-rw-r--r--services/tests/powerstatstests/src/com/android/server/power/stats/MockPowerStatsInternal.java198
-rw-r--r--services/tests/powerstatstests/src/com/android/server/power/stats/processor/PowerStatsAggregatorTest.java5
-rw-r--r--services/tests/powerstatstests/src/com/android/server/power/stats/processor/PowerStatsExporterTest.java3
-rw-r--r--services/tests/powerstatstests/src/com/android/server/power/stats/processor/WakelockPowerStatsProcessorTest.java3
-rw-r--r--services/tests/servicestests/Android.bp3
-rw-r--r--services/tests/servicestests/AndroidTest.xml1
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java45
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/HearingDevicePhoneCallNotificationControllerTest.java20
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/autoclick/AutoclickControllerTest.java18
-rw-r--r--services/tests/servicestests/src/com/android/server/accessibility/magnification/TwoFingersDownOrSwipeTest.java23
-rw-r--r--services/tests/servicestests/src/com/android/server/audio/AudioVolumeChangeHandlerTest.java142
-rw-r--r--services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java12
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/BaseAbsoluteVolumeBehaviorTest.java70
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/BasePlaybackDeviceAvbTest.java6
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/BaseTvToAudioSystemAvbTest.java35
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/DeviceSelectActionFromTvTest.java29
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/FakeAudioFramework.java32
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/PlaybackDeviceToAudioSystemAvbTest.java12
-rw-r--r--services/tests/servicestests/src/com/android/server/hdmi/PlaybackDeviceToTvAvbTest.java12
-rw-r--r--services/tests/servicestests/src/com/android/server/location/contexthub/ContextHubEndpointTest.java149
-rw-r--r--services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTests.java14
-rw-r--r--services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java61
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest8.java12
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserInfoTest.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/power/ThermalManagerServiceTest.java62
-rw-r--r--services/tests/servicestests/src/com/android/server/security/advancedprotection/AdvancedProtectionServiceTest.java33
-rw-r--r--services/tests/servicestests/src/com/android/server/statusbar/StatusBarManagerServiceTest.java74
-rw-r--r--services/tests/servicestests/src/com/android/server/storage/DiskStatsLoggingServiceTest.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/supervision/SupervisionServiceTest.kt50
-rw-r--r--services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java4
-rw-r--r--services/tests/servicestests/test-apps/TopologyTestApp/Android.bp38
-rw-r--r--services/tests/servicestests/test-apps/TopologyTestApp/AndroidManifest.xml27
-rw-r--r--services/tests/servicestests/test-apps/TopologyTestApp/OWNERS3
-rw-r--r--services/tests/servicestests/test-apps/TopologyTestApp/src/com/android/servicestests/apps/topologytestapp/TopologyUpdateActivity.java87
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java71
-rw-r--r--services/tests/wmtests/src/com/android/server/policy/CombinationKeyTests.java1
-rw-r--r--services/tests/wmtests/src/com/android/server/policy/KeyGestureEventTests.java153
-rw-r--r--services/tests/wmtests/src/com/android/server/policy/PhoneWindowManagerTests.java129
-rw-r--r--services/tests/wmtests/src/com/android/server/policy/ShortcutKeyTestBase.java20
-rw-r--r--services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java36
-rw-r--r--services/tests/wmtests/src/com/android/server/policy/WindowWakeUpPolicyTests.java26
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java15
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/AppWindowTokenAnimationTests.java142
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/BackgroundLaunchProcessControllerTests.java8
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DesktopAppCompatAspectRatioPolicyTests.java5
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DesktopModeLaunchParamsModifierTests.java13
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DeviceStateAutoRotateSettingIssueLoggerTests.java43
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsTests.java27
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java20
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TaskTests.java85
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java40
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java11
-rw-r--r--telecomm/java/android/telecom/Call.java60
-rw-r--r--telephony/java/android/telephony/CarrierConfigManager.java10
-rw-r--r--telephony/java/android/telephony/satellite/SatelliteManager.java83
-rw-r--r--telephony/java/com/android/internal/telephony/ITelephony.aidl24
-rw-r--r--tests/FsVerityTest/FsVerityTestApp/src/com/android/fsverity/Helper.java63
-rw-r--r--tests/FsVerityTest/src/com/android/fsverity/FsVerityHostTest.java21
-rw-r--r--tests/Input/src/android/hardware/input/KeyGestureEventHandlerTest.kt115
-rw-r--r--tests/Input/src/com/android/server/input/InputManagerServiceTests.kt25
-rw-r--r--tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt234
-rw-r--r--tests/Input/src/com/android/test/input/AnrTest.kt86
-rw-r--r--tests/TelephonyCommonTests/src/com/android/internal/telephony/tests/SmsApplicationTest.java2
-rw-r--r--tests/TtsTests/src/com/android/speech/tts/TextToSpeechTests.java10
-rw-r--r--tests/vcn/Android.bp4
-rw-r--r--tests/vcn/AndroidManifest.xml4
-rw-r--r--tests/vcn/AndroidTest.xml2
-rw-r--r--tests/vcn/java/android/net/vcn/VcnCellUnderlyingNetworkTemplateTest.java11
-rw-r--r--tests/vcn/java/android/net/vcn/VcnConfigTest.java10
-rw-r--r--tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java10
-rw-r--r--tests/vcn/java/android/net/vcn/VcnManagerTest.java10
-rw-r--r--tests/vcn/java/android/net/vcn/VcnTransportInfoTest.java10
-rw-r--r--tests/vcn/java/android/net/vcn/VcnUnderlyingNetworkPolicyTest.java10
-rw-r--r--tests/vcn/java/android/net/vcn/VcnUnderlyingNetworkSpecifierTest.java10
-rw-r--r--tests/vcn/java/android/net/vcn/VcnUtilsTest.java10
-rw-r--r--tests/vcn/java/android/net/vcn/VcnWifiUnderlyingNetworkTemplateTest.java11
-rw-r--r--tests/vcn/java/android/net/vcn/persistablebundleutils/EapSessionConfigUtilsTest.java10
-rw-r--r--tests/vcn/java/android/net/vcn/persistablebundleutils/IkeIdentificationUtilsTest.java10
-rw-r--r--tests/vcn/java/android/net/vcn/persistablebundleutils/IkeSessionParamsUtilsTest.java9
-rw-r--r--tests/vcn/java/android/net/vcn/persistablebundleutils/IkeTrafficSelectorUtilsTest.java10
-rw-r--r--tests/vcn/java/android/net/vcn/persistablebundleutils/SaProposalUtilsTest.java10
-rw-r--r--tests/vcn/java/android/net/vcn/persistablebundleutils/TunnelConnectionParamsUtilsTest.java10
-rw-r--r--tests/vcn/java/android/net/vcn/persistablebundleutils/TunnelModeChildSessionParamsUtilsTest.java10
-rw-r--r--tests/vcn/java/android/net/vcn/util/MtuUtilsTest.java10
-rw-r--r--tests/vcn/java/android/net/vcn/util/PersistableBundleUtilsTest.java10
-rw-r--r--tests/vcn/java/com/android/server/VcnManagementServiceTest.java8
-rw-r--r--tests/vcn/java/com/android/server/vcn/TelephonySubscriptionTrackerTest.java9
-rw-r--r--tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java9
-rw-r--r--tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectingStateTest.java10
-rw-r--r--tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectedStateTest.java10
-rw-r--r--tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectingStateTest.java11
-rw-r--r--tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionRetryTimeoutStateTest.java11
-rw-r--r--tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTest.java9
-rw-r--r--tests/vcn/java/com/android/server/vcn/VcnNetworkProviderTest.java9
-rw-r--r--tests/vcn/java/com/android/server/vcn/VcnTest.java9
-rw-r--r--tests/vcn/java/com/android/server/vcn/routeselection/IpSecPacketLossDetectorTest.java9
-rw-r--r--tests/vcn/java/com/android/server/vcn/routeselection/NetworkPriorityClassifierTest.java10
-rw-r--r--tests/vcn/java/com/android/server/vcn/routeselection/UnderlyingNetworkControllerTest.java9
-rw-r--r--tests/vcn/java/com/android/server/vcn/routeselection/UnderlyingNetworkEvaluatorTest.java9
-rw-r--r--tools/aapt2/ResourcesInternal.proto5
-rw-r--r--tools/aapt2/cmd/Compile.cpp43
-rw-r--r--tools/aapt2/cmd/Compile.h2
-rw-r--r--tools/aapt2/cmd/Convert.h14
-rw-r--r--tools/aapt2/cmd/Link.cpp22
-rw-r--r--tools/aapt2/cmd/Link.h9
-rw-r--r--tools/aapt2/cmd/Optimize.h14
-rw-r--r--tools/aapt2/format/binary/TableFlattener.cpp17
-rw-r--r--tools/aapt2/format/binary/TableFlattener_test.cpp122
-rw-r--r--tools/aapt2/format/proto/ProtoDeserialize.cpp1
-rw-r--r--tools/aapt2/format/proto/ProtoSerialize.cpp1
-rw-r--r--tools/aapt2/link/FlaggedResources_test.cpp6
-rw-r--r--tools/aapt2/link/FlaggedXmlVersioner.cpp48
-rw-r--r--tools/aapt2/link/FlaggedXmlVersioner_test.cpp4
-rw-r--r--tools/aapt2/link/TableMerger.cpp13
-rw-r--r--tools/aapt2/readme.md4
-rw-r--r--tools/protologtool/Android.bp6
-rw-r--r--tools/protologtool/src/com/android/protolog/tool/Exceptions.kt (renamed from tools/protologtool/src/com/android/protolog/tool/exceptions.kt)25
-rw-r--r--tools/protologtool/src/com/android/protolog/tool/ProtoLogCallProcessor.kt2
-rw-r--r--tools/protologtool/src/com/android/protolog/tool/ProtoLogCallProcessorImpl.kt77
-rw-r--r--tools/protologtool/src/com/android/protolog/tool/ProtoLogTool.kt25
-rw-r--r--tools/protologtool/src/com/android/protolog/tool/SourceTransformer.kt6
-rw-r--r--tools/protologtool/tests/com/android/protolog/tool/EndToEndTest.kt43
-rw-r--r--tools/protologtool/tests/com/android/protolog/tool/ProtoLogCallProcessorImplTest.kt72
-rw-r--r--tools/protologtool/tests/com/android/protolog/tool/SourceTransformerTest.kt24
2072 files changed, 38301 insertions, 17235 deletions
diff --git a/.gitignore b/.gitignore
index b093c811abd5..b4af5676e9d2 100644
--- a/.gitignore
+++ b/.gitignore
@@ -6,3 +6,8 @@ gen/
.vscode/
*.code-workspace
.gradle/
+# .classpath and .settings/ are configurations
+# used by the IDE and java development tools
+# to configure filepaths + project settings
+.classpath
+.settings/
diff --git a/AconfigFlags.bp b/AconfigFlags.bp
index f9cc1259a375..b9537b357517 100644
--- a/AconfigFlags.bp
+++ b/AconfigFlags.bp
@@ -50,6 +50,7 @@ aconfig_declarations_group {
"android.hardware.devicestate.feature.flags-aconfig-java",
"android.hardware.flags-aconfig-java",
"android.hardware.radio.flags-aconfig-java",
+ "android.hardware.serial.flags-aconfig-java",
"android.hardware.usb.flags-aconfig-java",
"android.location.flags-aconfig-java",
"android.media.codec-aconfig-java",
@@ -1962,3 +1963,18 @@ java_aconfig_library {
aconfig_declarations: "android.service.selinux.flags-aconfig",
defaults: ["framework-minus-apex-aconfig-java-defaults"],
}
+
+// Serial
+aconfig_declarations {
+ name: "android.hardware.serial.flags-aconfig",
+ exportable: true,
+ package: "android.hardware.serial.flags",
+ container: "system",
+ srcs: ["core/java/android/hardware/serial/flags/*.aconfig"],
+}
+
+java_aconfig_library {
+ name: "android.hardware.serial.flags-aconfig-java",
+ aconfig_declarations: "android.hardware.serial.flags-aconfig",
+ defaults: ["framework-minus-apex-aconfig-java-defaults"],
+}
diff --git a/Android.bp b/Android.bp
index 127556f8e075..e7c20418bcef 100644
--- a/Android.bp
+++ b/Android.bp
@@ -427,6 +427,7 @@ java_defaults {
"framework-permission-aidl-java",
"spatializer-aidl-java",
"audiopolicy-aidl-java",
+ "volumegroupcallback-aidl-java",
"sounddose-aidl-java",
"modules-utils-expresslog",
"perfetto_trace_javastream_protos_jarjar",
diff --git a/apct-tests/perftests/core/src/android/os/TracePerfTest.java b/apct-tests/perftests/core/src/android/os/TracePerfTest.java
index 00e1c1fdbf4b..3df708d1a5cd 100644
--- a/apct-tests/perftests/core/src/android/os/TracePerfTest.java
+++ b/apct-tests/perftests/core/src/android/os/TracePerfTest.java
@@ -46,6 +46,7 @@ public class TracePerfTest {
private static final String FOO = "foo";
private static final Category FOO_CATEGORY = new Category(FOO);
+ private static final Category UNREGISTERED_CATEGORY = new Category("unregistered");
private static PerfettoTrace.Session sPerfettoSession;
@BeforeClass
@@ -163,6 +164,30 @@ public class TracePerfTest {
}
}
+ @Test
+ public void testInstantPerfettoWithProtoUnregistered() {
+ PerfettoTrace.begin(UNREGISTERED_CATEGORY, "message_queue_receive")
+ .beginProto()
+ .beginNested(2004 /* message_queue */)
+ .addField(1 /* sending_thread_name */, "foo")
+ .endNested()
+ .endProto()
+ .setTerminatingFlow(5)
+ .emit();
+
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ PerfettoTrace.begin(UNREGISTERED_CATEGORY, "message_queue_receive")
+ .beginProto()
+ .beginNested(2004 /* message_queue */)
+ .addField(1 /* sending_thread_name */, "foo")
+ .endNested()
+ .endProto()
+ .setTerminatingFlow(5)
+ .emit();
+ }
+ }
+
private static TraceConfig getTraceConfig(String cat) {
BufferConfig bufferConfig = BufferConfig.newBuilder().setSizeKb(1024).build();
TrackEventConfig trackEventConfig = TrackEventConfig
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java
index a9c4a1501dd8..6dd7521e4d43 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java
@@ -36,7 +36,6 @@ import android.annotation.UserIdInt;
import android.app.ActivityManager;
import android.app.AlarmManager;
import android.app.UidObserver;
-import android.app.compat.CompatChanges;
import android.app.job.JobInfo;
import android.app.usage.UsageEvents;
import android.app.usage.UsageStatsManagerInternal;
@@ -54,6 +53,7 @@ import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.os.RemoteException;
+import android.os.ServiceManager;
import android.os.Trace;
import android.os.UserHandle;
import android.provider.DeviceConfig;
@@ -74,6 +74,7 @@ import com.android.internal.util.ArrayUtils;
import com.android.server.AppSchedulingModuleThread;
import com.android.server.LocalServices;
import com.android.server.PowerAllowlistInternal;
+import com.android.server.compat.PlatformCompat;
import com.android.server.job.ConstantsProto;
import com.android.server.job.Flags;
import com.android.server.job.JobSchedulerService;
@@ -157,6 +158,15 @@ public final class QuotaController extends StateController {
@Overridable // The change can be overridden in user build.
static final long OVERRIDE_QUOTA_ENFORCEMENT_TO_TOP_STARTED_JOBS = 374323858L;
+ /**
+ * When enabled this change id overrides the default quota parameters adjustment.
+ */
+ @VisibleForTesting
+ @ChangeId
+ @Disabled // Disabled by default
+ @Overridable // The change can be overridden in user build.
+ static final long OVERRIDE_QUOTA_ADJUST_DEFAULT_CONSTANTS = 378129159L;
+
@VisibleForTesting
static class ExecutionStats {
/**
@@ -536,6 +546,8 @@ public final class QuotaController extends StateController {
*/
private final SparseSetArray<String> mSystemInstallers = new SparseSetArray<>();
+ private final PlatformCompat mPlatformCompat;
+
/** An app has reached its quota. The message should contain a {@link UserPackage} object. */
@VisibleForTesting
static final int MSG_REACHED_TIME_QUOTA = 0;
@@ -587,6 +599,13 @@ public final class QuotaController extends StateController {
PowerAllowlistInternal pai = LocalServices.getService(PowerAllowlistInternal.class);
pai.registerTempAllowlistChangeListener(new TempAllowlistTracker());
+ mPlatformCompat = (PlatformCompat)
+ ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE);
+ if (Flags.adjustQuotaDefaultConstants()) {
+ mPlatformCompat.registerListener(OVERRIDE_QUOTA_ADJUST_DEFAULT_CONSTANTS,
+ (packageName) -> handleQuotaDefaultConstantsCompatChange());
+ }
+
try {
ActivityManager.getService().registerUidObserver(new QcUidObserver(),
ActivityManager.UID_OBSERVER_PROCSTATE,
@@ -651,8 +670,9 @@ public final class QuotaController extends StateController {
final int uid = jobStatus.getSourceUid();
if ((!Flags.enforceQuotaPolicyToTopStartedJobs()
- || CompatChanges.isChangeEnabled(OVERRIDE_QUOTA_ENFORCEMENT_TO_TOP_STARTED_JOBS,
- uid)) && mTopAppCache.get(uid)) {
+ || mPlatformCompat.isChangeEnabledByUid(
+ OVERRIDE_QUOTA_ENFORCEMENT_TO_TOP_STARTED_JOBS, uid))
+ && mTopAppCache.get(uid)) {
if (DEBUG) {
Slog.d(TAG, jobStatus.toShortString() + " is top started job");
}
@@ -690,8 +710,8 @@ public final class QuotaController extends StateController {
}
}
if (!Flags.enforceQuotaPolicyToTopStartedJobs()
- || CompatChanges.isChangeEnabled(OVERRIDE_QUOTA_ENFORCEMENT_TO_TOP_STARTED_JOBS,
- jobStatus.getSourceUid())) {
+ || mPlatformCompat.isChangeEnabledByUid(
+ OVERRIDE_QUOTA_ENFORCEMENT_TO_TOP_STARTED_JOBS, jobStatus.getSourceUid())) {
mTopStartedJobs.remove(jobStatus);
}
}
@@ -805,8 +825,8 @@ public final class QuotaController extends StateController {
/** @return true if the job was started while the app was in the TOP state. */
private boolean isTopStartedJobLocked(@NonNull final JobStatus jobStatus) {
if (!Flags.enforceQuotaPolicyToTopStartedJobs()
- || CompatChanges.isChangeEnabled(OVERRIDE_QUOTA_ENFORCEMENT_TO_TOP_STARTED_JOBS,
- jobStatus.getSourceUid())) {
+ || mPlatformCompat.isChangeEnabledByUid(
+ OVERRIDE_QUOTA_ENFORCEMENT_TO_TOP_STARTED_JOBS, jobStatus.getSourceUid())) {
return mTopStartedJobs.contains(jobStatus);
}
@@ -1102,6 +1122,7 @@ public final class QuotaController extends StateController {
final int standbyBucket) {
final long baseLimitMs = mAllowedTimePerPeriodMs[standbyBucket];
if (Flags.adjustQuotaDefaultConstants()
+ && !isCompatOverridedForQuotaConstantAdjustment()
&& Flags.additionalQuotaForSystemInstaller()
&& standbyBucket == EXEMPTED_INDEX
&& mSystemInstallers.contains(userId, pkgName)) {
@@ -1473,10 +1494,21 @@ public final class QuotaController extends StateController {
}
}
+ void handleQuotaDefaultConstantsCompatChange() {
+ synchronized (mLock) {
+ final boolean isCompatEnabled = isCompatOverridedForQuotaConstantAdjustment();
+ mQcConstants.adjustDefaultBucketWindowSizes(isCompatEnabled);
+ mQcConstants.adjustDefaultEjLimits(isCompatEnabled);
+ mQcConstants.mShouldReevaluateConstraints = true;
+ onConstantsUpdatedLocked();
+ }
+ }
+
void processQuotaConstantsAdjustment() {
- if (Flags.adjustQuotaDefaultConstants()) {
- mQcConstants.adjustDefaultBucketWindowSizes();
- mQcConstants.adjustDefaultEjLimits();
+ if (Flags.adjustQuotaDefaultConstants()
+ && !isCompatOverridedForQuotaConstantAdjustment()) {
+ mQcConstants.adjustDefaultBucketWindowSizes(false);
+ mQcConstants.adjustDefaultEjLimits(false);
}
}
@@ -1505,6 +1537,11 @@ public final class QuotaController extends StateController {
}
}
+ private boolean isCompatOverridedForQuotaConstantAdjustment() {
+ return mPlatformCompat.isChangeEnabledByPackageName(
+ OVERRIDE_QUOTA_ADJUST_DEFAULT_CONSTANTS, "android", UserHandle.USER_SYSTEM);
+ }
+
private void incrementTimingSessionCountLocked(final int userId,
@NonNull final String packageName) {
final long now = sElapsedRealtimeClock.millis();
@@ -2689,7 +2726,8 @@ public final class QuotaController extends StateController {
@VisibleForTesting
int getProcessStateQuotaFreeThreshold(int uid) {
if (Flags.enforceQuotaPolicyToFgsJobs()
- && !CompatChanges.isChangeEnabled(OVERRIDE_QUOTA_ENFORCEMENT_TO_FGS_JOBS, uid)) {
+ && !mPlatformCompat.isChangeEnabledByUid(
+ OVERRIDE_QUOTA_ENFORCEMENT_TO_FGS_JOBS, uid)) {
return ActivityManager.PROCESS_STATE_BOUND_TOP;
}
@@ -3596,25 +3634,40 @@ public final class QuotaController extends StateController {
*/
public long EJ_GRACE_PERIOD_TOP_APP_MS = DEFAULT_EJ_GRACE_PERIOD_TOP_APP_MS;
- void adjustDefaultBucketWindowSizes() {
- ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS = Flags.tuneQuotaWindowDefaultParameters()
- ? DEFAULT_CURRENT_ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS :
- DEFAULT_LEGACY_ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS;
- ALLOWED_TIME_PER_PERIOD_ACTIVE_MS = Flags.tuneQuotaWindowDefaultParameters()
- ? DEFAULT_CURRENT_ALLOWED_TIME_PER_PERIOD_ACTIVE_MS :
- DEFAULT_LEGACY_ALLOWED_TIME_PER_PERIOD_ACTIVE_MS;
- ALLOWED_TIME_PER_PERIOD_ADDITION_INSTALLER_MS = Flags.tuneQuotaWindowDefaultParameters()
- ? DEFAULT_CURRENT_ALLOWED_TIME_PER_PERIOD_ADDITION_INSTALLER_MS :
- DEFAULT_ALLOWED_TIME_PER_PERIOD_ADDITION_INSTALLER_MS;
-
- WINDOW_SIZE_EXEMPTED_MS = Flags.tuneQuotaWindowDefaultParameters()
- ? DEFAULT_LATEST_WINDOW_SIZE_EXEMPTED_MS :
- DEFAULT_CURRENT_WINDOW_SIZE_EXEMPTED_MS;
- WINDOW_SIZE_ACTIVE_MS = Flags.tuneQuotaWindowDefaultParameters()
- ? DEFAULT_LATEST_WINDOW_SIZE_ACTIVE_MS :
- DEFAULT_CURRENT_WINDOW_SIZE_ACTIVE_MS;
- WINDOW_SIZE_WORKING_MS = DEFAULT_CURRENT_WINDOW_SIZE_WORKING_MS;
- WINDOW_SIZE_FREQUENT_MS = DEFAULT_CURRENT_WINDOW_SIZE_FREQUENT_MS;
+ void adjustDefaultBucketWindowSizes(boolean useLegacyQuotaConstants) {
+ if (useLegacyQuotaConstants) {
+ ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS =
+ DEFAULT_LEGACY_ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS;
+ ALLOWED_TIME_PER_PERIOD_ACTIVE_MS =
+ DEFAULT_LEGACY_ALLOWED_TIME_PER_PERIOD_ACTIVE_MS;
+ ALLOWED_TIME_PER_PERIOD_ADDITION_INSTALLER_MS =
+ DEFAULT_ALLOWED_TIME_PER_PERIOD_ADDITION_INSTALLER_MS;
+
+ WINDOW_SIZE_EXEMPTED_MS = DEFAULT_LEGACY_WINDOW_SIZE_EXEMPTED_MS;
+ WINDOW_SIZE_ACTIVE_MS = DEFAULT_LEGACY_WINDOW_SIZE_ACTIVE_MS;
+ WINDOW_SIZE_WORKING_MS = DEFAULT_LEGACY_WINDOW_SIZE_WORKING_MS;
+ WINDOW_SIZE_FREQUENT_MS = DEFAULT_LEGACY_WINDOW_SIZE_FREQUENT_MS;
+ } else {
+ ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS = Flags.tuneQuotaWindowDefaultParameters()
+ ? DEFAULT_CURRENT_ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS :
+ DEFAULT_LEGACY_ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS;
+ ALLOWED_TIME_PER_PERIOD_ACTIVE_MS = Flags.tuneQuotaWindowDefaultParameters()
+ ? DEFAULT_CURRENT_ALLOWED_TIME_PER_PERIOD_ACTIVE_MS :
+ DEFAULT_LEGACY_ALLOWED_TIME_PER_PERIOD_ACTIVE_MS;
+ ALLOWED_TIME_PER_PERIOD_ADDITION_INSTALLER_MS =
+ Flags.tuneQuotaWindowDefaultParameters()
+ ? DEFAULT_CURRENT_ALLOWED_TIME_PER_PERIOD_ADDITION_INSTALLER_MS :
+ DEFAULT_ALLOWED_TIME_PER_PERIOD_ADDITION_INSTALLER_MS;
+
+ WINDOW_SIZE_EXEMPTED_MS = Flags.tuneQuotaWindowDefaultParameters()
+ ? DEFAULT_LATEST_WINDOW_SIZE_EXEMPTED_MS :
+ DEFAULT_CURRENT_WINDOW_SIZE_EXEMPTED_MS;
+ WINDOW_SIZE_ACTIVE_MS = Flags.tuneQuotaWindowDefaultParameters()
+ ? DEFAULT_LATEST_WINDOW_SIZE_ACTIVE_MS :
+ DEFAULT_CURRENT_WINDOW_SIZE_ACTIVE_MS;
+ WINDOW_SIZE_WORKING_MS = DEFAULT_CURRENT_WINDOW_SIZE_WORKING_MS;
+ WINDOW_SIZE_FREQUENT_MS = DEFAULT_CURRENT_WINDOW_SIZE_FREQUENT_MS;
+ }
mAllowedTimePerPeriodMs[EXEMPTED_INDEX] = Math.min(MAX_PERIOD_MS,
Math.max(MINUTE_IN_MILLIS, ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS));
@@ -3640,10 +3693,15 @@ public final class QuotaController extends StateController {
ALLOWED_TIME_PER_PERIOD_ADDITION_INSTALLER_MS);
}
- void adjustDefaultEjLimits() {
- EJ_LIMIT_WORKING_MS = DEFAULT_CURRENT_EJ_LIMIT_WORKING_MS;
- EJ_TOP_APP_TIME_CHUNK_SIZE_MS = DEFAULT_CURRENT_EJ_TOP_APP_TIME_CHUNK_SIZE_MS;
- EJ_REWARD_INTERACTION_MS = DEFAULT_CURRENT_EJ_REWARD_INTERACTION_MS;
+ void adjustDefaultEjLimits(boolean useLegacyQuotaConstants) {
+ EJ_LIMIT_WORKING_MS = useLegacyQuotaConstants ? DEFAULT_LEGACY_EJ_LIMIT_WORKING_MS
+ : DEFAULT_CURRENT_EJ_LIMIT_WORKING_MS;
+ EJ_TOP_APP_TIME_CHUNK_SIZE_MS = useLegacyQuotaConstants
+ ? DEFAULT_LEGACY_EJ_TOP_APP_TIME_CHUNK_SIZE_MS :
+ DEFAULT_CURRENT_EJ_TOP_APP_TIME_CHUNK_SIZE_MS;
+ EJ_REWARD_INTERACTION_MS = useLegacyQuotaConstants
+ ? DEFAULT_LEGACY_EJ_REWARD_INTERACTION_MS
+ : DEFAULT_CURRENT_EJ_REWARD_INTERACTION_MS;
// The limit must be in the range [15 minutes, active limit].
mEJLimitsMs[WORKING_INDEX] = Math.max(15 * MINUTE_IN_MILLIS,
@@ -3668,6 +3726,8 @@ public final class QuotaController extends StateController {
public void processConstantLocked(@NonNull DeviceConfig.Properties properties,
@NonNull String key) {
+ final boolean isCompatEnabled = isCompatOverridedForQuotaConstantAdjustment();
+
switch (key) {
case KEY_ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS:
case KEY_ALLOWED_TIME_PER_PERIOD_ACTIVE_MS:
@@ -3835,7 +3895,8 @@ public final class QuotaController extends StateController {
case KEY_EJ_TOP_APP_TIME_CHUNK_SIZE_MS:
// We don't need to re-evaluate execution stats or constraint status for this.
EJ_TOP_APP_TIME_CHUNK_SIZE_MS =
- properties.getLong(key, Flags.adjustQuotaDefaultConstants()
+ properties.getLong(key,
+ Flags.adjustQuotaDefaultConstants() && !isCompatEnabled
? DEFAULT_CURRENT_EJ_TOP_APP_TIME_CHUNK_SIZE_MS :
DEFAULT_LEGACY_EJ_TOP_APP_TIME_CHUNK_SIZE_MS);
// Limit chunking to be in the range [1 millisecond, 15 minutes] per event.
@@ -3873,7 +3934,8 @@ public final class QuotaController extends StateController {
case KEY_EJ_REWARD_INTERACTION_MS:
// We don't need to re-evaluate execution stats or constraint status for this.
EJ_REWARD_INTERACTION_MS =
- properties.getLong(key, Flags.adjustQuotaDefaultConstants()
+ properties.getLong(key,
+ Flags.adjustQuotaDefaultConstants() && !isCompatEnabled
? DEFAULT_CURRENT_EJ_REWARD_INTERACTION_MS :
DEFAULT_LEGACY_EJ_REWARD_INTERACTION_MS);
// Limit interaction reward to be in the range [5 seconds, 15 minutes] per
@@ -3914,6 +3976,8 @@ public final class QuotaController extends StateController {
}
mExecutionPeriodConstantsUpdated = true;
+ final boolean isCompatEnabled = isCompatOverridedForQuotaConstantAdjustment();
+
// Query the values as an atomic set.
final DeviceConfig.Properties properties = DeviceConfig.getProperties(
DeviceConfig.NAMESPACE_JOB_SCHEDULER,
@@ -3958,27 +4022,27 @@ public final class QuotaController extends StateController {
MAX_EXECUTION_TIME_MS = properties.getLong(KEY_MAX_EXECUTION_TIME_MS,
DEFAULT_MAX_EXECUTION_TIME_MS);
WINDOW_SIZE_EXEMPTED_MS = properties.getLong(KEY_WINDOW_SIZE_EXEMPTED_MS,
- (Flags.adjustQuotaDefaultConstants()
+ (Flags.adjustQuotaDefaultConstants() && !isCompatEnabled
&& Flags.tuneQuotaWindowDefaultParameters())
? DEFAULT_LATEST_WINDOW_SIZE_EXEMPTED_MS :
- (Flags.adjustQuotaDefaultConstants()
+ (Flags.adjustQuotaDefaultConstants() && !isCompatEnabled
? DEFAULT_CURRENT_WINDOW_SIZE_EXEMPTED_MS :
DEFAULT_LEGACY_WINDOW_SIZE_EXEMPTED_MS));
WINDOW_SIZE_ACTIVE_MS = properties.getLong(KEY_WINDOW_SIZE_ACTIVE_MS,
- (Flags.adjustQuotaDefaultConstants()
+ (Flags.adjustQuotaDefaultConstants() && !isCompatEnabled
&& Flags.tuneQuotaWindowDefaultParameters())
? DEFAULT_LATEST_WINDOW_SIZE_ACTIVE_MS :
- (Flags.adjustQuotaDefaultConstants()
+ (Flags.adjustQuotaDefaultConstants() && !isCompatEnabled
? DEFAULT_CURRENT_WINDOW_SIZE_ACTIVE_MS :
DEFAULT_LEGACY_WINDOW_SIZE_ACTIVE_MS));
WINDOW_SIZE_WORKING_MS =
properties.getLong(KEY_WINDOW_SIZE_WORKING_MS,
- Flags.adjustQuotaDefaultConstants()
+ Flags.adjustQuotaDefaultConstants() && !isCompatEnabled
? DEFAULT_CURRENT_WINDOW_SIZE_WORKING_MS :
DEFAULT_LEGACY_WINDOW_SIZE_WORKING_MS);
WINDOW_SIZE_FREQUENT_MS =
properties.getLong(KEY_WINDOW_SIZE_FREQUENT_MS,
- Flags.adjustQuotaDefaultConstants()
+ Flags.adjustQuotaDefaultConstants() && !isCompatEnabled
? DEFAULT_CURRENT_WINDOW_SIZE_FREQUENT_MS :
DEFAULT_LEGACY_WINDOW_SIZE_FREQUENT_MS);
WINDOW_SIZE_RARE_MS = properties.getLong(KEY_WINDOW_SIZE_RARE_MS,
@@ -4149,6 +4213,8 @@ public final class QuotaController extends StateController {
}
mEJLimitConstantsUpdated = true;
+ final boolean isCompatEnabled = isCompatOverridedForQuotaConstantAdjustment();
+
// Query the values as an atomic set.
final DeviceConfig.Properties properties = DeviceConfig.getProperties(
DeviceConfig.NAMESPACE_JOB_SCHEDULER,
@@ -4163,7 +4229,7 @@ public final class QuotaController extends StateController {
EJ_LIMIT_ACTIVE_MS = properties.getLong(
KEY_EJ_LIMIT_ACTIVE_MS, DEFAULT_EJ_LIMIT_ACTIVE_MS);
EJ_LIMIT_WORKING_MS = properties.getLong(
- KEY_EJ_LIMIT_WORKING_MS, Flags.adjustQuotaDefaultConstants()
+ KEY_EJ_LIMIT_WORKING_MS, Flags.adjustQuotaDefaultConstants() && !isCompatEnabled
? DEFAULT_CURRENT_EJ_LIMIT_WORKING_MS :
DEFAULT_LEGACY_EJ_LIMIT_WORKING_MS);
EJ_LIMIT_FREQUENT_MS = properties.getLong(
diff --git a/boot/boot-image-profile.txt b/boot/boot-image-profile.txt
index d7c409fd54b4..4c3e5dce7a95 100644
--- a/boot/boot-image-profile.txt
+++ b/boot/boot-image-profile.txt
@@ -28829,7 +28829,6 @@ Landroid/media/audiopolicy/AudioProductStrategy$AudioAttributesGroup;
Landroid/media/audiopolicy/AudioProductStrategy;
Landroid/media/audiopolicy/AudioVolumeGroup$1;
Landroid/media/audiopolicy/AudioVolumeGroup;
-Landroid/media/audiopolicy/AudioVolumeGroupChangeHandler;
Landroid/media/audiopolicy/IAudioPolicyCallback$Stub$Proxy;
Landroid/media/audiopolicy/IAudioPolicyCallback$Stub;
Landroid/media/audiopolicy/IAudioPolicyCallback;
diff --git a/boot/preloaded-classes b/boot/preloaded-classes
index 7f4b3244c164..048687781774 100644
--- a/boot/preloaded-classes
+++ b/boot/preloaded-classes
@@ -5509,7 +5509,6 @@ android.media.audiopolicy.AudioProductStrategy$AudioAttributesGroup
android.media.audiopolicy.AudioProductStrategy
android.media.audiopolicy.AudioVolumeGroup$1
android.media.audiopolicy.AudioVolumeGroup
-android.media.audiopolicy.AudioVolumeGroupChangeHandler
android.media.audiopolicy.IAudioPolicyCallback$Stub$Proxy
android.media.audiopolicy.IAudioPolicyCallback$Stub
android.media.audiopolicy.IAudioPolicyCallback
diff --git a/cmds/app_process/Android.bp b/cmds/app_process/Android.bp
index a1575173ded6..3c7609e1d8ed 100644
--- a/cmds/app_process/Android.bp
+++ b/cmds/app_process/Android.bp
@@ -56,6 +56,7 @@ cc_binary {
"libsigchain",
"libutils",
+ "libutilscallstack",
// This is a list of libraries that need to be included in order to avoid
// bad apps. This prevents a library from having a mismatch when resolving
diff --git a/cmds/incidentd/src/IncidentService.cpp b/cmds/incidentd/src/IncidentService.cpp
index 5ebf3e2c3047..de35ffc3fdb9 100644
--- a/cmds/incidentd/src/IncidentService.cpp
+++ b/cmds/incidentd/src/IncidentService.cpp
@@ -52,7 +52,11 @@ enum {
#define SKIPPED_DUMPSTATE_SECTIONS { \
1100, 1101, 1102, 1103, 1104, 1105, 1106, 1107, 1108, /* Logs */ \
1200, 1201, 1202, /* Native, hal, java traces */ \
- 3018, /* dumpsys meminfo*/ }
+ /* dumpsys sections except for odpm data (3054- 3056) which are still needed */ \
+ 3000, 3001, 3002, 3003, 3004, 3005, 3006, 3007, 3008, 3009, 3010, 3011, 3012, 3013, \
+ 3014, 3015, 3016, 3017, 3018, 3019, 3020, 3021, 3022, 3023, 3024, 3027, 3028, 3029, \
+ 3030, 3031, 3032, 3033, 3034, 3035, 3036, 3037, 3038, 3039, 3040, 3041, 3042, 3043, \
+ 3044, 3045, 3046, 3047, 3048, 3049, 3050, 3051, 3052, 3053, 4000, 4001,}
namespace android {
namespace os {
diff --git a/cmds/uinput/src/com/android/commands/uinput/EvemuParser.java b/cmds/uinput/src/com/android/commands/uinput/EvemuParser.java
index da991624e685..d3e62d5351f0 100644
--- a/cmds/uinput/src/com/android/commands/uinput/EvemuParser.java
+++ b/cmds/uinput/src/com/android/commands/uinput/EvemuParser.java
@@ -48,12 +48,17 @@ public class EvemuParser implements EventParser {
private static class CommentAwareReader {
private final LineNumberReader mReader;
- private String mPreviousLine;
- private String mNextLine;
+ /** The previous line of the file, or {@code null} if we're at the start of the file. */
+ private @Nullable String mPreviousLine;
+ /**
+ * The next line of the file to be returned from {@link #peekLine()}, or {@code null} if we
+ * haven't peeked since the last {@link #advance()} or are at the end of the file.
+ */
+ private @Nullable String mNextLine;
+ private boolean mAtEndOfFile = false;
- CommentAwareReader(LineNumberReader in) throws IOException {
+ CommentAwareReader(LineNumberReader in) {
mReader = in;
- mNextLine = findNextLine();
}
private @Nullable String findNextLine() throws IOException {
@@ -61,7 +66,7 @@ public class EvemuParser implements EventParser {
while (line != null && line.length() == 0) {
String unstrippedLine = mReader.readLine();
if (unstrippedLine == null) {
- // End of file.
+ mAtEndOfFile = true;
return null;
}
line = stripComments(unstrippedLine);
@@ -85,22 +90,28 @@ public class EvemuParser implements EventParser {
* {@code null} if the end of the file is reached. However, it does not advance to the
* next line of the file.
*/
- public @Nullable String peekLine() {
+ public @Nullable String peekLine() throws IOException {
+ if (mNextLine == null && !mAtEndOfFile) {
+ mNextLine = findNextLine();
+ }
return mNextLine;
}
/** Moves to the next line of the file. */
- public void advance() throws IOException {
+ public void advance() {
mPreviousLine = mNextLine;
- mNextLine = findNextLine();
+ mNextLine = null;
}
public boolean isAtEndOfFile() {
- return mNextLine == null;
+ return mAtEndOfFile;
}
- /** Returns the previous line, for error messages. */
- public String getPreviousLine() {
+ /**
+ * Returns the previous line, for error messages. Will be {@code null} if we're at the start
+ * of the file.
+ */
+ public @Nullable String getPreviousLine() {
return mPreviousLine;
}
diff --git a/config/preloaded-classes b/config/preloaded-classes
index 707acb00b102..3f0e00be75a7 100644
--- a/config/preloaded-classes
+++ b/config/preloaded-classes
@@ -5514,7 +5514,6 @@ android.media.audiopolicy.AudioProductStrategy$AudioAttributesGroup
android.media.audiopolicy.AudioProductStrategy
android.media.audiopolicy.AudioVolumeGroup$1
android.media.audiopolicy.AudioVolumeGroup
-android.media.audiopolicy.AudioVolumeGroupChangeHandler
android.media.audiopolicy.IAudioPolicyCallback$Stub$Proxy
android.media.audiopolicy.IAudioPolicyCallback$Stub
android.media.audiopolicy.IAudioPolicyCallback
diff --git a/core/api/current.txt b/core/api/current.txt
index 4cd6d6f03ee8..bba21f418e41 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -24820,7 +24820,7 @@ package android.media {
method public android.view.Surface getSurface();
method public boolean isPrivacySensitive();
method public void pause() throws java.lang.IllegalStateException;
- method public void prepare() throws java.io.IOException, java.lang.IllegalStateException;
+ method @RequiresPermission(value=android.Manifest.permission.RECORD_AUDIO, conditional=true) public void prepare() throws java.io.IOException, java.lang.IllegalStateException;
method public void registerAudioRecordingCallback(@NonNull java.util.concurrent.Executor, @NonNull android.media.AudioManager.AudioRecordingCallback);
method public void release();
method public void removeOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener);
@@ -24861,7 +24861,7 @@ package android.media {
method public void setVideoProfile(@NonNull android.media.EncoderProfiles.VideoProfile);
method public void setVideoSize(int, int) throws java.lang.IllegalStateException;
method public void setVideoSource(int) throws java.lang.IllegalStateException;
- method public void start() throws java.lang.IllegalStateException;
+ method @RequiresPermission(value=android.Manifest.permission.RECORD_AUDIO, conditional=true) public void start() throws java.lang.IllegalStateException;
method public void stop() throws java.lang.IllegalStateException;
method public void unregisterAudioRecordingCallback(@NonNull android.media.AudioManager.AudioRecordingCallback);
field public static final int MEDIA_ERROR_SERVER_DIED = 100; // 0x64
@@ -34264,6 +34264,7 @@ package android.os {
method public boolean hasFileDescriptors();
method public boolean hasFileDescriptors(int, int);
method public byte[] marshall();
+ method @FlaggedApi("android.os.parcel_marshall_bytebuffer") public void marshall(@NonNull java.nio.ByteBuffer);
method @NonNull public static android.os.Parcel obtain();
method @NonNull public static android.os.Parcel obtain(@NonNull android.os.IBinder);
method @Deprecated @Nullable public Object[] readArray(@Nullable ClassLoader);
@@ -34333,6 +34334,7 @@ package android.os {
method public void setDataSize(int);
method public void setPropagateAllowBlocking();
method public void unmarshall(@NonNull byte[], int, int);
+ method @FlaggedApi("android.os.parcel_marshall_bytebuffer") public void unmarshall(@NonNull java.nio.ByteBuffer);
method public void writeArray(@Nullable Object[]);
method public void writeBinderArray(@Nullable android.os.IBinder[]);
method public void writeBinderList(@Nullable java.util.List<android.os.IBinder>);
@@ -55423,6 +55425,7 @@ package android.view {
method public void dispatchOnDraw();
method public void dispatchOnGlobalLayout();
method public boolean dispatchOnPreDraw();
+ method @FlaggedApi("android.view.flags.enable_dispatch_on_scroll_changed") public void dispatchOnScrollChanged();
method public boolean isAlive();
method public void registerFrameCommitCallback(@NonNull Runnable);
method @Deprecated public void removeGlobalOnLayoutListener(android.view.ViewTreeObserver.OnGlobalLayoutListener);
diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt
index 526a213a6003..98570172e43c 100644
--- a/core/api/module-lib-current.txt
+++ b/core/api/module-lib-current.txt
@@ -304,6 +304,8 @@ package android.net {
field public static final int TYPE_VPN_LEGACY = 3; // 0x3
field public static final int TYPE_VPN_NONE = -1; // 0xffffffff
field public static final int TYPE_VPN_OEM = 4; // 0x4
+ field @FlaggedApi("android.net.platform.flags.vpn_type_oem_service_and_legacy") public static final int TYPE_VPN_OEM_LEGACY = 6; // 0x6
+ field @FlaggedApi("android.net.platform.flags.vpn_type_oem_service_and_legacy") public static final int TYPE_VPN_OEM_SERVICE = 5; // 0x5
field public static final int TYPE_VPN_PLATFORM = 2; // 0x2
field public static final int TYPE_VPN_SERVICE = 1; // 0x1
}
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 35720fd17769..42c60b0ba0da 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -2940,6 +2940,13 @@ package android.app.smartspace.uitemplatedata {
package android.app.supervision {
+ @FlaggedApi("android.app.supervision.flags.enable_supervision_app_service") public class SupervisionAppService extends android.app.Service {
+ ctor public SupervisionAppService();
+ method @Nullable public final android.os.IBinder onBind(@Nullable android.content.Intent);
+ method @FlaggedApi("android.app.supervision.flags.enable_supervision_app_service") public void onDisabled();
+ method @FlaggedApi("android.app.supervision.flags.enable_supervision_app_service") public void onEnabled();
+ }
+
@FlaggedApi("android.app.supervision.flags.supervision_manager_apis") public class SupervisionManager {
method @FlaggedApi("android.app.supervision.flags.supervision_manager_apis") @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.QUERY_USERS}) public android.content.Intent createConfirmSupervisionCredentialsIntent();
method @FlaggedApi("android.app.supervision.flags.supervision_manager_apis") @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.QUERY_USERS}) public boolean isSupervisionEnabled();
@@ -7354,7 +7361,15 @@ package android.media {
public class AudioDeviceVolumeManager {
method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.MODIFY_AUDIO_ROUTING, android.Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED}) public android.media.VolumeInfo getDeviceVolume(@NonNull android.media.VolumeInfo, @NonNull android.media.AudioDeviceAttributes);
+ method @FlaggedApi("android.media.audio.unify_absolute_volume_management") @RequiresPermission(anyOf={android.Manifest.permission.MODIFY_AUDIO_ROUTING, "android.permission.QUERY_AUDIO_STATE", android.Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED}) public int getDeviceVolumeBehavior(@NonNull android.media.AudioDeviceAttributes);
method @RequiresPermission(anyOf={android.Manifest.permission.MODIFY_AUDIO_ROUTING, android.Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED}) public void setDeviceVolume(@NonNull android.media.VolumeInfo, @NonNull android.media.AudioDeviceAttributes);
+ method @FlaggedApi("android.media.audio.unify_absolute_volume_management") @RequiresPermission(anyOf={android.Manifest.permission.MODIFY_AUDIO_ROUTING, android.Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED}) public void setDeviceVolumeBehavior(@NonNull android.media.AudioDeviceAttributes, int);
+ field @FlaggedApi("android.media.audio.unify_absolute_volume_management") public static final int DEVICE_VOLUME_BEHAVIOR_ABSOLUTE = 3; // 0x3
+ field @FlaggedApi("android.media.audio.unify_absolute_volume_management") public static final int DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_ADJUST_ONLY = 5; // 0x5
+ field @FlaggedApi("android.media.audio.unify_absolute_volume_management") public static final int DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_MULTI_MODE = 4; // 0x4
+ field @FlaggedApi("android.media.audio.unify_absolute_volume_management") public static final int DEVICE_VOLUME_BEHAVIOR_FIXED = 2; // 0x2
+ field @FlaggedApi("android.media.audio.unify_absolute_volume_management") public static final int DEVICE_VOLUME_BEHAVIOR_FULL = 1; // 0x1
+ field @FlaggedApi("android.media.audio.unify_absolute_volume_management") public static final int DEVICE_VOLUME_BEHAVIOR_VARIABLE = 0; // 0x0
}
public final class AudioFocusInfo implements android.os.Parcelable {
@@ -7399,7 +7414,7 @@ package android.media {
method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public static java.util.List<android.media.audiopolicy.AudioVolumeGroup> getAudioVolumeGroups();
method @NonNull @RequiresPermission(android.Manifest.permission.CALL_AUDIO_INTERCEPTION) public android.media.AudioRecord getCallDownlinkExtractionAudioRecord(@NonNull android.media.AudioFormat);
method @NonNull @RequiresPermission(android.Manifest.permission.CALL_AUDIO_INTERCEPTION) public android.media.AudioTrack getCallUplinkInjectionAudioTrack(@NonNull android.media.AudioFormat);
- method @RequiresPermission(anyOf={android.Manifest.permission.MODIFY_AUDIO_ROUTING, "android.permission.QUERY_AUDIO_STATE", android.Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED}) public int getDeviceVolumeBehavior(@NonNull android.media.AudioDeviceAttributes);
+ method @Deprecated @FlaggedApi("android.media.audio.unify_absolute_volume_management") @RequiresPermission(anyOf={android.Manifest.permission.MODIFY_AUDIO_ROUTING, "android.permission.QUERY_AUDIO_STATE", android.Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED}) public int getDeviceVolumeBehavior(@NonNull android.media.AudioDeviceAttributes);
method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.MODIFY_AUDIO_ROUTING, "android.permission.QUERY_AUDIO_STATE"}) public java.util.List<android.media.AudioDeviceAttributes> getDevicesForAttributes(@NonNull android.media.AudioAttributes);
method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED) public java.util.List<java.lang.Integer> getIndependentStreamTypes();
method @RequiresPermission("android.permission.QUERY_AUDIO_STATE") public int getLastAudibleStreamVolume(int);
@@ -7427,7 +7442,7 @@ package android.media {
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void muteAwaitConnection(@NonNull int[], @NonNull android.media.AudioDeviceAttributes, long, @NonNull java.util.concurrent.TimeUnit) throws java.lang.IllegalStateException;
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int registerAudioPolicy(@NonNull android.media.audiopolicy.AudioPolicy);
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void registerMuteAwaitConnectionCallback(@NonNull java.util.concurrent.Executor, @NonNull android.media.AudioManager.MuteAwaitConnectionCallback);
- method public void registerVolumeGroupCallback(@NonNull java.util.concurrent.Executor, @NonNull android.media.AudioManager.VolumeGroupCallback);
+ method @FlaggedApi("android.media.audio.register_volume_callback_api_hardening") @RequiresPermission("Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED") public void registerVolumeGroupCallback(@NonNull java.util.concurrent.Executor, @NonNull android.media.AudioManager.VolumeGroupCallback);
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void removeAssistantServicesUids(@NonNull int[]);
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public boolean removeDeviceAsNonDefaultForStrategy(@NonNull android.media.audiopolicy.AudioProductStrategy, @NonNull android.media.AudioDeviceAttributes);
method @RequiresPermission(anyOf={android.Manifest.permission.MODIFY_AUDIO_ROUTING, "android.permission.QUERY_AUDIO_STATE"}) public void removeOnDevicesForAttributesChangedListener(@NonNull android.media.AudioManager.OnDevicesForAttributesChangedListener);
@@ -7445,7 +7460,7 @@ package android.media {
method public void setAudioServerStateCallback(@NonNull java.util.concurrent.Executor, @NonNull android.media.AudioManager.AudioServerStateCallback);
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void setBluetoothVariableLatencyEnabled(boolean);
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public boolean setDeviceAsNonDefaultForStrategy(@NonNull android.media.audiopolicy.AudioProductStrategy, @NonNull android.media.AudioDeviceAttributes);
- method @RequiresPermission(anyOf={android.Manifest.permission.MODIFY_AUDIO_ROUTING, android.Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED}) public void setDeviceVolumeBehavior(@NonNull android.media.AudioDeviceAttributes, int);
+ method @Deprecated @FlaggedApi("android.media.audio.unify_absolute_volume_management") @RequiresPermission(anyOf={android.Manifest.permission.MODIFY_AUDIO_ROUTING, android.Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED}) public void setDeviceVolumeBehavior(@NonNull android.media.AudioDeviceAttributes, int);
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void setFocusRequestResult(@NonNull android.media.AudioFocusInfo, int, @NonNull android.media.audiopolicy.AudioPolicy);
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public boolean setPreferredDeviceForCapturePreset(int, @NonNull android.media.AudioDeviceAttributes);
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public boolean setPreferredDeviceForStrategy(@NonNull android.media.audiopolicy.AudioProductStrategy, @NonNull android.media.AudioDeviceAttributes);
@@ -7458,19 +7473,19 @@ package android.media {
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void unregisterAudioPolicy(@NonNull android.media.audiopolicy.AudioPolicy);
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void unregisterAudioPolicyAsync(@NonNull android.media.audiopolicy.AudioPolicy);
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void unregisterMuteAwaitConnectionCallback(@NonNull android.media.AudioManager.MuteAwaitConnectionCallback);
- method public void unregisterVolumeGroupCallback(@NonNull android.media.AudioManager.VolumeGroupCallback);
+ method @FlaggedApi("android.media.audio.register_volume_callback_api_hardening") @RequiresPermission("Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED") public void unregisterVolumeGroupCallback(@NonNull android.media.AudioManager.VolumeGroupCallback);
field public static final String ACTION_VOLUME_CHANGED = "android.media.VOLUME_CHANGED_ACTION";
field public static final int AUDIOFOCUS_FLAG_DELAY_OK = 1; // 0x1
field public static final int AUDIOFOCUS_FLAG_LOCK = 4; // 0x4
field public static final int AUDIOFOCUS_FLAG_PAUSES_ON_DUCKABLE_LOSS = 2; // 0x2
field @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public static final int DEVICE_CONNECTION_STATE_CONNECTED = 1; // 0x1
field @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public static final int DEVICE_CONNECTION_STATE_DISCONNECTED = 0; // 0x0
- field public static final int DEVICE_VOLUME_BEHAVIOR_ABSOLUTE = 3; // 0x3
- field public static final int DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_ADJUST_ONLY = 5; // 0x5
- field public static final int DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_MULTI_MODE = 4; // 0x4
- field public static final int DEVICE_VOLUME_BEHAVIOR_FIXED = 2; // 0x2
- field public static final int DEVICE_VOLUME_BEHAVIOR_FULL = 1; // 0x1
- field public static final int DEVICE_VOLUME_BEHAVIOR_VARIABLE = 0; // 0x0
+ field @Deprecated @FlaggedApi("android.media.audio.unify_absolute_volume_management") public static final int DEVICE_VOLUME_BEHAVIOR_ABSOLUTE = 3; // 0x3
+ field @Deprecated @FlaggedApi("android.media.audio.unify_absolute_volume_management") public static final int DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_ADJUST_ONLY = 5; // 0x5
+ field @Deprecated @FlaggedApi("android.media.audio.unify_absolute_volume_management") public static final int DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_MULTI_MODE = 4; // 0x4
+ field @Deprecated @FlaggedApi("android.media.audio.unify_absolute_volume_management") public static final int DEVICE_VOLUME_BEHAVIOR_FIXED = 2; // 0x2
+ field @Deprecated @FlaggedApi("android.media.audio.unify_absolute_volume_management") public static final int DEVICE_VOLUME_BEHAVIOR_FULL = 1; // 0x1
+ field @Deprecated @FlaggedApi("android.media.audio.unify_absolute_volume_management") public static final int DEVICE_VOLUME_BEHAVIOR_VARIABLE = 0; // 0x0
field public static final String EXTRA_VOLUME_STREAM_TYPE = "android.media.EXTRA_VOLUME_STREAM_TYPE";
field public static final String EXTRA_VOLUME_STREAM_VALUE = "android.media.EXTRA_VOLUME_STREAM_VALUE";
field public static final int FLAG_BLUETOOTH_ABS_VOLUME = 64; // 0x40
@@ -18613,6 +18628,7 @@ package android.telephony.satellite {
method @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void deprovisionService(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
method @NonNull @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public java.util.Set<java.lang.Integer> getAttachRestrictionReasonsForCarrier(int);
method @FlaggedApi("com.android.internal.telephony.flags.satellite_25q4_apis") @NonNull @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public java.util.List<java.lang.String> getSatelliteDataOptimizedApps();
+ method @FlaggedApi("com.android.internal.telephony.flags.satellite_25q4_apis") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public int getSatelliteDataSupportMode(int);
method @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") @NonNull @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public int[] getSatelliteDisallowedReasons();
method @NonNull @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public java.util.List<java.lang.String> getSatellitePlmnsForCarrier(int);
method @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void pollPendingDatagrams(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
@@ -18695,6 +18711,10 @@ package android.telephony.satellite {
field public static final int SATELLITE_DATAGRAM_TRANSFER_STATE_SEND_SUCCESS = 2; // 0x2
field public static final int SATELLITE_DATAGRAM_TRANSFER_STATE_UNKNOWN = -1; // 0xffffffff
field public static final int SATELLITE_DATAGRAM_TRANSFER_STATE_WAITING_TO_CONNECT = 8; // 0x8
+ field @FlaggedApi("com.android.internal.telephony.flags.satellite_25q4_apis") public static final int SATELLITE_DATA_SUPPORT_CONSTRAINED = 1; // 0x1
+ field @FlaggedApi("com.android.internal.telephony.flags.satellite_25q4_apis") public static final int SATELLITE_DATA_SUPPORT_RESTRICTED = 0; // 0x0
+ field @FlaggedApi("com.android.internal.telephony.flags.satellite_25q4_apis") public static final int SATELLITE_DATA_SUPPORT_UNCONSTRAINED = 2; // 0x2
+ field @FlaggedApi("com.android.internal.telephony.flags.satellite_25q4_apis") public static final int SATELLITE_DATA_SUPPORT_UNKNOWN = -1; // 0xffffffff
field public static final int SATELLITE_MODEM_STATE_CONNECTED = 7; // 0x7
field public static final int SATELLITE_MODEM_STATE_DATAGRAM_RETRYING = 3; // 0x3
field public static final int SATELLITE_MODEM_STATE_DATAGRAM_TRANSFERRING = 2; // 0x2
@@ -19263,7 +19283,6 @@ package android.webkit {
method @Deprecated public abstract void setUseWebViewBackgroundForOverscrollBackground(boolean);
method @Deprecated public abstract void setUserAgent(int);
method public abstract void setVideoOverlayForEmbeddedEncryptedVideoEnabled(boolean);
- field @FlaggedApi("android.webkit.enable_chips") public static final long ENABLE_CHIPS = 380890146L; // 0x16b3ec22L
field public static final long ENABLE_SIMPLIFIED_DARK_MODE = 214741472L; // 0xcccb1e0L
field @FlaggedApi("android.webkit.user_agent_reduction") public static final long ENABLE_USER_AGENT_REDUCTION = 371034303L; // 0x161d88bfL
}
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 4c8283907712..daa1902edf02 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -1955,6 +1955,10 @@ package android.media {
method public static void enforceValidAudioDeviceTypeOut(int);
}
+ public class AudioDeviceVolumeManager {
+ method @RequiresPermission(anyOf={android.Manifest.permission.MODIFY_AUDIO_ROUTING, android.Manifest.permission.QUERY_AUDIO_STATE, android.Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED}) public boolean isFullVolumeDevice();
+ }
+
public final class AudioFocusRequest {
method @Nullable public android.media.AudioManager.OnAudioFocusChangeListener getOnAudioFocusChangeListener();
}
@@ -2010,7 +2014,6 @@ package android.media {
method @NonNull public android.media.VolumePolicy getVolumePolicy();
method public boolean hasRegisteredDynamicPolicy();
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED) public boolean isCsdEnabled();
- method @RequiresPermission(anyOf={android.Manifest.permission.MODIFY_AUDIO_ROUTING, android.Manifest.permission.QUERY_AUDIO_STATE, android.Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED}) public boolean isFullVolumeDevice();
method @RequiresPermission(android.Manifest.permission.CALL_AUDIO_INTERCEPTION) public boolean isPstnCallAudioInterceptable();
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED) public boolean isVolumeControlUsingVolumeGroups();
method public void permissionUpdateBarrier();
diff --git a/core/java/Android.bp b/core/java/Android.bp
index 980d9737aba7..7123ee7b5777 100644
--- a/core/java/Android.bp
+++ b/core/java/Android.bp
@@ -17,19 +17,19 @@ aidl_library {
filegroup {
name: "framework-core-sources",
srcs: [
- "**/*.java",
"**/*.aidl",
- ":systemfeatures-gen-srcs",
+ "**/*.java",
":framework-nfc-non-updatable-sources",
":messagequeue-gen",
":ranging_stack_mock_initializer",
+ ":systemfeatures-gen-srcs",
],
// Exactly one MessageQueue.java will be added to srcs by messagequeue-gen
exclude_srcs: [
+ "**/*_ravenwood.java",
+ ":dynamic_instrumentation_manager_aidl_sources",
"android/os/*MessageQueue/**/*.java",
"android/ranging/**/*.java",
- ":dynamic_instrumentation_manager_aidl_sources",
- "**/*_ravenwood.java",
],
visibility: ["//frameworks/base"],
}
@@ -52,9 +52,9 @@ soong_config_module_type {
"release_ranging_stack",
],
properties: [
- "srcs",
"cmd",
"out",
+ "srcs",
],
}
@@ -153,9 +153,9 @@ filegroup {
filegroup {
name: "framework-core-sources-for-test-mock",
srcs: [
+ "android/accounts/AccountManager.java",
"android/accounts/AccountManagerCallback.java",
"android/accounts/AccountManagerFuture.java",
- "android/accounts/AccountManager.java",
"android/accounts/AccountsException.java",
"android/accounts/AuthenticatorException.java",
"android/accounts/OperationCanceledException.java",
@@ -163,8 +163,8 @@ filegroup {
"android/app/IApplicationThread.aidl",
"android/app/IServiceConnection.aidl",
"android/app/PackageDeleteObserver.java",
- "android/content/ComponentCallbacks2.java",
"android/content/ComponentCallbacks.java",
+ "android/content/ComponentCallbacks2.java",
"android/content/ContentInterface.java",
"android/content/ContentProvider.java",
"android/content/ContentProviderNative.java",
@@ -178,8 +178,8 @@ filegroup {
"android/content/OperationApplicationException.java",
"android/content/pm/ActivityInfo.java",
"android/content/pm/ApplicationInfo.java",
- "android/content/pm/InstantAppInfo.java",
"android/content/pm/IPackageDataObserver.aidl",
+ "android/content/pm/InstantAppInfo.java",
"android/content/pm/KeySet.java",
"android/content/pm/PackageManager.java",
"android/content/pm/VerifierDeviceIdentity.java",
@@ -192,8 +192,8 @@ filegroup {
"android/os/Bundle.java",
"android/os/IBinder.java",
"android/os/IInterface.java",
- "android/os/Parcelable.java",
"android/os/ParcelFileDescriptor.java",
+ "android/os/Parcelable.java",
"android/os/RemoteException.java",
"android/os/storage/VolumeInfo.java",
"android/util/AndroidException.java",
@@ -215,24 +215,24 @@ filegroup {
filegroup {
name: "libvibrator_aidl",
srcs: [
+ "android/os/ExternalVibrationScale.aidl",
"android/os/IExternalVibrationController.aidl",
"android/os/IExternalVibratorService.aidl",
- "android/os/ExternalVibrationScale.aidl",
],
}
filegroup {
name: "libpowermanager_aidl",
srcs: [
- "android/os/Temperature.aidl",
"android/os/CoolingDevice.aidl",
+ "android/os/IPowerManager.aidl",
+ "android/os/IScreenTimeoutPolicyListener.aidl",
"android/os/IThermalEventListener.aidl",
"android/os/IThermalHeadroomListener.aidl",
- "android/os/IThermalStatusListener.aidl",
"android/os/IThermalService.aidl",
- "android/os/IPowerManager.aidl",
+ "android/os/IThermalStatusListener.aidl",
"android/os/IWakeLockCallback.aidl",
- "android/os/IScreenTimeoutPolicyListener.aidl",
+ "android/os/Temperature.aidl",
],
}
@@ -282,8 +282,8 @@ genrule {
java_library {
name: "uieventloggerlib",
srcs: [
- "com/android/internal/logging/UiEventLoggerImpl.java",
":statslog-framework-java-gen",
+ "com/android/internal/logging/UiEventLoggerImpl.java",
],
libs: [
"androidx.annotation_annotation",
@@ -353,11 +353,11 @@ filegroup {
name: "framework-permission-s-shared-srcs",
srcs: [
":modules-utils-preconditions-srcs",
- "com/android/internal/infra/AndroidFuture.java",
- "com/android/internal/infra/ServiceConnector.java",
+ "android/os/HandlerExecutor.java",
"com/android/internal/infra/AndroidFuture.aidl",
+ "com/android/internal/infra/AndroidFuture.java",
"com/android/internal/infra/IAndroidFuture.aidl",
- "android/os/HandlerExecutor.java",
+ "com/android/internal/infra/ServiceConnector.java",
],
}
@@ -393,10 +393,10 @@ filegroup {
"android/content/pm/FileSystemControlParcel.aidl",
"android/content/pm/IDataLoader.aidl",
"android/content/pm/IDataLoaderManager.aidl",
- "android/content/pm/InstallationFileParcel.aidl",
- "android/content/pm/InstallationFileLocation.aidl",
"android/content/pm/IDataLoaderStatusListener.aidl",
"android/content/pm/IPackageInstallerSessionFileSystemConnector.aidl",
+ "android/content/pm/InstallationFileLocation.aidl",
+ "android/content/pm/InstallationFileParcel.aidl",
],
}
@@ -404,9 +404,9 @@ filegroup {
name: "incremental_manager_aidl",
srcs: [
"android/os/incremental/IIncrementalService.aidl",
+ "android/os/incremental/IStorageHealthListener.aidl",
"android/os/incremental/IStorageLoadingProgressListener.aidl",
"android/os/incremental/IncrementalNewFileParams.aidl",
- "android/os/incremental/IStorageHealthListener.aidl",
"android/os/incremental/PerUidReadTimeouts.aidl",
"android/os/incremental/StorageHealthCheckParams.aidl",
],
@@ -422,15 +422,15 @@ filegroup {
filegroup {
name: "hwbinder-stubs-srcs",
srcs: [
- "android/os/HidlSupport.java",
+ "android/os/DeadObjectException.java",
+ "android/os/DeadSystemException.java",
"android/os/HidlMemory.java",
+ "android/os/HidlSupport.java",
"android/os/HwBinder.java",
"android/os/HwBlob.java",
"android/os/HwParcel.java",
"android/os/IHwBinder.java",
"android/os/IHwInterface.java",
- "android/os/DeadObjectException.java",
- "android/os/DeadSystemException.java",
"android/os/NativeHandle.java",
"android/os/RemoteException.java",
"android/util/AndroidException.java",
@@ -444,12 +444,12 @@ filegroup {
cc_defaults {
name: "incremental_default",
cflags: [
+ "-DANDROID_BASE_UNIQUE_FD_DISABLE_IMPLICIT_CONVERSION",
"-Wall",
+ "-Werror",
"-Wextra",
"-Wextra-semi",
- "-Werror",
"-Wzero-as-null-pointer-constant",
- "-DANDROID_BASE_UNIQUE_FD_DISABLE_IMPLICIT_CONVERSION",
],
shared_libs: [
"libbinder",
@@ -489,8 +489,8 @@ cc_library {
],
defaults: ["incremental_default"],
shared_libs: [
- "libincremental_aidl-cpp",
"libdataloader_aidl-cpp",
+ "libincremental_aidl-cpp",
],
}
@@ -580,8 +580,8 @@ filegroup {
filegroup {
name: "framework-telephony-common-shared-srcs",
srcs: [
- "android/os/RegistrantList.java",
"android/os/Registrant.java",
+ "android/os/RegistrantList.java",
"android/util/IndentingPrintWriter.java",
"android/util/LocalLog.java",
"android/util/TimeUtils.java",
@@ -599,8 +599,8 @@ filegroup {
name: "framework-ims-common-shared-srcs",
srcs: [
":modules-utils-preconditions-srcs",
- "android/os/RegistrantList.java",
"android/os/Registrant.java",
+ "android/os/RegistrantList.java",
"com/android/internal/os/SomeArgs.java",
],
}
@@ -609,9 +609,9 @@ filegroup {
filegroup {
name: "framework-core-sources-for-fuzzers",
srcs: [
- "android/os/IInterface.java",
"android/os/Binder.java",
"android/os/IBinder.java",
+ "android/os/IInterface.java",
"android/os/Parcelable.java",
],
}
@@ -657,6 +657,17 @@ filegroup {
}
java_library {
+ name: "protolog-common-lib",
+ srcs: [
+ ":protolog-common-src",
+ ],
+ host_supported: true,
+ static_libs: [
+ "framework-annotations-lib",
+ ],
+}
+
+java_library {
name: "protolog-group",
srcs: [
"com/android/internal/protolog/common/IProtoLogGroup.java",
@@ -694,7 +705,9 @@ java_library {
name: "protolog-groups",
srcs: [
"com/android/internal/protolog/WmProtoLogGroups.java",
- ":protolog-common-src",
+ ],
+ static_libs: [
+ "protolog-common-lib",
],
}
diff --git a/core/java/android/animation/AnimatorSet.java b/core/java/android/animation/AnimatorSet.java
index 4a07de0410ae..36308e5e75b0 100644
--- a/core/java/android/animation/AnimatorSet.java
+++ b/core/java/android/animation/AnimatorSet.java
@@ -423,15 +423,19 @@ public final class AnimatorSet extends Animator implements AnimationHandler.Anim
notifyListeners(AnimatorCaller.ON_CANCEL, false);
callOnPlayingSet(Animator::cancel);
mPlayingSet.clear();
- // If the end callback is pending, invoke the end callbacks of the animator nodes before
- // ending this set. Pass notifyListeners=false because this endAnimation will do that.
- if (consumePendingEndListeners(false /* notifyListeners */)) {
- for (int i = mNodeMap.size() - 1; i >= 0; i--) {
- mNodeMap.keyAt(i).consumePendingEndListeners(true /* notifyListeners */);
- }
+ endAnimationAndNotifyEndListenersImmediately();
+ }
+ }
+
+ private void endAnimationAndNotifyEndListenersImmediately() {
+ // If the end callback is pending, invoke the end callbacks of the animator nodes before
+ // ending this set. Pass notifyListeners=false because endAnimation will do that.
+ if (consumePendingEndListeners(false /* notifyListeners */)) {
+ for (int i = mNodeMap.size() - 1; i >= 0; i--) {
+ mNodeMap.keyAt(i).consumePendingEndListeners(true /* notifyListeners */);
}
- endAnimation();
}
+ endAnimation();
}
/**
@@ -529,7 +533,7 @@ public final class AnimatorSet extends Animator implements AnimationHandler.Anim
}
}
}
- endAnimation();
+ endAnimationAndNotifyEndListenersImmediately();
}
/**
@@ -1455,8 +1459,6 @@ public final class AnimatorSet extends Animator implements AnimationHandler.Anim
private void endAnimation(boolean fromLastFrame) {
final boolean postNotifyEndListener = sPostNotifyEndListenerEnabled && mListeners != null
&& fromLastFrame && mTotalDuration > 0;
- mStarted = false;
- mLastFrameTime = -1;
mFirstFrame = -1;
mLastEventId = -1;
mPaused = false;
@@ -1466,11 +1468,18 @@ public final class AnimatorSet extends Animator implements AnimationHandler.Anim
// No longer receive callbacks
removeAnimationCallback();
+ // If postNotifyEndListener is false (most cases), then it is the same as calling
+ // completeEndAnimation directly.
notifyEndListenersFromEndAnimation(mReversing, postNotifyEndListener);
}
@Override
void completeEndAnimation(boolean isReversing, String notifyListenerTraceName) {
+ // The mStarted and mLastFrameTime are reset here because isStarted() and isRunning()
+ // can be true before notifying the end listeners. When notifying the end listeners,
+ // isStarted() and isRunning() should be false.
+ mStarted = false;
+ mLastFrameTime = -1;
super.completeEndAnimation(isReversing, notifyListenerTraceName);
removeAnimationEndListener();
mSelfPulse = true;
diff --git a/core/java/android/animation/ValueAnimator.java b/core/java/android/animation/ValueAnimator.java
index fbcc73ea59e7..8d34090c8f60 100644
--- a/core/java/android/animation/ValueAnimator.java
+++ b/core/java/android/animation/ValueAnimator.java
@@ -1212,6 +1212,10 @@ public class ValueAnimator extends Animator implements AnimationHandler.Animatio
initAnimation();
}
animateValue(shouldPlayBackward(mRepeatCount, mReversing) ? 0f : 1f);
+ if (mAnimationEndRequested) {
+ consumePendingEndListeners(true /* notifyListeners */);
+ return;
+ }
endAnimation();
}
@@ -1308,8 +1312,8 @@ public class ValueAnimator extends Animator implements AnimationHandler.Animatio
mLastFrameTime = -1;
mFirstFrameTime = -1;
mStartTime = -1;
- mRunning = false;
- mStarted = false;
+ // If postNotifyEndListener is false (most cases), then it is the same as calling
+ // completeEndAnimation directly.
notifyEndListenersFromEndAnimation(mReversing, postNotifyEndListener);
if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
Trace.asyncTraceEnd(Trace.TRACE_TAG_VIEW, getNameForTrace(),
@@ -1319,6 +1323,11 @@ public class ValueAnimator extends Animator implements AnimationHandler.Animatio
@Override
void completeEndAnimation(boolean isReversing, String notifyListenerTraceName) {
+ // The mRunning and mStarted are reset here because isStarted() and isRunning()
+ // can be true before notifying the end listeners. When notifying the end listeners,
+ // isStarted() and isRunning() should be false.
+ mRunning = false;
+ mStarted = false;
super.completeEndAnimation(isReversing, notifyListenerTraceName);
// mReversing needs to be reset *after* notifying the listeners for the end callbacks.
mReversing = false;
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 62816a2fa0f5..9cc7b8ff47c2 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -1188,7 +1188,7 @@ public class ActivityManager {
/** @hide Should this process state be considered jank perceptible? */
public static final boolean isProcStateJankPerceptible(int procState) {
- if (Flags.jankPerceptibleNarrow()) {
+ if (Flags.jankPerceptibleNarrow() && !Flags.jankPerceptibleNarrowHoldback()) {
return procState == PROCESS_STATE_PERSISTENT_UI
|| procState == PROCESS_STATE_TOP
|| procState == PROCESS_STATE_IMPORTANT_FOREGROUND
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 0a2b1eaaad6b..f44c2305591d 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -4014,7 +4014,7 @@ public final class ActivityThread extends ClientTransactionHandler
return VM_PROCESS_STATE_JANK_PERCEPTIBLE;
}
- if (Flags.jankPerceptibleNarrow()) {
+ if (Flags.jankPerceptibleNarrow() && !Flags.jankPerceptibleNarrowHoldback()) {
// Unlike other persistent processes, system server is often on
// the critical path for application startup. Mark it explicitly
// as jank perceptible regardless of processState.
@@ -7831,9 +7831,10 @@ public final class ActivityThread extends ClientTransactionHandler
// Register callback to report native memory metrics post GC cleanup
// Note: we do not report memory metrics of isolated processes unless
- // their native allocations become more significant
- if (!Process.isIsolated() && Flags.reportPostgcMemoryMetrics() &&
- com.android.libcore.readonly.Flags.postCleanupApis()) {
+ // their native allocations become more significant. Instrumentation is
+ // also excluded because the metrics from test cases are not meaningful.
+ if (!Process.isIsolated() && ii == null && Flags.reportPostgcMemoryMetrics()
+ && com.android.libcore.readonly.Flags.postCleanupApis()) {
VMRuntime.addPostCleanupCallback(new Runnable() {
@Override public void run() {
MetricsLoggerWrapper.logPostGcMemorySnapshot();
diff --git a/core/java/android/app/AppCompatTaskInfo.java b/core/java/android/app/AppCompatTaskInfo.java
index 3fd9d8b26611..85621c9c3fab 100644
--- a/core/java/android/app/AppCompatTaskInfo.java
+++ b/core/java/android/app/AppCompatTaskInfo.java
@@ -136,7 +136,7 @@ public class AppCompatTaskInfo implements Parcelable {
private static final int FLAGS_ORGANIZER_INTERESTED = FLAG_IS_FROM_LETTERBOX_DOUBLE_TAP
| FLAG_ELIGIBLE_FOR_USER_ASPECT_RATIO_BUTTON | FLAG_FULLSCREEN_OVERRIDE_SYSTEM
| FLAG_FULLSCREEN_OVERRIDE_USER | FLAG_HAS_MIN_ASPECT_RATIO_OVERRIDE
- | FLAG_OPT_OUT_EDGE_TO_EDGE;
+ | FLAG_OPT_OUT_EDGE_TO_EDGE | FLAG_ENABLE_RESTART_MENU_FOR_DISPLAY_MOVE;
@TopActivityFlag
private static final int FLAGS_COMPAT_UI_INTERESTED = FLAGS_ORGANIZER_INTERESTED
@@ -179,7 +179,8 @@ public class AppCompatTaskInfo implements Parcelable {
*/
public boolean hasCompatUI() {
return isTopActivityInSizeCompat() || eligibleForLetterboxEducation()
- || isLetterboxDoubleTapEnabled() || eligibleForUserAspectRatioButton();
+ || isLetterboxDoubleTapEnabled() || eligibleForUserAspectRatioButton()
+ || isRestartMenuEnabledForDisplayMove();
}
/**
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 00fa1c1a4f80..bdecbaeb455d 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -302,13 +302,23 @@ public class ApplicationPackageManager extends PackageManager {
@Override
public Intent getLaunchIntentForPackage(String packageName) {
+ return getLaunchIntentForPackage(packageName, false);
+ }
+
+ @Override
+ @Nullable
+ public Intent getLaunchIntentForPackage(@NonNull String packageName,
+ boolean includeDirectBootUnaware) {
+ ResolveInfoFlags queryFlags = ResolveInfoFlags.of(
+ includeDirectBootUnaware ? MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE : 0);
+
// First see if the package has an INFO activity; the existence of
// such an activity is implied to be the desired front-door for the
// overall package (such as if it has multiple launcher entries).
Intent intentToResolve = new Intent(Intent.ACTION_MAIN);
intentToResolve.addCategory(Intent.CATEGORY_INFO);
intentToResolve.setPackage(packageName);
- List<ResolveInfo> ris = queryIntentActivities(intentToResolve, 0);
+ List<ResolveInfo> ris = queryIntentActivities(intentToResolve, queryFlags);
// Otherwise, try to find a main launcher activity.
if (ris == null || ris.size() <= 0) {
@@ -316,7 +326,7 @@ public class ApplicationPackageManager extends PackageManager {
intentToResolve.removeCategory(Intent.CATEGORY_INFO);
intentToResolve.addCategory(Intent.CATEGORY_LAUNCHER);
intentToResolve.setPackage(packageName);
- ris = queryIntentActivities(intentToResolve, 0);
+ ris = queryIntentActivities(intentToResolve, queryFlags);
}
if (ris == null || ris.size() <= 0) {
return null;
diff --git a/core/java/android/app/ApplicationStartInfo.java b/core/java/android/app/ApplicationStartInfo.java
index 2e8031dd22fe..e2af7c03b5fa 100644
--- a/core/java/android/app/ApplicationStartInfo.java
+++ b/core/java/android/app/ApplicationStartInfo.java
@@ -231,9 +231,9 @@ public final class ApplicationStartInfo implements Parcelable {
public static final int START_COMPONENT_OTHER = 5;
/**
- * @see #getMonoticCreationTimeMs
+ * @see #getMonotonicCreationTimeMs
*/
- private long mMonoticCreationTimeMs;
+ private long mMonotonicCreationTimeMs;
/**
* @see #getStartupState
@@ -545,8 +545,8 @@ public final class ApplicationStartInfo implements Parcelable {
*
* @hide
*/
- public long getMonoticCreationTimeMs() {
- return mMonoticCreationTimeMs;
+ public long getMonotonicCreationTimeMs() {
+ return mMonotonicCreationTimeMs;
}
/**
@@ -751,14 +751,14 @@ public final class ApplicationStartInfo implements Parcelable {
dest.writeParcelable(mStartIntent, flags);
dest.writeInt(mLaunchMode);
dest.writeBoolean(mWasForceStopped);
- dest.writeLong(mMonoticCreationTimeMs);
+ dest.writeLong(mMonotonicCreationTimeMs);
dest.writeInt(mStartComponent);
}
// LINT.ThenChange(:read_parcel)
/** @hide */
public ApplicationStartInfo(long monotonicCreationTimeMs) {
- mMonoticCreationTimeMs = monotonicCreationTimeMs;
+ mMonotonicCreationTimeMs = monotonicCreationTimeMs;
}
/** @hide */
@@ -776,7 +776,7 @@ public final class ApplicationStartInfo implements Parcelable {
mStartIntent = other.mStartIntent;
mLaunchMode = other.mLaunchMode;
mWasForceStopped = other.mWasForceStopped;
- mMonoticCreationTimeMs = other.mMonoticCreationTimeMs;
+ mMonotonicCreationTimeMs = other.mMonotonicCreationTimeMs;
mStartComponent = other.mStartComponent;
}
@@ -803,7 +803,7 @@ public final class ApplicationStartInfo implements Parcelable {
in.readParcelable(Intent.class.getClassLoader(), android.content.Intent.class);
mLaunchMode = in.readInt();
mWasForceStopped = in.readBoolean();
- mMonoticCreationTimeMs = in.readLong();
+ mMonotonicCreationTimeMs = in.readLong();
mStartComponent = in.readInt();
}
// LINT.ThenChange(:write_parcel)
@@ -887,7 +887,7 @@ public final class ApplicationStartInfo implements Parcelable {
}
proto.write(ApplicationStartInfoProto.LAUNCH_MODE, mLaunchMode);
proto.write(ApplicationStartInfoProto.WAS_FORCE_STOPPED, mWasForceStopped);
- proto.write(ApplicationStartInfoProto.MONOTONIC_CREATION_TIME_MS, mMonoticCreationTimeMs);
+ proto.write(ApplicationStartInfoProto.MONOTONIC_CREATION_TIME_MS, mMonotonicCreationTimeMs);
proto.write(ApplicationStartInfoProto.START_COMPONENT, mStartComponent);
proto.end(token);
}
@@ -980,7 +980,7 @@ public final class ApplicationStartInfo implements Parcelable {
ApplicationStartInfoProto.WAS_FORCE_STOPPED);
break;
case (int) ApplicationStartInfoProto.MONOTONIC_CREATION_TIME_MS:
- mMonoticCreationTimeMs = proto.readLong(
+ mMonotonicCreationTimeMs = proto.readLong(
ApplicationStartInfoProto.MONOTONIC_CREATION_TIME_MS);
break;
case (int) ApplicationStartInfoProto.START_COMPONENT:
@@ -999,7 +999,7 @@ public final class ApplicationStartInfo implements Parcelable {
sb.append(prefix)
.append("ApplicationStartInfo ").append(seqSuffix).append(':')
.append('\n')
- .append(" monotonicCreationTimeMs=").append(mMonoticCreationTimeMs)
+ .append(" monotonicCreationTimeMs=").append(mMonotonicCreationTimeMs)
.append('\n')
.append(" pid=").append(mPid)
.append(" realUid=").append(mRealUid)
@@ -1082,6 +1082,15 @@ public final class ApplicationStartInfo implements Parcelable {
final ApplicationStartInfo o = (ApplicationStartInfo) other;
+ boolean intentEquals = true;
+ if (android.content.flags.Flags.intentSaveToXmlPackage()) {
+ if (mStartIntent == null) {
+ intentEquals = o.mStartIntent == null;
+ } else {
+ intentEquals = mStartIntent.filterEquals(o.mStartIntent);
+ }
+ }
+
return mPid == o.mPid
&& mRealUid == o.mRealUid
&& mPackageUid == o.mPackageUid
@@ -1094,15 +1103,17 @@ public final class ApplicationStartInfo implements Parcelable {
&& TextUtils.equals(mProcessName, o.mProcessName)
&& timestampsEquals(o)
&& mWasForceStopped == o.mWasForceStopped
- && mMonoticCreationTimeMs == o.mMonoticCreationTimeMs
- && mStartComponent == o.mStartComponent;
+ && mMonotonicCreationTimeMs == o.mMonotonicCreationTimeMs
+ && mStartComponent == o.mStartComponent
+ && intentEquals;
}
@Override
public int hashCode() {
return Objects.hash(mPid, mRealUid, mPackageUid, mDefiningUid, mReason, mStartupState,
mStartType, mLaunchMode, mPackageName, mProcessName, mStartupTimestampsNs,
- mMonoticCreationTimeMs, mStartComponent);
+ mMonotonicCreationTimeMs, mStartComponent,
+ android.content.flags.Flags.intentSaveToXmlPackage() ? mStartIntent : null);
}
private boolean timestampsEquals(@NonNull ApplicationStartInfo other) {
diff --git a/core/java/android/app/AutomaticZenRule.aidl b/core/java/android/app/AutomaticZenRule.aidl
index feb21d657c6a..92f7d5235cf7 100644
--- a/core/java/android/app/AutomaticZenRule.aidl
+++ b/core/java/android/app/AutomaticZenRule.aidl
@@ -16,4 +16,6 @@
package android.app;
-parcelable AutomaticZenRule; \ No newline at end of file
+parcelable AutomaticZenRule;
+
+parcelable AutomaticZenRule.AzrWithId; \ No newline at end of file
diff --git a/core/java/android/app/AutomaticZenRule.java b/core/java/android/app/AutomaticZenRule.java
index fa977c93113a..1ce38ace75d8 100644
--- a/core/java/android/app/AutomaticZenRule.java
+++ b/core/java/android/app/AutomaticZenRule.java
@@ -228,7 +228,7 @@ public final class AutomaticZenRule implements Parcelable {
public AutomaticZenRule(Parcel source) {
enabled = source.readInt() == ENABLED;
if (source.readInt() == ENABLED) {
- name = getTrimmedString(source.readString());
+ name = getTrimmedString(source.readString8());
}
interruptionFilter = source.readInt();
conditionId = getTrimmedUri(source.readParcelable(null, android.net.Uri.class));
@@ -238,11 +238,11 @@ public final class AutomaticZenRule implements Parcelable {
source.readParcelable(null, android.content.ComponentName.class));
creationTime = source.readLong();
mZenPolicy = source.readParcelable(null, ZenPolicy.class);
- mPkg = source.readString();
+ mPkg = source.readString8();
mDeviceEffects = source.readParcelable(null, ZenDeviceEffects.class);
mAllowManualInvocation = source.readBoolean();
mIconResId = source.readInt();
- mTriggerDescription = getTrimmedString(source.readString(), MAX_DESC_LENGTH);
+ mTriggerDescription = getTrimmedString(source.readString8(), MAX_DESC_LENGTH);
mType = source.readInt();
}
@@ -514,7 +514,7 @@ public final class AutomaticZenRule implements Parcelable {
dest.writeInt(enabled ? ENABLED : DISABLED);
if (name != null) {
dest.writeInt(1);
- dest.writeString(name);
+ dest.writeString8(name);
} else {
dest.writeInt(0);
}
@@ -524,11 +524,11 @@ public final class AutomaticZenRule implements Parcelable {
dest.writeParcelable(configurationActivity, 0);
dest.writeLong(creationTime);
dest.writeParcelable(mZenPolicy, 0);
- dest.writeString(mPkg);
+ dest.writeString8(mPkg);
dest.writeParcelable(mDeviceEffects, 0);
dest.writeBoolean(mAllowManualInvocation);
dest.writeInt(mIconResId);
- dest.writeString(mTriggerDescription);
+ dest.writeString8(mTriggerDescription);
dest.writeInt(mType);
}
@@ -843,4 +843,41 @@ public final class AutomaticZenRule implements Parcelable {
return rule;
}
}
+
+ /** @hide */
+ public static final class AzrWithId implements Parcelable {
+ public final String mId;
+ public final AutomaticZenRule mRule;
+
+ public AzrWithId(String id, AutomaticZenRule rule) {
+ mId = id;
+ mRule = rule;
+ }
+
+ public static final Creator<AzrWithId> CREATOR = new Creator<>() {
+ @Override
+ public AzrWithId createFromParcel(Parcel in) {
+ return new AzrWithId(
+ in.readString8(),
+ in.readParcelable(AutomaticZenRule.class.getClassLoader(),
+ AutomaticZenRule.class));
+ }
+
+ @Override
+ public AzrWithId[] newArray(int size) {
+ return new AzrWithId[size];
+ }
+ };
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeString8(mId);
+ dest.writeParcelable(mRule, flags);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+ }
}
diff --git a/core/java/android/app/ForegroundServiceTypePolicy.java b/core/java/android/app/ForegroundServiceTypePolicy.java
index 6efc4ef55180..3003b79435e2 100644
--- a/core/java/android/app/ForegroundServiceTypePolicy.java
+++ b/core/java/android/app/ForegroundServiceTypePolicy.java
@@ -49,11 +49,14 @@ import android.annotation.Nullable;
import android.annotation.SuppressLint;
import android.app.compat.CompatChanges;
import android.app.role.RoleManager;
+import android.companion.virtual.VirtualDevice;
+import android.companion.virtual.VirtualDeviceManager;
import android.compat.Compatibility;
import android.compat.annotation.ChangeId;
import android.compat.annotation.Disabled;
import android.compat.annotation.EnabledAfter;
import android.compat.annotation.Overridable;
+import android.content.AttributionSource;
import android.content.Context;
import android.content.PermissionChecker;
import android.content.pm.PackageManager;
@@ -67,6 +70,7 @@ import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.UserHandle;
import android.permission.PermissionCheckerManager;
+import android.permission.PermissionManager;
import android.provider.DeviceConfig;
import android.text.TextUtils;
import android.util.ArrayMap;
@@ -1174,17 +1178,48 @@ public abstract class ForegroundServiceTypePolicy {
@PackageManager.PermissionResult
public int checkPermission(@NonNull Context context, int callerUid, int callerPid,
String packageName, boolean allowWhileInUse) {
- return checkPermission(context, mName, callerUid, callerPid, packageName,
- allowWhileInUse);
+ int permissionResult = checkPermission(context, mName, callerUid, callerPid,
+ packageName, allowWhileInUse, Context.DEVICE_ID_DEFAULT);
+
+ if (permissionResult == PERMISSION_GRANTED
+ || !PermissionManager.DEVICE_AWARE_PERMISSIONS.contains(mName)) {
+ return permissionResult;
+ }
+
+ // For device aware permissions, check if the permission is granted on any other
+ // active virtual device
+ VirtualDeviceManager vdm = context.getSystemService(VirtualDeviceManager.class);
+ if (vdm == null) {
+ return permissionResult;
+ }
+
+ final List<VirtualDevice> virtualDevices = vdm.getVirtualDevices();
+ for (int i = 0, size = virtualDevices.size(); i < size; i++) {
+ final VirtualDevice virtualDevice = virtualDevices.get(i);
+ int resolvedDeviceId = PermissionManager.resolveDeviceIdForPermissionCheck(
+ context, virtualDevice.getDeviceId(), mName);
+ // we already checked on the default device context
+ if (resolvedDeviceId == Context.DEVICE_ID_DEFAULT) {
+ continue;
+ }
+ permissionResult = checkPermission(context, mName, callerUid, callerPid,
+ packageName, allowWhileInUse, resolvedDeviceId);
+ if (permissionResult == PERMISSION_GRANTED) {
+ break;
+ }
+ }
+
+ return permissionResult;
}
@SuppressLint("AndroidFrameworkRequiresPermission")
@PackageManager.PermissionResult
int checkPermission(@NonNull Context context, @NonNull String name, int callerUid,
- int callerPid, String packageName, boolean allowWhileInUse) {
+ int callerPid, String packageName, boolean allowWhileInUse, int deviceId) {
+ final AttributionSource attributionSource = new AttributionSource(callerUid,
+ packageName, null /*attributionTag*/, deviceId);
@PermissionCheckerManager.PermissionResult final int result =
- PermissionChecker.checkPermissionForPreflight(context, name,
- callerPid, callerUid, packageName);
+ PermissionChecker.checkPermissionForPreflight(context, name, attributionSource);
if (result == PERMISSION_HARD_DENIED) {
// If the user didn't grant this permission at all.
return PERMISSION_DENIED;
@@ -1196,7 +1231,7 @@ public abstract class ForegroundServiceTypePolicy {
? PERMISSION_GRANTED : PERMISSION_DENIED;
}
final AppOpsManager appOpsManager = context.getSystemService(AppOpsManager.class);
- final int mode = appOpsManager.unsafeCheckOpRawNoThrow(opCode, callerUid, packageName);
+ final int mode = appOpsManager.unsafeCheckOpRawNoThrow(opCode, attributionSource);
switch (mode) {
case MODE_ALLOWED:
// The appop is just allowed, plain and simple.
diff --git a/core/java/android/app/IActivityTaskManager.aidl b/core/java/android/app/IActivityTaskManager.aidl
index 01b2953362b5..6c6709bb2b47 100644
--- a/core/java/android/app/IActivityTaskManager.aidl
+++ b/core/java/android/app/IActivityTaskManager.aidl
@@ -369,6 +369,11 @@ interface IActivityTaskManager {
in RemoteCallback navigationObserver, in BackAnimationAdapter adaptor);
/**
+ * Registers a callback to be invoked when the system server requests a back gesture.
+ */
+ void registerBackGestureDelegate(in RemoteCallback monitor);
+
+ /**
* registers a callback to be invoked when a background activity launch is aborted.
*
* @param observer callback to be registered.
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index 00df7246a300..1f0cd39a823c 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -224,7 +224,7 @@ interface INotificationManager
void setNotificationPolicyAccessGrantedForUser(String pkg, int userId, boolean granted);
ZenPolicy getDefaultZenPolicy();
AutomaticZenRule getAutomaticZenRule(String id);
- Map<String, AutomaticZenRule> getAutomaticZenRules();
+ ParceledListSlice getAutomaticZenRules();
String addAutomaticZenRule(in AutomaticZenRule automaticZenRule, String pkg, boolean fromUser);
boolean updateAutomaticZenRule(String id, in AutomaticZenRule automaticZenRule, boolean fromUser);
boolean removeAutomaticZenRule(String id, boolean fromUser);
diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java
index 8af5b1bd40f8..19fecb9bf7c2 100644
--- a/core/java/android/app/Instrumentation.java
+++ b/core/java/android/app/Instrumentation.java
@@ -101,14 +101,20 @@ public class Instrumentation {
*/
public static final String REPORT_KEY_STREAMRESULT = "stream";
- static final String TAG = "Instrumentation";
+ /**
+ * @hide
+ */
+ public static final String TAG = "Instrumentation";
private static final long CONNECT_TIMEOUT_MILLIS = 60_000;
private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
- // If set, will print the stack trace for activity starts within the process
- static final boolean DEBUG_START_ACTIVITY = Build.IS_DEBUGGABLE &&
+ /**
+ * If set, will print the stack trace for activity starts within the process
+ * @hide
+ */
+ public static final boolean DEBUG_START_ACTIVITY = Build.IS_DEBUGGABLE &&
SystemProperties.getBoolean("persist.wm.debug.start_activity", false);
static final boolean DEBUG_FINISH_ACTIVITY = Build.IS_DEBUGGABLE &&
SystemProperties.getBoolean("persist.wm.debug.finish_activity", false);
diff --git a/core/java/android/app/LocaleConfig.java b/core/java/android/app/LocaleConfig.java
index f56bf4d434e7..cbfd7fccb7c6 100644
--- a/core/java/android/app/LocaleConfig.java
+++ b/core/java/android/app/LocaleConfig.java
@@ -144,15 +144,12 @@ public class LocaleConfig implements Parcelable {
}
}
Resources res = context.getResources();
- //Get the resource id
int resId = context.getApplicationInfo().getLocaleConfigRes();
if (resId == 0) {
mStatus = STATUS_NOT_SPECIFIED;
return;
}
- try {
- //Get the parser to read XML data
- XmlResourceParser parser = res.getXml(resId);
+ try (XmlResourceParser parser = res.getXml(resId)) {
parseLocaleConfig(parser, res);
} catch (Resources.NotFoundException e) {
Slog.w(TAG, "The resource file pointed to by the given resource ID isn't found.");
@@ -208,22 +205,22 @@ public class LocaleConfig implements Parcelable {
String defaultLocale = null;
if (android.content.res.Flags.defaultLocale()) {
// Read the defaultLocale attribute of the LocaleConfig element
- TypedArray att = res.obtainAttributes(
- attrs, com.android.internal.R.styleable.LocaleConfig);
- defaultLocale = att.getString(
- R.styleable.LocaleConfig_defaultLocale);
- att.recycle();
+ try (TypedArray att = res.obtainAttributes(
+ attrs, com.android.internal.R.styleable.LocaleConfig)) {
+ defaultLocale = att.getString(
+ R.styleable.LocaleConfig_defaultLocale);
+ }
}
Set<String> localeNames = new HashSet<>();
while (XmlUtils.nextElementWithin(parser, outerDepth)) {
if (TAG_LOCALE.equals(parser.getName())) {
- final TypedArray attributes = res.obtainAttributes(
- attrs, com.android.internal.R.styleable.LocaleConfig_Locale);
- String nameAttr = attributes.getString(
- com.android.internal.R.styleable.LocaleConfig_Locale_name);
- localeNames.add(nameAttr);
- attributes.recycle();
+ try (TypedArray attributes = res.obtainAttributes(
+ attrs, com.android.internal.R.styleable.LocaleConfig_Locale)) {
+ String nameAttr = attributes.getString(
+ com.android.internal.R.styleable.LocaleConfig_Locale_name);
+ localeNames.add(nameAttr);
+ }
} else {
XmlUtils.skipCurrentTag(parser);
}
diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java
index 050ef23b89e3..69e3ef9086d5 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -1747,7 +1747,15 @@ public class NotificationManager {
public Map<String, AutomaticZenRule> getAutomaticZenRules() {
INotificationManager service = service();
try {
- return service.getAutomaticZenRules();
+ Map<String, AutomaticZenRule> result = new HashMap<>();
+ ParceledListSlice<AutomaticZenRule.AzrWithId> parceledRules =
+ service.getAutomaticZenRules();
+ if (parceledRules != null) {
+ for (AutomaticZenRule.AzrWithId rule : parceledRules.getList()) {
+ result.put(rule.mId, rule.mRule);
+ }
+ }
+ return result;
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/app/PictureInPictureParams.java b/core/java/android/app/PictureInPictureParams.java
index afe915eece26..dd87d288566e 100644
--- a/core/java/android/app/PictureInPictureParams.java
+++ b/core/java/android/app/PictureInPictureParams.java
@@ -594,7 +594,7 @@ public final class PictureInPictureParams implements Parcelable {
* @see PictureInPictureParams.Builder#setSeamlessResizeEnabled(boolean)
*/
public boolean isSeamlessResizeEnabled() {
- return mSeamlessResizeEnabled == null ? true : mSeamlessResizeEnabled;
+ return mSeamlessResizeEnabled == null ? false : mSeamlessResizeEnabled;
}
/**
diff --git a/core/java/android/app/PropertyInvalidatedCache.java b/core/java/android/app/PropertyInvalidatedCache.java
index 38141cf20ce3..6e495768bfd4 100644
--- a/core/java/android/app/PropertyInvalidatedCache.java
+++ b/core/java/android/app/PropertyInvalidatedCache.java
@@ -1417,7 +1417,36 @@ public class PropertyInvalidatedCache<Query, Result> {
}
/**
- * Enable or disable testing. The protocol requires that the mode toggle: for instance, it is
+ * Throw if the current process is not allowed to use test APIs.
+ */
+ @android.ravenwood.annotation.RavenwoodReplace
+ private static void throwIfNotTest() {
+ final ActivityThread activityThread = ActivityThread.currentActivityThread();
+ if (activityThread == null) {
+ // Only tests can reach here.
+ return;
+ }
+ final Instrumentation instrumentation = activityThread.getInstrumentation();
+ if (instrumentation == null) {
+ // Only tests can reach here.
+ return;
+ }
+ if (instrumentation.isInstrumenting()) {
+ return;
+ }
+ if (Flags.enforcePicTestmodeProtocol()) {
+ throw new IllegalStateException("Test-only API called not from a test.");
+ }
+ }
+
+ /**
+ * Do not throw if running under ravenwood.
+ */
+ private static void throwIfNotTest$ravenwood() {
+ }
+
+ /**
+ * Enable or disable test mode. The protocol requires that the mode toggle: for instance, it is
* illegal to clear the test mode if the test mode is already off. Enabling test mode puts
* all caches in the process into test mode; all nonces are initialized to UNSET and
* subsequent reads and writes are to process memory. This has the effect of disabling all
@@ -1425,10 +1454,12 @@ public class PropertyInvalidatedCache<Query, Result> {
* operation.
* @param mode The desired test mode.
* @throws IllegalStateException if the supplied mode is already set.
+ * @throws IllegalStateException if the process is not running an instrumentation test.
* @hide
*/
@VisibleForTesting
public static void setTestMode(boolean mode) {
+ throwIfNotTest();
synchronized (sGlobalLock) {
if (sTestMode == mode) {
final String msg = "cannot set test mode redundantly: mode=" + mode;
@@ -1464,9 +1495,11 @@ public class PropertyInvalidatedCache<Query, Result> {
* for which it would not otherwise have permission. Caches in test mode do NOT write their
* values to the system properties. The effect is local to the current process. Test mode
* must be true when this method is called.
+ * @throws IllegalStateException if the process is not running an instrumentation test.
* @hide
*/
public void testPropertyName() {
+ throwIfNotTest();
synchronized (sGlobalLock) {
if (sTestMode == false) {
throw new IllegalStateException("cannot test property name with test mode off");
@@ -1777,10 +1810,12 @@ public class PropertyInvalidatedCache<Query, Result> {
* When multiple caches share a single property value, using an instance method on one of
* the cache objects to invalidate all of the cache objects becomes confusing and you should
* just use the static version of this function.
+ * @throws IllegalStateException if the process is not running an instrumentation test.
* @hide
*/
@VisibleForTesting
public void disableSystemWide() {
+ throwIfNotTest();
disableSystemWide(mPropertyName);
}
diff --git a/core/java/android/app/StatusBarManager.java b/core/java/android/app/StatusBarManager.java
index 01868cc601fe..927d46999284 100644
--- a/core/java/android/app/StatusBarManager.java
+++ b/core/java/android/app/StatusBarManager.java
@@ -58,8 +58,10 @@ import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.statusbar.IUndoMediaTransferCallback;
import com.android.internal.statusbar.NotificationVisibility;
+import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@@ -119,6 +121,7 @@ public class StatusBarManager {
| DISABLE_SEARCH | DISABLE_ONGOING_CALL_CHIP;
/** @hide */
+ @Target(ElementType.TYPE_USE)
@IntDef(flag = true, prefix = {"DISABLE_"}, value = {
DISABLE_NONE,
DISABLE_EXPAND,
@@ -161,6 +164,7 @@ public class StatusBarManager {
| DISABLE2_NOTIFICATION_SHADE | DISABLE2_GLOBAL_ACTIONS | DISABLE2_ROTATE_SUGGESTIONS;
/** @hide */
+ @Target(ElementType.TYPE_USE)
@IntDef(flag = true, prefix = { "DISABLE2_" }, value = {
DISABLE2_NONE,
DISABLE2_MASK,
diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java
index 957482450893..e8d2e2871ef2 100644
--- a/core/java/android/app/WallpaperManager.java
+++ b/core/java/android/app/WallpaperManager.java
@@ -20,6 +20,7 @@ import static android.Manifest.permission.MANAGE_EXTERNAL_STORAGE;
import static android.Manifest.permission.READ_WALLPAPER_INTERNAL;
import static android.Manifest.permission.SET_WALLPAPER_DIM_AMOUNT;
import static android.app.Flags.FLAG_LIVE_WALLPAPER_CONTENT_HANDLING;
+import static android.app.Flags.enableConnectedDisplaysWallpaper;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.os.ParcelFileDescriptor.MODE_READ_ONLY;
@@ -248,7 +249,9 @@ public class WallpaperManager {
/**
* Command for {@link #sendWallpaperCommand}: reported by System UI when the device keyguard
* starts going away.
- * This command is triggered by {@link android.app.IActivityTaskManager#keyguardGoingAway(int)}.
+ * <p>
+ * This command is triggered by {@link android.app.IActivityTaskManager#keyguardGoingAway(int)}
+ * or by {@link android.app.IActivityTaskManager#setLockScreenShown(boolean, boolean)}.
*
* @hide
*/
@@ -256,6 +259,18 @@ public class WallpaperManager {
"android.wallpaper.keyguardgoingaway";
/**
+ * Command for {@link #sendWallpaperCommand}: reported by System UI when the device keyguard
+ * starts going away.
+ *
+ * <p>This command is triggered by
+ * {@link android.app.IActivityTaskManager#setLockScreenShown(boolean, boolean)}.
+ *
+ * @hide
+ */
+ public static final String COMMAND_KEYGUARD_APPEARING =
+ "android.wallpaper.keyguardappearing";
+
+ /**
* Command for {@link #sendWallpaperCommand}: reported by System UI when the device is going to
* sleep. The x and y arguments are a location (possibly very roughly) corresponding to the
* action that caused the device to go to sleep. For example, if the power button was pressed,
@@ -860,7 +875,13 @@ public class WallpaperManager {
return null;
}
try (InputStream is = new ParcelFileDescriptor.AutoCloseInputStream(pfd)) {
- ImageDecoder.Source src = ImageDecoder.createSource(context.getResources(), is);
+ ImageDecoder.Source src;
+ if (enableConnectedDisplaysWallpaper()) {
+ src = ImageDecoder.createSource(context.getResources(), is,
+ /* density= */ 0);
+ } else {
+ src = ImageDecoder.createSource(context.getResources(), is);
+ }
return ImageDecoder.decodeBitmap(src, ((decoder, info, source) -> {
// Mutable and hardware config can't be set at the same time.
decoder.setMutableRequired(!hardware);
diff --git a/core/java/android/app/WindowConfiguration.java b/core/java/android/app/WindowConfiguration.java
index c6d0f61b529e..8c99bd8e2ed9 100644
--- a/core/java/android/app/WindowConfiguration.java
+++ b/core/java/android/app/WindowConfiguration.java
@@ -790,12 +790,6 @@ public class WindowConfiguration implements Parcelable, Comparable<WindowConfigu
|| mWindowingMode == WINDOWING_MODE_MULTI_WINDOW;
}
- /** Returns true if the task bounds should persist across power cycles.
- * @hide */
- public boolean persistTaskBounds() {
- return mWindowingMode == WINDOWING_MODE_FREEFORM;
- }
-
/**
* Returns true if the tasks associated with this window configuration are floating.
* Floating tasks are laid out differently as they are allowed to extend past the display bounds
diff --git a/core/java/android/app/activity_manager.aconfig b/core/java/android/app/activity_manager.aconfig
index 720e045dc944..a26bfa4f586c 100644
--- a/core/java/android/app/activity_manager.aconfig
+++ b/core/java/android/app/activity_manager.aconfig
@@ -163,3 +163,37 @@ flag {
description: "Narrow the scope of Jank Perceptible"
bug: "304837972"
}
+
+flag {
+ name: "jank_perceptible_narrow_holdback"
+ namespace: "system_performance"
+ description: "Holdback study for jank_perceptible_narrow"
+ bug: "304837972"
+}
+
+flag {
+ namespace: "system_performance"
+ name: "app_start_info_cleanup_old_records"
+ description: "Cleanup old records to reduce size of in memory store."
+ bug: "384539178"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
+ namespace: "system_performance"
+ name: "app_start_info_keep_records_sorted"
+ description: "Ensure records are kept sorted to avoid extra work"
+ bug: "384539178"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
+ name: "enable_process_observer_broadcast_on_process_started"
+ namespace: "system_performance"
+ description: "Enable ProcessObserver's onProcessStarted callbacks."
+ bug: "323959187"
+}
diff --git a/core/java/android/app/admin/StringSetIntersection.java b/core/java/android/app/admin/StringSetIntersection.java
new file mode 100644
index 000000000000..5f2031e93ad9
--- /dev/null
+++ b/core/java/android/app/admin/StringSetIntersection.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2025 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 android.app.admin;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.TestApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Set;
+
+/**
+ * Class to identify a intersection resolution mechanism for {@code Set<String>} policies, it's
+ * used to resolve the enforced policy when being set by multiple admins (see {@link
+ * PolicyState#getResolutionMechanism()}).
+ *
+ * @hide
+ */
+public final class StringSetIntersection extends ResolutionMechanism<Set<String>> {
+
+ /**
+ * Intersection resolution for policies represented {@code Set<String>} which resolves as the
+ * intersection of all sets.
+ */
+ @NonNull
+ public static final StringSetIntersection STRING_SET_INTERSECTION = new StringSetIntersection();
+
+ @Override
+ public boolean equals(@Nullable Object o) {
+ if (this == o) return true;
+ return o != null && getClass() == o.getClass();
+ }
+
+ @Override
+ public int hashCode() {
+ return 0;
+ }
+
+ @Override
+ public String toString() {
+ return "StringSetIntersection {}";
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {}
+
+ @NonNull
+ public static final Parcelable.Creator<StringSetIntersection> CREATOR =
+ new Parcelable.Creator<StringSetIntersection>() {
+ @Override
+ public StringSetIntersection createFromParcel(Parcel source) {
+ return new StringSetIntersection();
+ }
+
+ @Override
+ public StringSetIntersection[] newArray(int size) {
+ return new StringSetIntersection[size];
+ }
+ };
+}
diff --git a/core/java/android/app/admin/flags/flags.aconfig b/core/java/android/app/admin/flags/flags.aconfig
index 572bffe6c6a4..b87ef7061c39 100644
--- a/core/java/android/app/admin/flags/flags.aconfig
+++ b/core/java/android/app/admin/flags/flags.aconfig
@@ -412,3 +412,13 @@ flag {
purpose: PURPOSE_BUGFIX
}
}
+
+flag {
+ name: "use_policy_intersection_for_permitted_input_methods"
+ namespace: "enterprise"
+ description: "When deciding on permitted input methods, use policy intersection instead of last recorded policy."
+ bug: "340914586"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/core/java/android/app/notification.aconfig b/core/java/android/app/notification.aconfig
index 6f0eafe487af..7eda66e22a97 100644
--- a/core/java/android/app/notification.aconfig
+++ b/core/java/android/app/notification.aconfig
@@ -287,7 +287,7 @@ flag {
name: "notif_entry_creation_time_use_elapsed_realtime"
namespace: "systemui"
description: "makes the notification entry expect its creation time to be elapsedRealtime, not uptimeMillis"
- bug: "343631648"
+ bug: "389606876"
metadata {
purpose: PURPOSE_BUGFIX
}
@@ -385,3 +385,13 @@ flag {
description: "Shows summarized notifications in the UI"
bug: "390217880"
}
+
+flag {
+ name: "nm_collapsed_lines"
+ namespace: "systemui"
+ description: "Shows 2 lines for collapsed notifications by default"
+ bug: "390217880"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/core/java/android/app/supervision/SupervisionAppService.java b/core/java/android/app/supervision/SupervisionAppService.java
index 4530be5c270a..93eb96204444 100644
--- a/core/java/android/app/supervision/SupervisionAppService.java
+++ b/core/java/android/app/supervision/SupervisionAppService.java
@@ -16,7 +16,11 @@
package android.app.supervision;
+import android.annotation.FlaggedApi;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
import android.app.Service;
+import android.app.supervision.flags.Flags;
import android.content.Intent;
import android.os.IBinder;
@@ -26,31 +30,43 @@ import android.os.IBinder;
*
* @hide
*/
+@SystemApi
+@FlaggedApi(Flags.FLAG_ENABLE_SUPERVISION_APP_SERVICE)
public class SupervisionAppService extends Service {
- private final ISupervisionAppService mBinder = new ISupervisionAppService.Stub() {
- @Override
- public void onEnabled() {
- SupervisionAppService.this.onEnabled();
- }
+ private final ISupervisionAppService mBinder =
+ new ISupervisionAppService.Stub() {
+ @Override
+ public void onEnabled() {
+ SupervisionAppService.this.onEnabled();
+ }
- @Override
- public void onDisabled() {
- SupervisionAppService.this.onDisabled();
- }
- };
+ @Override
+ public void onDisabled() {
+ SupervisionAppService.this.onDisabled();
+ }
+ };
+ @Nullable
@Override
- public final IBinder onBind(Intent intent) {
+ public final IBinder onBind(@Nullable Intent intent) {
return mBinder.asBinder();
}
/**
* Called when supervision is enabled.
+ *
+ * @hide
*/
+ @SystemApi
+ @FlaggedApi(Flags.FLAG_ENABLE_SUPERVISION_APP_SERVICE)
public void onEnabled() {}
/**
* Called when supervision is disabled.
+ *
+ * @hide
*/
+ @SystemApi
+ @FlaggedApi(Flags.FLAG_ENABLE_SUPERVISION_APP_SERVICE)
public void onDisabled() {}
}
diff --git a/core/java/android/app/wallpaper.aconfig b/core/java/android/app/wallpaper.aconfig
index 7aba172fad79..4a15a723da1a 100644
--- a/core/java/android/app/wallpaper.aconfig
+++ b/core/java/android/app/wallpaper.aconfig
@@ -24,6 +24,16 @@ flag {
}
flag {
+ name: "notify_keyguard_events"
+ namespace: "systemui"
+ description: "Send keyguard showing/hiding/going-away events to wallpaper as wallpaper commands (guarded by permission)"
+ bug: "395897130"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
name: "accurate_wallpaper_downsampling"
namespace: "systemui"
description: "Accurate downsampling of wallpaper bitmap for high resolution images"
diff --git a/core/java/android/app/wallpaper/WallpaperDescription.java b/core/java/android/app/wallpaper/WallpaperDescription.java
index a13af7f1ddcd..d7d6262599da 100644
--- a/core/java/android/app/wallpaper/WallpaperDescription.java
+++ b/core/java/android/app/wallpaper/WallpaperDescription.java
@@ -168,6 +168,12 @@ public final class WallpaperDescription implements Parcelable {
return mSampleSize;
}
+ @Override
+ public String toString() {
+ String component = (mComponent != null) ? mComponent.toString() : "{null}";
+ return component + ":" + mId;
+ }
+
////// Comparison overrides
@Override
diff --git a/core/java/android/companion/virtual/flags/flags.aconfig b/core/java/android/companion/virtual/flags/flags.aconfig
index 615a6dffdf99..161f05bc5139 100644
--- a/core/java/android/companion/virtual/flags/flags.aconfig
+++ b/core/java/android/companion/virtual/flags/flags.aconfig
@@ -166,3 +166,10 @@ flag {
bug: "393517834"
is_exported: true
}
+
+flag {
+ name: "external_virtual_cameras"
+ namespace: "virtual_devices"
+ description: "Allow external virtual cameras visible only in the Context of the virtual device"
+ bug: "375609768"
+}
diff --git a/core/java/android/companion/virtual/sensor/VirtualSensor.java b/core/java/android/companion/virtual/sensor/VirtualSensor.java
index 8d4acfcb30d7..7f228ab37f21 100644
--- a/core/java/android/companion/virtual/sensor/VirtualSensor.java
+++ b/core/java/android/companion/virtual/sensor/VirtualSensor.java
@@ -174,7 +174,7 @@ public final class VirtualSensor implements Parcelable {
@FlaggedApi(Flags.FLAG_VIRTUAL_SENSOR_ADDITIONAL_INFO)
public void sendAdditionalInfo(@NonNull VirtualSensorAdditionalInfo info) {
if (!Flags.virtualSensorAdditionalInfo()) {
- return;
+ throw new UnsupportedOperationException("Sensor additional info not supported.");
}
if ((mFlags & VirtualSensorConfig.ADDITIONAL_INFO_MASK) == 0) {
throw new UnsupportedOperationException("Sensor additional info not supported.");
diff --git a/core/java/android/companion/virtual/sensor/VirtualSensorAdditionalInfo.java b/core/java/android/companion/virtual/sensor/VirtualSensorAdditionalInfo.java
index a4fca507b1d5..0f1a2989b808 100644
--- a/core/java/android/companion/virtual/sensor/VirtualSensorAdditionalInfo.java
+++ b/core/java/android/companion/virtual/sensor/VirtualSensorAdditionalInfo.java
@@ -90,6 +90,7 @@ public final class VirtualSensorAdditionalInfo implements Parcelable {
*
* @see SensorAdditionalInfo#type
*/
+ @VirtualSensorAdditionalInfo.Type
public int getType() {
return mType;
}
@@ -114,6 +115,21 @@ public final class VirtualSensorAdditionalInfo implements Parcelable {
@NonNull
private final ArrayList<float[]> mValues = new ArrayList<>();
+ /** Payload size for {@link SensorAdditionalInfo#TYPE_SAMPLING} */
+ private static final int TYPE_SAMPLING_PLAYLOAD_SIZE = 2;
+
+ /** Payload size for {@link SensorAdditionalInfo#TYPE_UNTRACKED_DELAY} */
+ private static final int TYPE_UNTRACKED_DELAY_PAYLOAD_SIZE = 2;
+
+ /** Payload size for {@link SensorAdditionalInfo#TYPE_INTERNAL_TEMPERATURE} */
+ private static final int TYPE_INTERNAL_TEMPERATURE_PLAYLOAD_SIZE = 1;
+
+ /** Payload size for {@link SensorAdditionalInfo#TYPE_VEC3_CALIBRATION} */
+ private static final int TYPE_VEC3_CALIBRATION_PAYLOAD_SIZE = 12;
+
+ /** Payload size for {@link SensorAdditionalInfo#TYPE_SENSOR_PLACEMENT} */
+ private static final int TYPE_SENSOR_PLACEMENT_PAYLOAD_SIZE = 12;
+
/**
* Creates a new builder.
*
@@ -135,28 +151,31 @@ public final class VirtualSensorAdditionalInfo implements Parcelable {
}
/**
- * Additional info payload data represented in float values. Depending on the type of
- * information, this may be null.
+ * Additional info payload data represented in float values.
*
+ * @param values the float values of this additional info frame.
+ * @throws IllegalArgumentException if the payload size doesn't match the expectation
+ * for the given type, as documented in {@link SensorAdditionalInfo}.
* @see SensorAdditionalInfo#floatValues
*/
@NonNull
public Builder addValues(@NonNull float[] values) {
- if (values.length > 14) {
- throw new IllegalArgumentException("Maximum payload value size is 14.");
- }
if (mValues.isEmpty()) {
switch (mType) {
case SensorAdditionalInfo.TYPE_UNTRACKED_DELAY:
+ assertValuesLength(values, TYPE_UNTRACKED_DELAY_PAYLOAD_SIZE);
+ break;
case SensorAdditionalInfo.TYPE_SAMPLING:
- assertValuesLength(values, 2);
+ assertValuesLength(values, TYPE_SAMPLING_PLAYLOAD_SIZE);
break;
case SensorAdditionalInfo.TYPE_INTERNAL_TEMPERATURE:
- assertValuesLength(values, 1);
+ assertValuesLength(values, TYPE_INTERNAL_TEMPERATURE_PLAYLOAD_SIZE);
break;
case SensorAdditionalInfo.TYPE_VEC3_CALIBRATION:
+ assertValuesLength(values, TYPE_VEC3_CALIBRATION_PAYLOAD_SIZE);
+ break;
case SensorAdditionalInfo.TYPE_SENSOR_PLACEMENT:
- assertValuesLength(values, 11);
+ assertValuesLength(values, TYPE_SENSOR_PLACEMENT_PAYLOAD_SIZE);
break;
}
} else if (values.length != mValues.getFirst().length) {
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index bb62ac321202..a253613e060c 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -680,6 +680,7 @@ public class Intent implements Parcelable, Cloneable {
private static final String ATTR_COMPONENT = "component";
private static final String ATTR_DATA = "data";
private static final String ATTR_FLAGS = "flags";
+ private static final String ATTR_PACKAGE = "package";
// ---------------------------------------------------------------------
// ---------------------------------------------------------------------
@@ -12893,6 +12894,9 @@ public class Intent implements Parcelable, Cloneable {
if (mComponent != null) {
out.attribute(null, ATTR_COMPONENT, mComponent.flattenToShortString());
}
+ if (android.content.flags.Flags.intentSaveToXmlPackage() && mPackage != null) {
+ out.attribute(null, ATTR_PACKAGE, mPackage);
+ }
out.attribute(null, ATTR_FLAGS, Integer.toHexString(getFlags()));
if (mCategories != null) {
@@ -12926,6 +12930,9 @@ public class Intent implements Parcelable, Cloneable {
intent.setComponent(ComponentName.unflattenFromString(attrValue));
} else if (ATTR_FLAGS.equals(attrName)) {
intent.setFlags(Integer.parseInt(attrValue, 16));
+ } else if (android.content.flags.Flags.intentSaveToXmlPackage()
+ && ATTR_PACKAGE.equals(attrName)) {
+ intent.setPackage(attrValue);
} else {
Log.e(TAG, "restoreFromXml: unknown attribute=" + attrName);
}
diff --git a/core/java/android/content/flags/flags.aconfig b/core/java/android/content/flags/flags.aconfig
index aac04b3a9d15..148532b62c36 100644
--- a/core/java/android/content/flags/flags.aconfig
+++ b/core/java/android/content/flags/flags.aconfig
@@ -7,4 +7,14 @@ flag {
namespace: "machine_learning"
description: "This flag enables the newly added flag for binding package-private isolated processes."
bug: "312706530"
-} \ No newline at end of file
+}
+
+flag {
+ namespace: "system_performance"
+ name: "intent_save_to_xml_package"
+ description: "Add package to saveToXml so save then restore passes filterEquals."
+ bug: "369856202"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 49fd6344270e..53966b8af533 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -5964,7 +5964,39 @@ public abstract class PackageManager {
*
* @see #getLaunchIntentSenderForPackage(String)
*/
- public abstract @Nullable Intent getLaunchIntentForPackage(@NonNull String packageName);
+ public abstract @Nullable Intent getLaunchIntentForPackage(@NonNull String packageName);
+
+ /**
+ * Returns a "good" intent to launch a front-door activity in a package.
+ * This is used, for example, to implement an "open" button when browsing
+ * through packages. The current implementation looks first for a main
+ * activity in the category {@link Intent#CATEGORY_INFO}, and next for a
+ * main activity in the category {@link Intent#CATEGORY_LAUNCHER}. Returns
+ * <code>null</code> if neither are found.
+ *
+ * <p>Consider using {@link #getLaunchIntentSenderForPackage(String)} if
+ * the caller is not allowed to query for the <code>packageName</code>.
+ *
+ * @param packageName The name of the package to inspect.
+ * @param includeDirectBootUnaware When {@code true}, activities that are direct-boot-unaware
+ * will be considered even if the device hasn't been unlocked (i.e. querying will be done
+ * with {@code MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE}).
+ *
+ * @return A fully-qualified {@link Intent} that can be used to launch the
+ * main activity in the package. Returns <code>null</code> if the package
+ * does not contain such an activity, or if <em>packageName</em> is not
+ * recognized.
+ *
+ * @see #getLaunchIntentSenderForPackage(String)
+ *
+ * @hide
+ */
+ public @Nullable Intent getLaunchIntentForPackage(@NonNull String packageName,
+ boolean includeDirectBootUnaware) {
+ throw new UnsupportedOperationException(
+ "getLaunchIntentForPackage(packageName, includeDirectBootUnaware) not implemented"
+ + " in subclass");
+ }
/**
* Return a "good" intent to launch a front-door Leanback activity in a
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index b1ea6e9b68eb..219b20428d7a 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -2628,15 +2628,6 @@ public class PackageParser {
return Build.VERSION_CODES.CUR_DEVELOPMENT;
}
- // STOPSHIP: hack for the pre-release SDK
- if (platformSdkCodenames.length == 0
- && Build.VERSION.KNOWN_CODENAMES.stream().max(String::compareTo).orElse("").equals(
- targetCode)) {
- Slog.w(TAG, "Package requires development platform " + targetCode
- + ", returning current version " + Build.VERSION.SDK_INT);
- return Build.VERSION.SDK_INT;
- }
-
// Otherwise, we're looking at an incompatible pre-release SDK.
if (platformSdkCodenames.length > 0) {
outError[0] = "Requires development platform " + targetCode
@@ -2708,15 +2699,6 @@ public class PackageParser {
return Build.VERSION_CODES.CUR_DEVELOPMENT;
}
- // STOPSHIP: hack for the pre-release SDK
- if (platformSdkCodenames.length == 0
- && Build.VERSION.KNOWN_CODENAMES.stream().max(String::compareTo).orElse("").equals(
- minCode)) {
- Slog.w(TAG, "Package requires min development platform " + minCode
- + ", returning current version " + Build.VERSION.SDK_INT);
- return Build.VERSION.SDK_INT;
- }
-
// Otherwise, we're looking at an incompatible pre-release SDK.
if (platformSdkCodenames.length > 0) {
outError[0] = "Requires development platform " + minCode
diff --git a/core/java/android/content/pm/RegisteredServicesCache.java b/core/java/android/content/pm/RegisteredServicesCache.java
index ded35b23608d..1ddab2c86ec2 100644
--- a/core/java/android/content/pm/RegisteredServicesCache.java
+++ b/core/java/android/content/pm/RegisteredServicesCache.java
@@ -104,14 +104,6 @@ public abstract class RegisteredServicesCache<V> {
private final Handler mBackgroundHandler;
- private final Runnable mClearServiceInfoCachesRunnable = new Runnable() {
- public void run() {
- synchronized (mUserIdToServiceInfoCaches) {
- mUserIdToServiceInfoCaches.clear();
- }
- }
- };
-
private static class UserServices<V> {
@GuardedBy("mServicesLock")
final Map<V, Integer> persistentServices = Maps.newHashMap();
@@ -565,9 +557,11 @@ public abstract class RegisteredServicesCache<V> {
if (Flags.optimizeParsingInRegisteredServicesCache()) {
synchronized (mUserIdToServiceInfoCaches) {
- if (mUserIdToServiceInfoCaches.numMaps() > 0) {
- mBackgroundHandler.removeCallbacks(mClearServiceInfoCachesRunnable);
- mBackgroundHandler.postDelayed(mClearServiceInfoCachesRunnable,
+ if (mUserIdToServiceInfoCaches.numElementsForKey(userId) > 0) {
+ final Integer token = Integer.valueOf(userId);
+ mBackgroundHandler.removeCallbacksAndEqualMessages(token);
+ mBackgroundHandler.postDelayed(
+ new ClearServiceInfoCachesTimeoutRunnable(userId), token,
SERVICE_INFO_CACHES_TIMEOUT_MILLIS);
}
}
@@ -953,4 +947,19 @@ public abstract class RegisteredServicesCache<V> {
return BackgroundThread.getHandler();
}
}
+
+ class ClearServiceInfoCachesTimeoutRunnable implements Runnable {
+ final int mUserId;
+
+ ClearServiceInfoCachesTimeoutRunnable(int userId) {
+ this.mUserId = userId;
+ }
+
+ @Override
+ public void run() {
+ synchronized (mUserIdToServiceInfoCaches) {
+ mUserIdToServiceInfoCaches.delete(mUserId);
+ }
+ }
+ }
}
diff --git a/core/java/android/content/pm/UserInfo.java b/core/java/android/content/pm/UserInfo.java
index 53203eba4020..c5412a982110 100644
--- a/core/java/android/content/pm/UserInfo.java
+++ b/core/java/android/content/pm/UserInfo.java
@@ -494,10 +494,14 @@ public class UserInfo implements Parcelable {
// TODO(b/142482943): Make this logic more specific and customizable. (canHaveProfile(userType))
/* @hide */
public boolean canHaveProfile() {
- if (isProfile() || isGuest() || isRestricted()) {
+ if (!isFull() || isProfile() || isGuest() || isRestricted() || isDemo()) {
return false;
}
- return isMain();
+ // NOTE: profiles used to be restricted just to the system user (and later to the main
+ // user), but from the framework point of view there is no need for such restriction, hence
+ // it's lifted
+ // TODO(b/374832167): check value of config_supportProfilesOnNonMainUser
+ return isMain() || android.multiuser.Flags.profilesForAll();
}
// TODO(b/142482943): Get rid of this (after removing it from all tests) if feasible.
diff --git a/core/java/android/content/pm/multiuser.aconfig b/core/java/android/content/pm/multiuser.aconfig
index 3411a4897e83..3dbd5b239ae5 100644
--- a/core/java/android/content/pm/multiuser.aconfig
+++ b/core/java/android/content/pm/multiuser.aconfig
@@ -646,3 +646,10 @@ flag {
purpose: PURPOSE_BUGFIX
}
}
+
+flag {
+ name: "profiles_for_all"
+ namespace: "multiuser"
+ description: "Allows any regular user to have profiles"
+ bug: "374832167"
+}
diff --git a/core/java/android/content/pm/parsing/FrameworkParsingPackageUtils.java b/core/java/android/content/pm/parsing/FrameworkParsingPackageUtils.java
index c7403c0ea98c..153dd9a93490 100644
--- a/core/java/android/content/pm/parsing/FrameworkParsingPackageUtils.java
+++ b/core/java/android/content/pm/parsing/FrameworkParsingPackageUtils.java
@@ -316,15 +316,6 @@ public class FrameworkParsingPackageUtils {
return input.success(Build.VERSION_CODES.CUR_DEVELOPMENT);
}
- // STOPSHIP: hack for the pre-release SDK
- if (platformSdkCodenames.length == 0
- && Build.VERSION.KNOWN_CODENAMES.stream().max(String::compareTo).orElse("").equals(
- minCode)) {
- Slog.w(TAG, "Parsed package requires min development platform " + minCode
- + ", returning current version " + Build.VERSION.SDK_INT);
- return input.success(Build.VERSION.SDK_INT);
- }
-
// Otherwise, we're looking at an incompatible pre-release SDK.
if (platformSdkCodenames.length > 0) {
return input.error(PackageManager.INSTALL_FAILED_OLDER_SDK,
@@ -377,27 +368,19 @@ public class FrameworkParsingPackageUtils {
return input.success(targetVers);
}
- // If it's a pre-release SDK and the codename matches this platform, it
- // definitely targets this SDK.
- if (matchTargetCode(platformSdkCodenames, targetCode)) {
- return input.success(Build.VERSION_CODES.CUR_DEVELOPMENT);
- }
-
- // STOPSHIP: hack for the pre-release SDK
- if (platformSdkCodenames.length == 0
- && Build.VERSION.KNOWN_CODENAMES.stream().max(String::compareTo).orElse("").equals(
- targetCode)) {
- Slog.w(TAG, "Parsed package requires development platform " + targetCode
- + ", returning current version " + Build.VERSION.SDK_INT);
- return input.success(Build.VERSION.SDK_INT);
- }
-
try {
if (allowUnknownCodenames && UnboundedSdkLevel.isAtMost(targetCode)) {
return input.success(Build.VERSION_CODES.CUR_DEVELOPMENT);
}
} catch (IllegalArgumentException e) {
- return input.error(PackageManager.INSTALL_FAILED_OLDER_SDK, "Bad package SDK");
+ // isAtMost() throws it when encountering an older SDK codename
+ return input.error(PackageManager.INSTALL_FAILED_OLDER_SDK, e.getMessage());
+ }
+
+ // If it's a pre-release SDK and the codename matches this platform, it
+ // definitely targets this SDK.
+ if (matchTargetCode(platformSdkCodenames, targetCode)) {
+ return input.success(Build.VERSION_CODES.CUR_DEVELOPMENT);
}
// Otherwise, we're looking at an incompatible pre-release SDK.
diff --git a/core/java/android/hardware/biometrics/BiometricConstants.java b/core/java/android/hardware/biometrics/BiometricConstants.java
index a7fbce51e9df..7dc6afba3f1c 100644
--- a/core/java/android/hardware/biometrics/BiometricConstants.java
+++ b/core/java/android/hardware/biometrics/BiometricConstants.java
@@ -188,24 +188,6 @@ public interface BiometricConstants {
int BIOMETRIC_ERROR_CONTENT_VIEW_MORE_OPTIONS_BUTTON = 22;
/**
- * The error code returned after lock out error happens, the error dialog shows, and the users
- * dismisses the dialog. This is a placeholder that is currently only used by the support
- * library.
- *
- * @hide
- */
- int BIOMETRIC_ERROR_LOCKOUT_ERROR_DIALOG_DISMISSED = 23;
-
- /**
- * The error code returned after biometric hardware error happens, the error dialog shows, and
- * the users dismisses the dialog.This is a placeholder that is currently only used by the
- * support library.
- *
- * @hide
- */
- int BIOMETRIC_ERROR_BIOMETRIC_HARDWARE_ERROR_DIALOG_DISMISSED = 24;
-
- /**
* This constant is only used by SystemUI. It notifies SystemUI that authentication was paused
* because the authentication attempt was unsuccessful.
* @hide
@@ -237,8 +219,6 @@ public interface BiometricConstants {
BIOMETRIC_ERROR_IDENTITY_CHECK_NOT_ACTIVE,
BIOMETRIC_ERROR_NOT_ENABLED_FOR_APPS,
BIOMETRIC_ERROR_CONTENT_VIEW_MORE_OPTIONS_BUTTON,
- BIOMETRIC_ERROR_LOCKOUT_ERROR_DIALOG_DISMISSED,
- BIOMETRIC_ERROR_BIOMETRIC_HARDWARE_ERROR_DIALOG_DISMISSED,
BIOMETRIC_PAUSED_REJECTED})
@Retention(RetentionPolicy.SOURCE)
@interface Errors {}
diff --git a/core/java/android/hardware/biometrics/flags.aconfig b/core/java/android/hardware/biometrics/flags.aconfig
index 4815f3e4f524..061e6f44c9c7 100644
--- a/core/java/android/hardware/biometrics/flags.aconfig
+++ b/core/java/android/hardware/biometrics/flags.aconfig
@@ -64,3 +64,11 @@ flag {
purpose: PURPOSE_BUGFIX
}
}
+
+flag {
+ name: "move_fm_api_to_bm"
+ is_exported: true
+ namespace: "biometrics_framework"
+ description: "Feature flag for moving some FingerprintManager APIs to BiometricManager to unblock FM removal."
+ bug: "323957939"
+}
diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java
index 210653bb41e5..63c819eb8233 100644
--- a/core/java/android/hardware/camera2/CameraCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraCharacteristics.java
@@ -5198,12 +5198,6 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri
* </tr>
* <tr>
* <td style="text-align: center;">PRIV</td>
- * <td style="text-align: center;">S1080P</td>
- * <td style="text-align: center;">PRIV</td>
- * <td style="text-align: center;">UHD</td>
- * </tr>
- * <tr>
- * <td style="text-align: center;">PRIV</td>
* <td style="text-align: center;">S720P</td>
* <td style="text-align: center;">JPEG/JPEG_R</td>
* <td style="text-align: center;">MAXIMUM_16_9</td>
@@ -5271,28 +5265,36 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri
* </thead>
* <tbody>
* <tr>
- * <td style="text-align: center;">PRIV</td>
+ * <td style="text-align: center;">PRIV Preview</td>
* <td style="text-align: center;">S1080P</td>
- * <td style="text-align: center;">PRIV</td>
+ * <td style="text-align: center;">PRIV Video</td>
* <td style="text-align: center;">S1080P</td>
* <td style="text-align: center;"></td>
* <td style="text-align: center;"></td>
* </tr>
* <tr>
- * <td style="text-align: center;">PRIV</td>
+ * <td style="text-align: center;">PRIV Preview</td>
* <td style="text-align: center;">S1080P</td>
- * <td style="text-align: center;">PRIV</td>
+ * <td style="text-align: center;">PRIV Video</td>
* <td style="text-align: center;">S1440P</td>
* <td style="text-align: center;"></td>
* <td style="text-align: center;"></td>
* </tr>
* <tr>
- * <td style="text-align: center;">PRIV</td>
+ * <td style="text-align: center;">PRIV Preview</td>
* <td style="text-align: center;">S1080P</td>
- * <td style="text-align: center;">YUV</td>
+ * <td style="text-align: center;">PRIV Video</td>
+ * <td style="text-align: center;">UHD</td>
+ * <td style="text-align: center;"></td>
+ * <td style="text-align: center;"></td>
+ * </tr>
+ * <tr>
+ * <td style="text-align: center;">PRIV Preview</td>
* <td style="text-align: center;">S1080P</td>
+ * <td style="text-align: center;">YUV Analysis</td>
* <td style="text-align: center;">S1080P</td>
- * <td style="text-align: center;">PRIV</td>
+ * <td style="text-align: center;">PRIV Video</td>
+ * <td style="text-align: center;">1080P</td>
* </tr>
* </tbody>
* </table>
diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java
index 6e9dcf5a83a1..335448bf131e 100644
--- a/core/java/android/hardware/camera2/CameraManager.java
+++ b/core/java/android/hardware/camera2/CameraManager.java
@@ -79,6 +79,7 @@ import android.util.Log;
import android.util.Pair;
import android.util.Size;
import android.view.Display;
+import android.window.DesktopModeFlags;
import com.android.internal.camera.flags.Flags;
import com.android.internal.util.ArrayUtils;
@@ -1685,7 +1686,7 @@ public final class CameraManager {
*/
public static int getRotationOverride(@Nullable Context context,
@Nullable PackageManager packageManager, @Nullable String packageName) {
- if (com.android.window.flags.Flags.enableCameraCompatForDesktopWindowing()) {
+ if (DesktopModeFlags.ENABLE_CAMERA_COMPAT_SIMULATE_REQUESTED_ORIENTATION.isTrue()) {
return getRotationOverrideInternal(context, packageManager, packageName);
} else {
return shouldOverrideToPortrait(packageManager, packageName)
diff --git a/core/java/android/hardware/display/DisplayTopology.java b/core/java/android/hardware/display/DisplayTopology.java
index 4ed0fc056e7d..aae09e6f4c01 100644
--- a/core/java/android/hardware/display/DisplayTopology.java
+++ b/core/java/android/hardware/display/DisplayTopology.java
@@ -169,7 +169,23 @@ public final class DisplayTopology implements Parcelable {
* @hide
*/
public void addDisplay(int displayId, float width, float height) {
- addDisplay(displayId, width, height, /* shouldLog= */ true);
+ if (findDisplay(displayId, mRoot) != null) {
+ return;
+ }
+ if (mRoot == null) {
+ mRoot = new TreeNode(displayId, width, height, POSITION_LEFT, /* offset= */ 0);
+ mPrimaryDisplayId = displayId;
+ } else if (mRoot.mChildren.isEmpty()) {
+ // This is the 2nd display. Align the middles of the top and bottom edges.
+ float offset = mRoot.mWidth / 2 - width / 2;
+ TreeNode display = new TreeNode(displayId, width, height, POSITION_TOP, offset);
+ mRoot.mChildren.add(display);
+ } else {
+ TreeNode rightMostDisplay = findRightMostDisplay(mRoot, mRoot.mWidth).first;
+ TreeNode newDisplay = new TreeNode(displayId, width, height, POSITION_RIGHT,
+ /* offset= */ 0);
+ rightMostDisplay.mChildren.add(newDisplay);
+ }
}
/**
@@ -216,7 +232,7 @@ public final class DisplayTopology implements Parcelable {
while (!queue.isEmpty()) {
TreeNode node = queue.poll();
if (node.mDisplayId != displayId) {
- addDisplay(node.mDisplayId, node.mWidth, node.mHeight, /* shouldLog= */ false);
+ addDisplay(node.mDisplayId, node.mWidth, node.mHeight);
}
queue.addAll(node.mChildren);
}
@@ -227,10 +243,6 @@ public final class DisplayTopology implements Parcelable {
} else {
mPrimaryDisplayId = Display.INVALID_DISPLAY;
}
- Slog.i(TAG, "Primary display with ID " + displayId
- + " removed, new primary display: " + mPrimaryDisplayId);
- } else {
- Slog.i(TAG, "Display with ID " + displayId + " removed");
}
return true;
}
@@ -598,38 +610,6 @@ public final class DisplayTopology implements Parcelable {
return out.toString();
}
- private void addDisplay(int displayId, float width, float height, boolean shouldLog) {
- if (findDisplay(displayId, mRoot) != null) {
- throw new IllegalArgumentException(
- "DisplayTopology: attempting to add a display that already exists");
- }
- if (mRoot == null) {
- mRoot = new TreeNode(displayId, width, height, POSITION_LEFT, /* offset= */ 0);
- mPrimaryDisplayId = displayId;
- if (shouldLog) {
- Slog.i(TAG, "First display added: " + mRoot);
- }
- } else if (mRoot.mChildren.isEmpty()) {
- // This is the 2nd display. Align the middles of the top and bottom edges.
- float offset = mRoot.mWidth / 2 - width / 2;
- TreeNode display = new TreeNode(displayId, width, height, POSITION_TOP, offset);
- mRoot.mChildren.add(display);
- if (shouldLog) {
- Slog.i(TAG, "Second display added: " + display + ", parent ID: "
- + mRoot.mDisplayId);
- }
- } else {
- TreeNode rightMostDisplay = findRightMostDisplay(mRoot, mRoot.mWidth).first;
- TreeNode newDisplay = new TreeNode(displayId, width, height, POSITION_RIGHT,
- /* offset= */ 0);
- rightMostDisplay.mChildren.add(newDisplay);
- if (shouldLog) {
- Slog.i(TAG, "Display added: " + newDisplay + ", parent ID: "
- + rightMostDisplay.mDisplayId);
- }
- }
- }
-
/**
* @param display The display from which the search should start.
* @param xPos The x position of the right edge of that display.
diff --git a/core/java/android/hardware/input/IInputManager.aidl b/core/java/android/hardware/input/IInputManager.aidl
index 1c2150f3c09f..5537135f7bfa 100644
--- a/core/java/android/hardware/input/IInputManager.aidl
+++ b/core/java/android/hardware/input/IInputManager.aidl
@@ -273,7 +273,7 @@ interface IInputManager {
@PermissionManuallyEnforced
@JavaPassthrough(annotation="@android.annotation.RequiresPermission(value = "
+ "android.Manifest.permission.MANAGE_KEY_GESTURES)")
- void registerKeyGestureHandler(IKeyGestureHandler handler);
+ void registerKeyGestureHandler(in int[] keyGesturesToHandle, IKeyGestureHandler handler);
@PermissionManuallyEnforced
@JavaPassthrough(annotation="@android.annotation.RequiresPermission(value = "
diff --git a/core/java/android/hardware/input/IKeyGestureHandler.aidl b/core/java/android/hardware/input/IKeyGestureHandler.aidl
index 4da991ee85b1..08b015892710 100644
--- a/core/java/android/hardware/input/IKeyGestureHandler.aidl
+++ b/core/java/android/hardware/input/IKeyGestureHandler.aidl
@@ -20,12 +20,12 @@ import android.hardware.input.AidlKeyGestureEvent;
import android.os.IBinder;
/** @hide */
-interface IKeyGestureHandler {
+oneway interface IKeyGestureHandler {
/**
- * Called when a key gesture starts, ends, or is cancelled. If a handler returns {@code true},
- * it means they intend to handle the full gesture and should handle all the events pertaining
- * to that gesture.
+ * Called when a key gesture starts, ends, or is cancelled. It is only sent to the handler that
+ * registered the callback for that particular gesture type.
+ * {@see IInputManager#registerKeyGestureHandler(int[], IKeyGestureHandler)}
*/
- boolean handleKeyGesture(in AidlKeyGestureEvent event, in IBinder focusedToken);
+ void handleKeyGesture(in AidlKeyGestureEvent event, in IBinder focusedToken);
}
diff --git a/core/java/android/hardware/input/InputManager.java b/core/java/android/hardware/input/InputManager.java
index d6419afb2a5a..a66ac76d7597 100644
--- a/core/java/android/hardware/input/InputManager.java
+++ b/core/java/android/hardware/input/InputManager.java
@@ -1446,16 +1446,18 @@ public final class InputManager {
/**
* Registers a key gesture event handler for {@link KeyGestureEvent} handling.
*
+ * @param keyGesturesToHandle list of KeyGestureTypes to listen to
* @param handler the {@link KeyGestureEventHandler}
- * @throws IllegalArgumentException if {@code handler} has already been registered previously.
+ * @throws IllegalArgumentException if {@code handler} has already been registered previously
+ * or key gestures provided are already registered by some other gesture handler.
* @throws NullPointerException if {@code handler} or {@code executor} is null.
* @hide
* @see #unregisterKeyGestureEventHandler(KeyGestureEventHandler)
*/
@RequiresPermission(Manifest.permission.MANAGE_KEY_GESTURES)
- public void registerKeyGestureEventHandler(@NonNull KeyGestureEventHandler handler)
- throws IllegalArgumentException {
- mGlobal.registerKeyGestureEventHandler(handler);
+ public void registerKeyGestureEventHandler(List<Integer> keyGesturesToHandle,
+ @NonNull KeyGestureEventHandler handler) throws IllegalArgumentException {
+ mGlobal.registerKeyGestureEventHandler(keyGesturesToHandle, handler);
}
/**
@@ -1463,7 +1465,7 @@ public final class InputManager {
*
* @param handler the {@link KeyGestureEventHandler}
* @hide
- * @see #registerKeyGestureEventHandler(KeyGestureEventHandler)
+ * @see #registerKeyGestureEventHandler(List, KeyGestureEventHandler)
*/
@RequiresPermission(Manifest.permission.MANAGE_KEY_GESTURES)
public void unregisterKeyGestureEventHandler(@NonNull KeyGestureEventHandler handler) {
@@ -1741,7 +1743,7 @@ public final class InputManager {
* {@see KeyGestureEventListener} which is to listen to successfully handled key gestures, this
* interface allows system components to register handler for handling key gestures.
*
- * @see #registerKeyGestureEventHandler(KeyGestureEventHandler)
+ * @see #registerKeyGestureEventHandler(List, KeyGestureEventHandler)
* @see #unregisterKeyGestureEventHandler(KeyGestureEventHandler)
*
* <p> NOTE: All callbacks will occur on system main and input threads, so the caller needs
@@ -1750,14 +1752,11 @@ public final class InputManager {
*/
public interface KeyGestureEventHandler {
/**
- * Called when a key gesture event starts, is completed, or is cancelled. If a handler
- * returns {@code true}, it implies that the handler intends to handle the key gesture and
- * only this handler will receive the future events for this key gesture.
+ * Called when a key gesture event starts, is completed, or is cancelled.
*
* @param event the gesture event
*/
- boolean handleKeyGestureEvent(@NonNull KeyGestureEvent event,
- @Nullable IBinder focusedToken);
+ void handleKeyGestureEvent(@NonNull KeyGestureEvent event, @Nullable IBinder focusedToken);
}
/** @hide */
diff --git a/core/java/android/hardware/input/InputManagerGlobal.java b/core/java/android/hardware/input/InputManagerGlobal.java
index c4b4831ba76e..754182ce3d11 100644
--- a/core/java/android/hardware/input/InputManagerGlobal.java
+++ b/core/java/android/hardware/input/InputManagerGlobal.java
@@ -25,8 +25,8 @@ import android.hardware.BatteryState;
import android.hardware.SensorManager;
import android.hardware.input.InputManager.InputDeviceBatteryListener;
import android.hardware.input.InputManager.InputDeviceListener;
-import android.hardware.input.InputManager.KeyGestureEventHandler;
import android.hardware.input.InputManager.KeyEventActivityListener;
+import android.hardware.input.InputManager.KeyGestureEventHandler;
import android.hardware.input.InputManager.KeyGestureEventListener;
import android.hardware.input.InputManager.KeyboardBacklightListener;
import android.hardware.input.InputManager.OnTabletModeChangedListener;
@@ -49,6 +49,7 @@ import android.os.ServiceManager;
import android.os.VibrationEffect;
import android.os.Vibrator;
import android.os.VibratorManager;
+import android.util.IntArray;
import android.util.Log;
import android.util.SparseArray;
import android.view.Display;
@@ -132,13 +133,13 @@ public final class InputManagerGlobal {
@Nullable
private IKeyEventActivityListener mKeyEventActivityListener;
- private final Object mKeyGestureEventHandlerLock = new Object();
- @GuardedBy("mKeyGestureEventHandlerLock")
- @Nullable
- private ArrayList<KeyGestureEventHandler> mKeyGestureEventHandlers;
- @GuardedBy("mKeyGestureEventHandlerLock")
+ @GuardedBy("mKeyGesturesToHandlerMap")
@Nullable
private IKeyGestureHandler mKeyGestureHandler;
+ @GuardedBy("mKeyGesturesToHandlerMap")
+ private final SparseArray<KeyGestureEventHandler> mKeyGesturesToHandlerMap =
+ new SparseArray<>();
+
// InputDeviceSensorManager gets notified synchronously from the binder thread when input
// devices change, so it must be synchronized with the input device listeners.
@@ -1177,50 +1178,69 @@ public final class InputManagerGlobal {
private class LocalKeyGestureHandler extends IKeyGestureHandler.Stub {
@Override
- public boolean handleKeyGesture(@NonNull AidlKeyGestureEvent ev, IBinder focusedToken) {
- synchronized (mKeyGestureEventHandlerLock) {
- if (mKeyGestureEventHandlers == null) {
- return false;
- }
- final int numHandlers = mKeyGestureEventHandlers.size();
- final KeyGestureEvent event = new KeyGestureEvent(ev);
- for (int i = 0; i < numHandlers; i++) {
- KeyGestureEventHandler handler = mKeyGestureEventHandlers.get(i);
- if (handler.handleKeyGestureEvent(event, focusedToken)) {
- return true;
- }
+ public void handleKeyGesture(@NonNull AidlKeyGestureEvent ev, IBinder focusedToken) {
+ synchronized (mKeyGesturesToHandlerMap) {
+ KeyGestureEventHandler handler = mKeyGesturesToHandlerMap.get(ev.gestureType);
+ if (handler == null) {
+ Log.w(TAG, "Key gesture event " + ev.gestureType
+ + " occurred without a registered handler!");
+ return;
}
+ handler.handleKeyGestureEvent(new KeyGestureEvent(ev), focusedToken);
}
- return false;
}
}
/**
- * @see InputManager#registerKeyGestureEventHandler(KeyGestureEventHandler)
+ * @see InputManager#registerKeyGestureEventHandler(List, KeyGestureEventHandler)
*/
@RequiresPermission(Manifest.permission.MANAGE_KEY_GESTURES)
- void registerKeyGestureEventHandler(@NonNull KeyGestureEventHandler handler)
- throws IllegalArgumentException {
+ void registerKeyGestureEventHandler(List<Integer> keyGesturesToHandle,
+ @NonNull KeyGestureEventHandler handler) throws IllegalArgumentException {
+ Objects.requireNonNull(keyGesturesToHandle, "List of gestures should not be null");
Objects.requireNonNull(handler, "handler should not be null");
- synchronized (mKeyGestureEventHandlerLock) {
- if (mKeyGestureHandler == null) {
- mKeyGestureEventHandlers = new ArrayList<>();
- mKeyGestureHandler = new LocalKeyGestureHandler();
+ if (keyGesturesToHandle.isEmpty()) {
+ throw new IllegalArgumentException("No key gestures provided!");
+ }
- try {
- mIm.registerKeyGestureHandler(mKeyGestureHandler);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
+ synchronized (mKeyGesturesToHandlerMap) {
+ IntArray newKeyGestures = new IntArray(
+ keyGesturesToHandle.size() + mKeyGesturesToHandlerMap.size());
+
+ // Check if the handler already exists
+ for (int i = 0; i < mKeyGesturesToHandlerMap.size(); i++) {
+ KeyGestureEventHandler h = mKeyGesturesToHandlerMap.valueAt(i);
+ if (h == handler) {
+ throw new IllegalArgumentException("Handler has already been registered!");
}
+ newKeyGestures.add(mKeyGesturesToHandlerMap.keyAt(i));
}
- final int numHandlers = mKeyGestureEventHandlers.size();
- for (int i = 0; i < numHandlers; i++) {
- if (mKeyGestureEventHandlers.get(i) == handler) {
- throw new IllegalArgumentException("Handler has already been registered!");
+
+ // Check if any of the key gestures are already handled by existing handlers
+ for (int gesture : keyGesturesToHandle) {
+ if (mKeyGesturesToHandlerMap.contains(gesture)) {
+ throw new IllegalArgumentException("Key gesture " + gesture
+ + " is already registered by another handler!");
+ }
+ newKeyGestures.add(gesture);
+ }
+
+ try {
+ // If handler was already registered for this process, we need to unregister and
+ // re-register it for the new set of gestures
+ if (mKeyGestureHandler != null) {
+ mIm.unregisterKeyGestureHandler(mKeyGestureHandler);
+ } else {
+ mKeyGestureHandler = new LocalKeyGestureHandler();
+ }
+ mIm.registerKeyGestureHandler(newKeyGestures.toArray(), mKeyGestureHandler);
+ for (int gesture : keyGesturesToHandle) {
+ mKeyGesturesToHandlerMap.put(gesture, handler);
}
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
}
- mKeyGestureEventHandlers.add(handler);
}
}
@@ -1231,18 +1251,21 @@ public final class InputManagerGlobal {
void unregisterKeyGestureEventHandler(@NonNull KeyGestureEventHandler handler) {
Objects.requireNonNull(handler, "handler should not be null");
- synchronized (mKeyGestureEventHandlerLock) {
- if (mKeyGestureEventHandlers == null) {
+ synchronized (mKeyGesturesToHandlerMap) {
+ if (mKeyGestureHandler == null) {
return;
}
- mKeyGestureEventHandlers.removeIf(existingHandler -> existingHandler == handler);
- if (mKeyGestureEventHandlers.isEmpty()) {
+ for (int i = mKeyGesturesToHandlerMap.size() - 1; i >= 0; i--) {
+ if (mKeyGesturesToHandlerMap.valueAt(i) == handler) {
+ mKeyGesturesToHandlerMap.removeAt(i);
+ }
+ }
+ if (mKeyGesturesToHandlerMap.size() == 0) {
try {
mIm.unregisterKeyGestureHandler(mKeyGestureHandler);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
- mKeyGestureEventHandlers = null;
mKeyGestureHandler = null;
}
}
diff --git a/core/java/android/hardware/input/input_framework.aconfig b/core/java/android/hardware/input/input_framework.aconfig
index 6c2ce3685b30..c41a5ce02e61 100644
--- a/core/java/android/hardware/input/input_framework.aconfig
+++ b/core/java/android/hardware/input/input_framework.aconfig
@@ -213,3 +213,12 @@ flag {
is_fixed_read_only: true
}
+flag {
+ name: "fix_search_modifier_fallbacks"
+ namespace: "input"
+ description: "Fixes a bug in which fallbacks from Search based key combinations were not activating."
+ bug: "384113980"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/core/java/android/hardware/serial/flags/flags.aconfig b/core/java/android/hardware/serial/flags/flags.aconfig
index d8244ba55fcc..bdb8b40af2bf 100644
--- a/core/java/android/hardware/serial/flags/flags.aconfig
+++ b/core/java/android/hardware/serial/flags/flags.aconfig
@@ -1,4 +1,4 @@
-package: "android.hardware.serial"
+package: "android.hardware.serial.flags"
container: "system"
flag {
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index 2e7bc6d9b9f7..84d96bd1e155 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -3504,6 +3504,10 @@ public class InputMethodService extends AbstractInputMethodService {
mInlineSuggestionSessionController.notifyOnStartInputView();
onStartInputView(mInputEditorInfo, restarting);
startExtractingText(true);
+ // Back callback is typically registered in {@link #showWindow()}, but it's possible
+ // for {@link #doStartInput()} to be called without {@link #showWindow()} so we also
+ // register here.
+ registerDefaultOnBackInvokedCallback();
} else if (mCandidatesVisibility == View.VISIBLE) {
if (DEBUG) Log.v(TAG, "CALL: onStartCandidatesView");
mCandidatesViewStarted = true;
diff --git a/core/java/android/inputmethodservice/NavigationBarController.java b/core/java/android/inputmethodservice/NavigationBarController.java
index f420b5d7b886..7da053d0010e 100644
--- a/core/java/android/inputmethodservice/NavigationBarController.java
+++ b/core/java/android/inputmethodservice/NavigationBarController.java
@@ -236,7 +236,12 @@ final class NavigationBarController {
systemInsets.bottom, Gravity.BOTTOM));
mLastInsets = systemInsets;
} else {
- decorView.addView(mNavigationBarFrame);
+ // If systemInsets are null, the DecorView is not attached to the window yet.
+ // Use the final captionBar height as the initial one, otherwise it resolves to
+ // match parent, and can lead to full size IME insets.
+ final int height = getImeCaptionBarHeight(true /* imeDrawsImeNavBar */);
+ decorView.addView(mNavigationBarFrame, new FrameLayout.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT, height, Gravity.BOTTOM));
}
final NavigationBarView navigationBarView = mNavigationBarFrame.findViewByPredicate(
NavigationBarView.class::isInstance);
@@ -461,7 +466,7 @@ final class NavigationBarController {
final Insets systemInsets = getSystemInsets();
if (systemInsets != null) {
if (!Objects.equals(systemInsets, mLastInsets)) {
- mNavigationBarFrame.setLayoutParams(new NavigationBarFrame.LayoutParams(
+ mNavigationBarFrame.setLayoutParams(new FrameLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
systemInsets.bottom, Gravity.BOTTOM));
mLastInsets = systemInsets;
diff --git a/core/java/android/net/VpnManager.java b/core/java/android/net/VpnManager.java
index c50bc569de72..4ef293a90a80 100644
--- a/core/java/android/net/VpnManager.java
+++ b/core/java/android/net/VpnManager.java
@@ -20,6 +20,7 @@ import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
import static com.android.internal.util.Preconditions.checkNotNull;
+import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -32,6 +33,7 @@ import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
+import android.net.platform.flags.Flags;
import android.os.RemoteException;
import com.android.internal.net.LegacyVpnInfo;
@@ -85,13 +87,33 @@ public class VpnManager {
public static final int TYPE_VPN_LEGACY = 3;
/**
- * An VPN created by OEM code through other means than {@link VpnService} or {@link VpnManager}.
+ * A VPN created by OEM code through other means than {@link VpnService} or {@link VpnManager}.
* @hide
*/
@SystemApi(client = MODULE_LIBRARIES)
public static final int TYPE_VPN_OEM = 4;
/**
+ * A VPN created by OEM code using {@link VpnService}, and which OEM code desires to
+ * differentiate from other VPN types. The core networking stack will treat this VPN type
+ * similarly to {@link #TYPE_VPN_SERVICE}.
+ * @hide
+ */
+ @FlaggedApi(Flags.FLAG_VPN_TYPE_OEM_SERVICE_AND_LEGACY)
+ @SystemApi(client = MODULE_LIBRARIES)
+ public static final int TYPE_VPN_OEM_SERVICE = 5;
+
+ /**
+ * A VPN created by OEM code using the legacy VPN mechanisms, and which OEM code desires to
+ * differentiate from other VPN types. The core networking stack will treat this VPN type
+ * similarly to {@link #TYPE_VPN_LEGACY}.
+ * @hide
+ */
+ @FlaggedApi(Flags.FLAG_VPN_TYPE_OEM_SERVICE_AND_LEGACY)
+ @SystemApi(client = MODULE_LIBRARIES)
+ public static final int TYPE_VPN_OEM_LEGACY = 6;
+
+ /**
* Channel for VPN notifications.
* @hide
*/
@@ -308,7 +330,7 @@ public class VpnManager {
/** @hide */
@IntDef(value = {TYPE_VPN_NONE, TYPE_VPN_SERVICE, TYPE_VPN_PLATFORM, TYPE_VPN_LEGACY,
- TYPE_VPN_OEM})
+ TYPE_VPN_OEM, TYPE_VPN_OEM_SERVICE, TYPE_VPN_OEM_LEGACY})
@Retention(RetentionPolicy.SOURCE)
public @interface VpnType {}
diff --git a/core/java/android/net/flags.aconfig b/core/java/android/net/flags.aconfig
index 8d12b76e23ff..519729bc1c88 100644
--- a/core/java/android/net/flags.aconfig
+++ b/core/java/android/net/flags.aconfig
@@ -37,3 +37,11 @@ flag {
description: "Flag for MDNS quality, reliability and performance improvement in 25Q2"
bug: "373270045"
}
+
+flag {
+ name: "vpn_type_oem_service_and_legacy"
+ namespace: "android_core_networking"
+ is_exported: false
+ description: "Flags the TYPE_VPN_OEM_SERVICE and TYPE_VPN_OEM_LEGACY VpnManager API constants"
+ bug: "389829981"
+}
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index 71d79bb32807..cc1dee7a5747 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -1780,7 +1780,7 @@ public abstract class BatteryStats {
out.writeInt(statIrqTime);
out.writeInt(statSoftIrqTime);
out.writeInt(statIdlTime);
- out.writeString(statSubsystemPowerState);
+ out.writeString8(statSubsystemPowerState);
}
public void readFromParcel(Parcel in) {
@@ -1801,7 +1801,15 @@ public abstract class BatteryStats {
statIrqTime = in.readInt();
statSoftIrqTime = in.readInt();
statIdlTime = in.readInt();
- statSubsystemPowerState = in.readString();
+ statSubsystemPowerState = in.readString8();
+ }
+
+
+ public boolean isEmpty() {
+ return userTime == 0 && systemTime == 0 && appCpuUid1 == Process.INVALID_UID
+ && appCpuUid2 == Process.INVALID_UID && appCpuUid3 == Process.INVALID_UID
+ && statSystemTime == 0 && statIOWaitTime == 0 && statIrqTime == 0
+ && statSoftIrqTime == 0 && statIdlTime == 0 && statSubsystemPowerState == null;
}
}
@@ -2238,6 +2246,7 @@ public abstract class BatteryStats {
tagsFirstOccurrence = false;
powerStats = null;
processStateChange = null;
+ stepDetails = null;
}
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
@@ -2289,6 +2298,7 @@ public abstract class BatteryStats {
currentTime = o.currentTime;
powerStats = o.powerStats;
processStateChange = o.processStateChange;
+ stepDetails = o.stepDetails;
}
public boolean sameNonEvent(HistoryItem o) {
@@ -7318,7 +7328,8 @@ public abstract class BatteryStats {
}
item.append(", SubsystemPowerState ");
- item.append(rec.stepDetails.statSubsystemPowerState);
+ item.append(rec.stepDetails.statSubsystemPowerState != null
+ ? rec.stepDetails.statSubsystemPowerState : "Empty");
item.append("\n");
} else {
item.append(BATTERY_STATS_CHECKIN_VERSION); item.append(',');
diff --git a/core/java/android/os/Binder.java b/core/java/android/os/Binder.java
index ee62dea7f9e5..6b1e918a3c47 100644
--- a/core/java/android/os/Binder.java
+++ b/core/java/android/os/Binder.java
@@ -149,6 +149,11 @@ public class Binder implements IBinder {
private static volatile boolean sStackTrackingEnabled = false;
/**
+ * The extension binder object
+ */
+ private IBinder mExtension = null;
+
+ /**
* Enable Binder IPC stack tracking. If enabled, every binder transaction will be logged to
* {@link TransactionTracker}.
*
@@ -1237,7 +1242,9 @@ public class Binder implements IBinder {
/** @hide */
@Override
- public final native @Nullable IBinder getExtension();
+ public final @Nullable IBinder getExtension() {
+ return mExtension;
+ }
/**
* Set the binder extension.
@@ -1245,7 +1252,12 @@ public class Binder implements IBinder {
*
* @hide
*/
- public final native void setExtension(@Nullable IBinder extension);
+ public final void setExtension(@Nullable IBinder extension) {
+ mExtension = extension;
+ setExtensionNative(extension);
+ }
+
+ private final native void setExtensionNative(@Nullable IBinder extension);
/**
* Default implementation rewinds the parcels and calls onTransact. On
diff --git a/core/java/android/os/CombinedMessageQueue/MessageQueue.java b/core/java/android/os/CombinedMessageQueue/MessageQueue.java
index c3ec96d17437..c3c28b20d649 100644
--- a/core/java/android/os/CombinedMessageQueue/MessageQueue.java
+++ b/core/java/android/os/CombinedMessageQueue/MessageQueue.java
@@ -34,8 +34,6 @@ import android.util.Printer;
import android.util.SparseArray;
import android.util.proto.ProtoOutputStream;
-import com.android.internal.ravenwood.RavenwoodEnvironment;
-
import dalvik.annotation.optimization.NeverCompile;
import java.io.FileDescriptor;
@@ -144,6 +142,12 @@ public final class MessageQueue {
return;
}
+ // Holdback study.
+ if (Flags.messageQueueForceLegacy()) {
+ sIsProcessAllowedToUseConcurrent = false;
+ return;
+ }
+
if (Flags.forceConcurrentMessageQueue()) {
// b/379472827: Robolectric tests use reflection to access MessageQueue.mMessages.
// This is a hack to allow Robolectric tests to use the legacy implementation.
@@ -1115,7 +1119,6 @@ public final class MessageQueue {
msg.markInUse();
msg.arg1 = token;
- incAndTraceMessageCount(msg, when);
if (!enqueueMessageUnchecked(msg, when)) {
Log.wtf(TAG_C, "Unexpected error while adding sync barrier!");
@@ -1131,7 +1134,6 @@ public final class MessageQueue {
msg.markInUse();
msg.when = when;
msg.arg1 = token;
- incAndTraceMessageCount(msg, when);
if (Flags.messageQueueTailTracking() && mLast != null && mLast.when <= when) {
/* Message goes to tail of list */
diff --git a/core/java/android/os/IpcDataCache.java b/core/java/android/os/IpcDataCache.java
index 2e7c3be53d90..07a7f8b50f20 100644
--- a/core/java/android/os/IpcDataCache.java
+++ b/core/java/android/os/IpcDataCache.java
@@ -718,7 +718,7 @@ public class IpcDataCache<Query, Result> extends PropertyInvalidatedCache<Query,
}
/**
- * Enable or disable testing. The protocol requires that the mode toggle: for instance, it is
+ * Enable or disable test mode. The protocol requires that the mode toggle: for instance, it is
* illegal to clear the test mode if the test mode is already off. Enabling test mode puts
* all caches in the process into test mode; all nonces are initialized to UNSET and
* subsequent reads and writes are to process memory. This has the effect of disabling all
@@ -726,6 +726,7 @@ public class IpcDataCache<Query, Result> extends PropertyInvalidatedCache<Query,
* operation.
* @param mode The desired test mode.
* @throws IllegalStateException if the supplied mode is already set.
+ * @throws IllegalStateException if the process is not running an instrumentation test.
* @hide
*/
@TestApi
diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java
index 6cb49b3ea166..4a9928532b93 100644
--- a/core/java/android/os/Parcel.java
+++ b/core/java/android/os/Parcel.java
@@ -20,6 +20,7 @@ import static com.android.internal.util.Preconditions.checkArgument;
import static java.util.Objects.requireNonNull;
+import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -27,6 +28,7 @@ import android.annotation.SuppressLint;
import android.annotation.TestApi;
import android.app.AppOpsManager;
import android.compat.annotation.UnsupportedAppUsage;
+import android.os.Flags;
import android.ravenwood.annotation.RavenwoodClassLoadHook;
import android.ravenwood.annotation.RavenwoodKeepWholeClass;
import android.ravenwood.annotation.RavenwoodReplace;
@@ -837,9 +839,8 @@ public final class Parcel {
* @param buffer The ByteBuffer to write the data to.
* @throws ReadOnlyBufferException if the buffer is read-only.
* @throws BufferOverflowException if the buffer is too small.
- *
- * @hide
*/
+ @FlaggedApi(Flags.FLAG_PARCEL_MARSHALL_BYTEBUFFER)
public final void marshall(@NonNull ByteBuffer buffer) {
if (buffer == null) {
throw new NullPointerException();
@@ -875,9 +876,8 @@ public final class Parcel {
* Fills the raw bytes of this Parcel with data from the supplied buffer.
*
* @param buffer will read buffer.remaining() bytes from the buffer.
- *
- * @hide
*/
+ @FlaggedApi(Flags.FLAG_PARCEL_MARSHALL_BYTEBUFFER)
public final void unmarshall(@NonNull ByteBuffer buffer) {
if (buffer == null) {
throw new NullPointerException();
diff --git a/core/java/android/os/PerfettoTrace.java b/core/java/android/os/PerfettoTrace.java
index 932836f8a050..8b96b89c949a 100644
--- a/core/java/android/os/PerfettoTrace.java
+++ b/core/java/android/os/PerfettoTrace.java
@@ -232,7 +232,8 @@ public final class PerfettoTrace {
* @param eventName The event name to appear in the trace.
*/
public static PerfettoTrackEventExtra.Builder instant(Category category, String eventName) {
- return PerfettoTrackEventExtra.builder().init(PERFETTO_TE_TYPE_INSTANT, category)
+ return PerfettoTrackEventExtra.builder(category.isEnabled())
+ .init(PERFETTO_TE_TYPE_INSTANT, category)
.setEventName(eventName);
}
@@ -243,7 +244,8 @@ public final class PerfettoTrace {
* @param eventName The event name to appear in the trace.
*/
public static PerfettoTrackEventExtra.Builder begin(Category category, String eventName) {
- return PerfettoTrackEventExtra.builder().init(PERFETTO_TE_TYPE_SLICE_BEGIN, category)
+ return PerfettoTrackEventExtra.builder(category.isEnabled())
+ .init(PERFETTO_TE_TYPE_SLICE_BEGIN, category)
.setEventName(eventName);
}
@@ -253,7 +255,8 @@ public final class PerfettoTrace {
* @param category The perfetto category.
*/
public static PerfettoTrackEventExtra.Builder end(Category category) {
- return PerfettoTrackEventExtra.builder().init(PERFETTO_TE_TYPE_SLICE_END, category);
+ return PerfettoTrackEventExtra.builder(category.isEnabled())
+ .init(PERFETTO_TE_TYPE_SLICE_END, category);
}
/**
@@ -263,7 +266,8 @@ public final class PerfettoTrace {
* @param value The value of the counter.
*/
public static PerfettoTrackEventExtra.Builder counter(Category category, long value) {
- return PerfettoTrackEventExtra.builder().init(PERFETTO_TE_TYPE_COUNTER, category)
+ return PerfettoTrackEventExtra.builder(category.isEnabled())
+ .init(PERFETTO_TE_TYPE_COUNTER, category)
.setCounter(value);
}
@@ -286,7 +290,8 @@ public final class PerfettoTrace {
* @param value The value of the counter.
*/
public static PerfettoTrackEventExtra.Builder counter(Category category, double value) {
- return PerfettoTrackEventExtra.builder().init(PERFETTO_TE_TYPE_COUNTER, category)
+ return PerfettoTrackEventExtra.builder(category.isEnabled())
+ .init(PERFETTO_TE_TYPE_COUNTER, category)
.setCounter(value);
}
diff --git a/core/java/android/os/PerfettoTrackEventExtra.java b/core/java/android/os/PerfettoTrackEventExtra.java
index 07b44a87ef88..5fc7cf7be246 100644
--- a/core/java/android/os/PerfettoTrackEventExtra.java
+++ b/core/java/android/os/PerfettoTrackEventExtra.java
@@ -37,6 +37,7 @@ import java.util.function.Supplier;
public final class PerfettoTrackEventExtra {
private static final boolean DEBUG = false;
private static final int DEFAULT_EXTRA_CACHE_SIZE = 5;
+ private static final Builder NO_OP_BUILDER = new Builder(/* extra= */ null, /* isCategoryEnabled= */ false);
private static final ThreadLocal<PerfettoTrackEventExtra> sTrackEventExtra =
new ThreadLocal<PerfettoTrackEventExtra>() {
@Override
@@ -153,8 +154,8 @@ public final class PerfettoTrackEventExtra {
private Builder mParent;
private FieldContainer mCurrentContainer;
- private boolean mIsCategoryEnabled;
+ private final boolean mIsCategoryEnabled;
private final CounterInt64 mCounterInt64;
private final CounterDouble mCounterDouble;
private final Proto mProto;
@@ -175,24 +176,29 @@ public final class PerfettoTrackEventExtra {
private final Pool<Builder> mBuilderCache;
private Builder() {
- mExtra = sTrackEventExtra.get();
- mNamedTrackCache = mExtra.mNamedTrackCache;
- mCounterTrackCache = mExtra.mCounterTrackCache;
- mArgInt64Cache = mExtra.mArgInt64Cache;
- mArgDoubleCache = mExtra.mArgDoubleCache;
- mArgBoolCache = mExtra.mArgBoolCache;
- mArgStringCache = mExtra.mArgStringCache;
- mFieldInt64Cache = mExtra.mFieldInt64Cache;
- mFieldDoubleCache = mExtra.mFieldDoubleCache;
- mFieldStringCache = mExtra.mFieldStringCache;
- mFieldNestedCache = mExtra.mFieldNestedCache;
- mBuilderCache = mExtra.mBuilderCache;
-
- mCounterInt64 = mExtra.getCounterInt64();
- mCounterDouble = mExtra.getCounterDouble();
- mProto = mExtra.getProto();
- mFlow = mExtra.getFlow();
- mTerminatingFlow = mExtra.getTerminatingFlow();
+ this(sTrackEventExtra.get(), true);
+ }
+
+ public Builder(PerfettoTrackEventExtra extra, boolean isCategoryEnabled) {
+ mIsCategoryEnabled = isCategoryEnabled;
+ mExtra = extra;
+ mNamedTrackCache = mExtra == null ? null : mExtra.mNamedTrackCache;
+ mCounterTrackCache = mExtra == null ? null : mExtra.mCounterTrackCache;
+ mArgInt64Cache = mExtra == null ? null : mExtra.mArgInt64Cache;
+ mArgDoubleCache = mExtra == null ? null : mExtra.mArgDoubleCache;
+ mArgBoolCache = mExtra == null ? null : mExtra.mArgBoolCache;
+ mArgStringCache = mExtra == null ? null : mExtra.mArgStringCache;
+ mFieldInt64Cache = mExtra == null ? null : mExtra.mFieldInt64Cache;
+ mFieldDoubleCache = mExtra == null ? null : mExtra.mFieldDoubleCache;
+ mFieldStringCache = mExtra == null ? null : mExtra.mFieldStringCache;
+ mFieldNestedCache = mExtra == null ? null : mExtra.mFieldNestedCache;
+ mBuilderCache = mExtra == null ? null : mExtra.mBuilderCache;
+
+ mCounterInt64 = mExtra == null ? null : mExtra.getCounterInt64();
+ mCounterDouble = mExtra == null ? null : mExtra.getCounterDouble();
+ mProto = mExtra == null ? null : mExtra.getProto();
+ mFlow = mExtra == null ? null : mExtra.getFlow();
+ mTerminatingFlow = mExtra == null ? null : mExtra.getTerminatingFlow();
}
/**
@@ -214,6 +220,10 @@ public final class PerfettoTrackEventExtra {
* Initialize the builder for a new trace event.
*/
public Builder init(int traceType, PerfettoTrace.Category category) {
+ if (!mIsCategoryEnabled) {
+ return this;
+ }
+
mTraceType = traceType;
mCategory = category;
mEventName = "";
@@ -225,7 +235,7 @@ public final class PerfettoTrackEventExtra {
mExtra.reset();
// Reset after on init in case the thread created builders without calling emit
- return initInternal(this, null, category.isEnabled());
+ return initInternal(this, null);
}
/**
@@ -529,7 +539,7 @@ public final class PerfettoTrackEventExtra {
}
mProto.clearFields();
mExtra.addPerfettoPointer(mProto);
- return mBuilderCache.get(sBuilderSupplier).initInternal(this, mProto, true);
+ return mBuilderCache.get(sBuilderSupplier).initInternal(this, mProto);
}
/**
@@ -560,7 +570,7 @@ public final class PerfettoTrackEventExtra {
FieldNested field = mFieldNestedCache.get(sFieldNestedSupplier);
field.setId(id);
mExtra.addPerfettoPointer(mCurrentContainer, field);
- return mBuilderCache.get(sBuilderSupplier).initInternal(this, field, true);
+ return mBuilderCache.get(sBuilderSupplier).initInternal(this, field);
}
/**
@@ -577,11 +587,9 @@ public final class PerfettoTrackEventExtra {
}
- private Builder initInternal(Builder parent, FieldContainer field,
- boolean isCategoryEnabled) {
+ private Builder initInternal(Builder parent, FieldContainer field) {
mParent = parent;
mCurrentContainer = field;
- mIsCategoryEnabled = isCategoryEnabled;
mIsBuilt = false;
return this;
@@ -613,9 +621,12 @@ public final class PerfettoTrackEventExtra {
/**
* Start a {@link Builder} to build a {@link PerfettoTrackEventExtra}.
*/
- public static Builder builder() {
- return sTrackEventExtra.get().mBuilderCache.get(sBuilderSupplier).initInternal(null, null,
- false);
+ public static Builder builder(boolean isCategoryEnabled) {
+ if (isCategoryEnabled) {
+ return sTrackEventExtra.get().mBuilderCache.get(sBuilderSupplier)
+ .initInternal(null, null);
+ }
+ return NO_OP_BUILDER;
}
private final RingBuffer<NamedTrack> mNamedTrackCache =
diff --git a/core/java/android/os/flags.aconfig b/core/java/android/os/flags.aconfig
index 86acb2b21cfa..14c154d2e3a9 100644
--- a/core/java/android/os/flags.aconfig
+++ b/core/java/android/os/flags.aconfig
@@ -4,6 +4,25 @@ container: "system"
# keep-sorted start block=yes newline_separated=yes
flag {
+ # Holdback study for concurrent MessageQueue.
+ # Do not promote beyond trunkfood.
+ namespace: "system_performance"
+ name: "message_queue_force_legacy"
+ description: "Whether to holdback concurrent MessageQueue (force legacy)."
+ bug: "336880969"
+}
+
+flag {
+ name: "adpf_25q2_metrics"
+ namespace: "game"
+ description: "Add missing metrics for ADPF 25Q2 features."
+ bug: "367803904"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
name: "adpf_gpu_report_actual_work_duration"
is_exported: true
namespace: "game"
@@ -354,6 +373,15 @@ flag {
flag {
namespace: "system_performance"
+ name: "parcel_marshall_bytebuffer"
+ is_exported: true
+ description: "Parcel marshal/unmarshall APIs that use ByteBuffer."
+ is_fixed_read_only: true
+ bug: "401362825"
+}
+
+flag {
+ namespace: "system_performance"
name: "perfetto_sdk_tracing"
description: "Tracing using Perfetto SDK."
bug: "303199244"
diff --git a/core/java/android/os/image/flags/trade_in_mode_flags.aconfig b/core/java/android/os/image/flags/trade_in_mode_flags.aconfig
index e2e56ef70d62..c1adfe50db9e 100644
--- a/core/java/android/os/image/flags/trade_in_mode_flags.aconfig
+++ b/core/java/android/os/image/flags/trade_in_mode_flags.aconfig
@@ -9,3 +9,12 @@ flag {
bug: "332683751"
is_fixed_read_only: true
}
+
+flag {
+ name: "trade_in_mode_2025q4"
+ is_exported: true
+ namespace: "phoenix"
+ description: "Enable Trade-in Mode 2025Q4 changes"
+ bug: "397154502"
+ is_fixed_read_only: true
+}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index b7e296228e45..e7bca1419418 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -13317,10 +13317,18 @@ public final class Settings {
public static final String CONTEXTUAL_SEARCH_PACKAGE = "contextual_search_package";
/**
- * Inetger property which determines whether advanced protection is on or not.
+ * Integer property which determines whether advanced protection is on or not.
* @hide
*/
public static final String ADVANCED_PROTECTION_MODE = "advanced_protection_mode";
+
+ /**
+ * Integer property which determines whether advanced protection USB data protection
+ * feature is on or not.
+ *
+ * @hide
+ */
+ public static final String AAPM_USB_DATA_PROTECTION = "aapm_usb_data_protection";
}
/**
diff --git a/core/java/android/security/advancedprotection/AdvancedProtectionManager.java b/core/java/android/security/advancedprotection/AdvancedProtectionManager.java
index 62b2bcf32442..cb2b13d1e120 100644
--- a/core/java/android/security/advancedprotection/AdvancedProtectionManager.java
+++ b/core/java/android/security/advancedprotection/AdvancedProtectionManager.java
@@ -349,7 +349,8 @@ public final class AdvancedProtectionManager {
*
* @param featureId The feature identifier.
* @param type The type of the feature describing the action that needs to be explained
- * in the dialog or null for default explanation.
+ * in the dialog or {@link #SUPPORT_DIALOG_TYPE_UNKNOWN} for default
+ * explanation.
* @return Intent An intent to be used to start the dialog-activity that explains a feature was
* disabled by advanced protection.
* @hide
@@ -373,7 +374,27 @@ public final class AdvancedProtectionManager {
return intent;
}
- /** @hide */
+ /**
+ * Called by a feature to display a support dialog when a feature was disabled by advanced
+ * protection based on a policy identifier or restriction. This returns an intent that can be
+ * used with {@link Context#startActivity(Intent)} to display the dialog.
+ *
+ * <p>At the moment, if the dialog is for {@link #FEATURE_ID_DISALLOW_CELLULAR_2G} or
+ * {@link #FEATURE_ID_ENABLE_MTE} and the provided type is
+ * {@link #SUPPORT_DIALOG_TYPE_UNKNOWN}, the type will be changed to
+ * {@link #SUPPORT_DIALOG_TYPE_DISABLED_SETTING} in the returned intent, as these features only
+ * have a disabled setting UI.
+ *
+ * <p>Note that this method doesn't check if the feature is actually disabled, i.e. this method
+ * will always return an intent.
+ *
+ * @param identifier The policy identifier or restriction.
+ * @param type The type of the feature describing the action that needs to be explained
+ * in the dialog or {@link #SUPPORT_DIALOG_TYPE_UNKNOWN} for default
+ * explanation.
+ * @return Intent An intent to be used to start the dialog-activity that explains a feature was
+ * disabled by advanced protection.
+ * @hide */
public static @NonNull Intent createSupportIntentForPolicyIdentifierOrRestriction(
@NonNull String identifier, @SupportDialogType int type) {
Objects.requireNonNull(identifier);
@@ -382,16 +403,21 @@ public final class AdvancedProtectionManager {
+ " SUPPORT_DIALOG_TYPE_* APIs.");
}
final int featureId;
+ int dialogType = type;
if (DISALLOW_INSTALL_UNKNOWN_SOURCES_GLOBALLY.equals(identifier)) {
featureId = FEATURE_ID_DISALLOW_INSTALL_UNKNOWN_SOURCES;
} else if (DISALLOW_CELLULAR_2G.equals(identifier)) {
featureId = FEATURE_ID_DISALLOW_CELLULAR_2G;
+ dialogType = (dialogType == SUPPORT_DIALOG_TYPE_UNKNOWN)
+ ? SUPPORT_DIALOG_TYPE_DISABLED_SETTING : dialogType;
} else if (MEMORY_TAGGING_POLICY.equals(identifier)) {
featureId = FEATURE_ID_ENABLE_MTE;
+ dialogType = (dialogType == SUPPORT_DIALOG_TYPE_UNKNOWN)
+ ? SUPPORT_DIALOG_TYPE_DISABLED_SETTING : dialogType;
} else {
throw new UnsupportedOperationException("Unsupported identifier: " + identifier);
}
- return createSupportIntent(featureId, type);
+ return createSupportIntent(featureId, dialogType);
}
/** @hide */
diff --git a/core/java/android/security/flags.aconfig b/core/java/android/security/flags.aconfig
index 9fd4618bc5da..0a922d61a786 100644
--- a/core/java/android/security/flags.aconfig
+++ b/core/java/android/security/flags.aconfig
@@ -97,10 +97,13 @@ flag {
}
flag {
- name: "clear_strong_auth_on_add_primary_credential"
+ name: "clear_strong_auth_on_adding_primary_credential"
namespace: "biometrics"
- description: "Clear StrongAuth on add credential"
+ description: "Clear StrongAuth on adding credential"
bug: "320817991"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
}
flag {
diff --git a/core/java/android/service/dreams/flags.aconfig b/core/java/android/service/dreams/flags.aconfig
index d3a230d1335d..971942ecfe8b 100644
--- a/core/java/android/service/dreams/flags.aconfig
+++ b/core/java/android/service/dreams/flags.aconfig
@@ -84,3 +84,11 @@ flag {
description: "Allow dreaming when device is stationary and upright"
bug: "383208131"
}
+
+flag {
+ name: "dreams_v2"
+ namespace: "systemui"
+ description: "Enables various improvements to the dream experience "
+ "such as new triggers and various bug fixes"
+ bug: "375689917"
+}
diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java
index 4cbd5beb3a8c..fce2df185461 100644
--- a/core/java/android/service/notification/ZenModeConfig.java
+++ b/core/java/android/service/notification/ZenModeConfig.java
@@ -62,6 +62,7 @@ import android.content.ComponentName;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
+import android.content.pm.ParceledListSlice;
import android.content.res.Resources;
import android.net.Uri;
import android.os.Build;
@@ -460,14 +461,21 @@ public class ZenModeConfig implements Parcelable {
}
private static void readRulesFromParcel(ArrayMap<String, ZenRule> ruleMap, Parcel source) {
- final int len = source.readInt();
+ int len = source.readInt();
if (len > 0) {
final String[] ids = new String[len];
- final ZenRule[] rules = new ZenRule[len];
- source.readStringArray(ids);
- source.readTypedArray(rules, ZenRule.CREATOR);
+ source.readString8Array(ids);
+ ParceledListSlice<?> parceledRules = source.readParcelable(
+ ZenRule.class.getClassLoader(), ParceledListSlice.class);
+ List<?> rules = parceledRules != null ? parceledRules.getList() : new ArrayList<>();
+ if (rules.size() != len) {
+ Slog.wtf(TAG, String.format(
+ "Unexpected parceled rules count (%s != %s), throwing them out",
+ rules.size(), len));
+ len = 0;
+ }
for (int i = 0; i < len; i++) {
- ruleMap.put(ids[i], rules[i]);
+ ruleMap.put(ids[i], (ZenRule) rules.get(i));
}
}
}
@@ -485,8 +493,8 @@ public class ZenModeConfig implements Parcelable {
}
dest.writeInt(user);
dest.writeParcelable(manualRule, 0);
- writeRulesToParcel(automaticRules, dest);
- writeRulesToParcel(deletedRules, dest);
+ writeRulesToParcel(automaticRules, dest, flags);
+ writeRulesToParcel(deletedRules, dest, flags);
if (!Flags.modesUi()) {
dest.writeInt(allowAlarms ? 1 : 0);
dest.writeInt(allowMedia ? 1 : 0);
@@ -501,18 +509,19 @@ public class ZenModeConfig implements Parcelable {
}
}
- private static void writeRulesToParcel(ArrayMap<String, ZenRule> ruleMap, Parcel dest) {
+ private static void writeRulesToParcel(ArrayMap<String, ZenRule> ruleMap, Parcel dest,
+ int flags) {
if (!ruleMap.isEmpty()) {
final int len = ruleMap.size();
final String[] ids = new String[len];
- final ZenRule[] rules = new ZenRule[len];
+ final ArrayList<ZenRule> rules = new ArrayList<>();
for (int i = 0; i < len; i++) {
ids[i] = ruleMap.keyAt(i);
- rules[i] = ruleMap.valueAt(i);
+ rules.add(ruleMap.valueAt(i));
}
dest.writeInt(len);
- dest.writeStringArray(ids);
- dest.writeTypedArray(rules, 0);
+ dest.writeString8Array(ids);
+ dest.writeParcelable(new ParceledListSlice<>(rules), flags);
} else {
dest.writeInt(0);
}
@@ -2636,7 +2645,7 @@ public class ZenModeConfig implements Parcelable {
enabled = source.readInt() == 1;
snoozing = source.readInt() == 1;
if (source.readInt() == 1) {
- name = source.readString();
+ name = source.readString8();
}
zenMode = source.readInt();
conditionId = source.readParcelable(null, android.net.Uri.class);
@@ -2644,18 +2653,18 @@ public class ZenModeConfig implements Parcelable {
component = source.readParcelable(null, android.content.ComponentName.class);
configurationActivity = source.readParcelable(null, android.content.ComponentName.class);
if (source.readInt() == 1) {
- id = source.readString();
+ id = source.readString8();
}
creationTime = source.readLong();
if (source.readInt() == 1) {
- enabler = source.readString();
+ enabler = source.readString8();
}
zenPolicy = source.readParcelable(null, android.service.notification.ZenPolicy.class);
zenDeviceEffects = source.readParcelable(null, ZenDeviceEffects.class);
- pkg = source.readString();
+ pkg = source.readString8();
allowManualInvocation = source.readBoolean();
- iconResName = source.readString();
- triggerDescription = source.readString();
+ iconResName = source.readString8();
+ triggerDescription = source.readString8();
type = source.readInt();
userModifiedFields = source.readInt();
zenPolicyUserModifiedFields = source.readInt();
@@ -2703,7 +2712,7 @@ public class ZenModeConfig implements Parcelable {
dest.writeInt(snoozing ? 1 : 0);
if (name != null) {
dest.writeInt(1);
- dest.writeString(name);
+ dest.writeString8(name);
} else {
dest.writeInt(0);
}
@@ -2714,23 +2723,23 @@ public class ZenModeConfig implements Parcelable {
dest.writeParcelable(configurationActivity, 0);
if (id != null) {
dest.writeInt(1);
- dest.writeString(id);
+ dest.writeString8(id);
} else {
dest.writeInt(0);
}
dest.writeLong(creationTime);
if (enabler != null) {
dest.writeInt(1);
- dest.writeString(enabler);
+ dest.writeString8(enabler);
} else {
dest.writeInt(0);
}
dest.writeParcelable(zenPolicy, 0);
dest.writeParcelable(zenDeviceEffects, 0);
- dest.writeString(pkg);
+ dest.writeString8(pkg);
dest.writeBoolean(allowManualInvocation);
- dest.writeString(iconResName);
- dest.writeString(triggerDescription);
+ dest.writeString8(iconResName);
+ dest.writeString8(triggerDescription);
dest.writeInt(type);
dest.writeInt(userModifiedFields);
dest.writeInt(zenPolicyUserModifiedFields);
diff --git a/core/java/android/util/ArrayMap.java b/core/java/android/util/ArrayMap.java
index 7ee0ff15c5ad..c59907937d6a 100644
--- a/core/java/android/util/ArrayMap.java
+++ b/core/java/android/util/ArrayMap.java
@@ -129,7 +129,7 @@ public final class ArrayMap<K, V> implements Map<K, V> {
return ContainerHelpers.binarySearch(hashes, N, hash);
} catch (ArrayIndexOutOfBoundsException e) {
if (CONCURRENT_MODIFICATION_EXCEPTIONS) {
- throw new ConcurrentModificationException();
+ throw new ConcurrentModificationException(e);
} else {
throw e; // the cache is poisoned at this point, there's not much we can do
}
diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java
index 6b7b81887706..4c578fb93600 100644
--- a/core/java/android/view/InsetsController.java
+++ b/core/java/android/view/InsetsController.java
@@ -1030,10 +1030,18 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
handlePendingControlRequest(statsToken);
} else {
if (showTypes[0] != 0) {
+ if ((showTypes[0] & ime()) != 0) {
+ ImeTracker.forLogging().onProgress(statsToken,
+ ImeTracker.PHASE_CLIENT_ON_CONTROLS_CHANGED);
+ }
applyAnimation(showTypes[0], true /* show */, false /* fromIme */,
false /* skipsCallbacks */, statsToken);
}
if (hideTypes[0] != 0) {
+ if ((hideTypes[0] & ime()) != 0) {
+ ImeTracker.forLogging().onProgress(statsToken,
+ ImeTracker.PHASE_CLIENT_ON_CONTROLS_CHANGED);
+ }
applyAnimation(hideTypes[0], false /* show */, false /* fromIme */,
// The animation of hiding transient types shouldn't be detected by the
// app. Otherwise, it might be able to react to the callbacks and cause
@@ -1041,6 +1049,10 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
(hideTypes[0] & ~transientTypes[0]) == 0 /* skipsCallbacks */,
statsToken);
}
+ if ((showTypes[0] & ime()) == 0 && (hideTypes[0] & ime()) == 0) {
+ ImeTracker.forLogging().onCancelled(statsToken,
+ ImeTracker.PHASE_CLIENT_ON_CONTROLS_CHANGED);
+ }
}
} else {
if (showTypes[0] != 0) {
@@ -2027,8 +2039,8 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation
} else if (Flags.refactorInsetsController()) {
if ((typesToReport & ime()) != 0 && mImeSourceConsumer != null) {
InsetsSourceControl control = mImeSourceConsumer.getControl();
- if (control != null && control.getLeash() == null) {
- // If the IME was requested twice, and we didn't receive the controls
+ if (control == null || control.getLeash() == null) {
+ // If the IME was requested to show twice, and we didn't receive the controls
// yet, this request will not continue. It should be cancelled here, as
// it would time out otherwise.
ImeTracker.forLogging().onCancelled(statsToken,
diff --git a/core/java/android/view/OrientationEventListener.java b/core/java/android/view/OrientationEventListener.java
index 2feb44ffc3ae..892b80bd0213 100644
--- a/core/java/android/view/OrientationEventListener.java
+++ b/core/java/android/view/OrientationEventListener.java
@@ -26,8 +26,7 @@ import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.util.Log;
-
-import com.android.window.flags.Flags;
+import android.window.DesktopModeFlags;
/**
* Helper class for receiving notifications from the SensorManager when
@@ -77,9 +76,10 @@ public abstract class OrientationEventListener {
mSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
if (mSensor != null) {
// Create listener only if sensors do exist.
- mSensorEventListener = Flags.enableCameraCompatForDesktopWindowing()
- ? new CompatSensorEventListenerImpl(new SensorEventListenerImpl())
- : new SensorEventListenerImpl();
+ mSensorEventListener =
+ DesktopModeFlags.ENABLE_CAMERA_COMPAT_SIMULATE_REQUESTED_ORIENTATION.isTrue()
+ ? new CompatSensorEventListenerImpl(new SensorEventListenerImpl())
+ : new SensorEventListenerImpl();
}
}
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index c4347f05f4a3..c7ae3283c46c 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -28,6 +28,7 @@ import static android.view.SurfaceControlProto.NAME;
import android.Manifest;
import android.annotation.CallbackExecutor;
+import android.annotation.DurationNanosLong;
import android.annotation.FlaggedApi;
import android.annotation.FloatRange;
import android.annotation.IntDef;
@@ -469,9 +470,9 @@ public final class SurfaceControl implements Parcelable {
private final long mFrameVsyncId;
private final @JankType int mJankType;
- private final long mFrameIntervalNs;
- private final long mScheduledAppFrameTimeNs;
- private final long mActualAppFrameTimeNs;
+ private final @DurationNanosLong long mFrameIntervalNs;
+ private final @DurationNanosLong long mScheduledAppFrameTimeNs;
+ private final @DurationNanosLong long mActualAppFrameTimeNs;
/**
* @hide
@@ -512,7 +513,7 @@ public final class SurfaceControl implements Parcelable {
* @return the frame interval in ns
* @hide
*/
- public long getFrameIntervalNanos() {
+ public @DurationNanosLong long getFrameIntervalNanos() {
return mFrameIntervalNs;
}
@@ -525,7 +526,7 @@ public final class SurfaceControl implements Parcelable {
*
* @return scheduled app time in ns
*/
- public long getScheduledAppFrameTimeNanos() {
+ public @DurationNanosLong long getScheduledAppFrameTimeNanos() {
return mScheduledAppFrameTimeNs;
}
@@ -534,7 +535,7 @@ public final class SurfaceControl implements Parcelable {
*
* @return the actual app time in ns
*/
- public long getActualAppFrameTimeNanos() {
+ public @DurationNanosLong long getActualAppFrameTimeNanos() {
return mActualAppFrameTimeNs;
}
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index 1b57b0045537..94e9aa709369 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -1070,9 +1070,9 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
}
if (mSurfacePackage != null) {
- mSurfaceControlViewHostParent.detach();
mEmbeddedWindowParams.clear();
if (releaseSurfacePackage) {
+ mSurfaceControlViewHostParent.detach();
mSurfacePackage.release();
mSurfacePackage = null;
}
diff --git a/core/java/android/view/ViewTreeObserver.java b/core/java/android/view/ViewTreeObserver.java
index 3b444c44c368..fc66e49ff6b0 100644
--- a/core/java/android/view/ViewTreeObserver.java
+++ b/core/java/android/view/ViewTreeObserver.java
@@ -16,6 +16,9 @@
package android.view;
+import static android.view.flags.Flags.FLAG_ENABLE_DISPATCH_ON_SCROLL_CHANGED;
+
+import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
@@ -1258,8 +1261,9 @@ public final class ViewTreeObserver {
/**
* Notifies registered listeners that something has scrolled.
*/
+ @FlaggedApi(FLAG_ENABLE_DISPATCH_ON_SCROLL_CHANGED)
@UnsupportedAppUsage
- final void dispatchOnScrollChanged() {
+ public final void dispatchOnScrollChanged() {
// NOTE: because of the use of CopyOnWriteArrayList, we *must* use an iterator to
// perform the dispatching. The iterator is a safe guard against listeners that
// could mutate the list by calling the various add/remove methods. This prevents
diff --git a/core/java/android/view/WindowManagerGlobal.java b/core/java/android/view/WindowManagerGlobal.java
index 624216776f42..b97f28da7559 100644
--- a/core/java/android/view/WindowManagerGlobal.java
+++ b/core/java/android/view/WindowManagerGlobal.java
@@ -329,13 +329,17 @@ public final class WindowManagerGlobal {
/**
* Adds a listener that will be notified whenever {@link #getWindowViews()} changes. The
- * current value is provided immediately. If it was registered previously then this is ano op.
+ * current value is provided immediately using the provided {@link Executor}. If this
+ * {@link Consumer} was registered previously, then this is a no op.
*/
public void addWindowViewsListener(@NonNull Executor executor,
@NonNull Consumer<List<View>> consumer) {
synchronized (mLock) {
+ if (mWindowViewsListenerGroup.isConsumerPresent(consumer)) {
+ return;
+ }
mWindowViewsListenerGroup.addListener(executor, consumer);
- mWindowViewsListenerGroup.accept(getWindowViews());
+ executor.execute(() -> consumer.accept(getWindowViews()));
}
}
diff --git a/core/java/android/view/WindowManagerImpl.java b/core/java/android/view/WindowManagerImpl.java
index 97cf8fc748e8..8944c3fa0b19 100644
--- a/core/java/android/view/WindowManagerImpl.java
+++ b/core/java/android/view/WindowManagerImpl.java
@@ -120,7 +120,7 @@ public class WindowManagerImpl implements WindowManager {
this(context, null /* parentWindow */, null /* clientToken */);
}
- private WindowManagerImpl(Context context, Window parentWindow,
+ public WindowManagerImpl(Context context, Window parentWindow,
@Nullable IBinder windowContextToken) {
mContext = context;
mParentWindow = parentWindow;
diff --git a/core/java/android/view/flags/view_tree_observer_flags.aconfig b/core/java/android/view/flags/view_tree_observer_flags.aconfig
new file mode 100644
index 000000000000..82f3300a87cb
--- /dev/null
+++ b/core/java/android/view/flags/view_tree_observer_flags.aconfig
@@ -0,0 +1,9 @@
+package: "android.view.flags"
+container: "system"
+
+flag {
+ name: "enable_dispatch_on_scroll_changed"
+ namespace: "toolkit"
+ description: "Feature flag for enabling the dispatchOnScrollChanged method in ViewTreeObserver."
+ bug: "238109286"
+} \ No newline at end of file
diff --git a/core/java/android/view/inputmethod/ImeTracker.java b/core/java/android/view/inputmethod/ImeTracker.java
index 5dadf32d2a36..b1ba8b32d2f4 100644
--- a/core/java/android/view/inputmethod/ImeTracker.java
+++ b/core/java/android/view/inputmethod/ImeTracker.java
@@ -231,6 +231,7 @@ public interface ImeTracker {
PHASE_WM_WINDOW_ANIMATING_TYPES_CHANGED,
PHASE_WM_NOTIFY_HIDE_ANIMATION_FINISHED,
PHASE_WM_UPDATE_DISPLAY_WINDOW_ANIMATING_TYPES,
+ PHASE_CLIENT_ON_CONTROLS_CHANGED,
})
@Retention(RetentionPolicy.SOURCE)
@interface Phase {}
@@ -469,6 +470,9 @@ public interface ImeTracker {
/** The control target reported its animatingTypes back to WindowManagerService. */
int PHASE_WM_UPDATE_DISPLAY_WINDOW_ANIMATING_TYPES =
ImeProtoEnums.PHASE_WM_UPDATE_DISPLAY_WINDOW_ANIMATING_TYPES;
+ /** InsetsController received a control for the IME. */
+ int PHASE_CLIENT_ON_CONTROLS_CHANGED =
+ ImeProtoEnums.PHASE_CLIENT_ON_CONTROLS_CHANGED;
/**
* Called when an IME request is started.
diff --git a/core/java/android/view/inspector/WindowInspector.java b/core/java/android/view/inspector/WindowInspector.java
index 3ebca3c9d9b6..f0cc01133e07 100644
--- a/core/java/android/view/inspector/WindowInspector.java
+++ b/core/java/android/view/inspector/WindowInspector.java
@@ -42,8 +42,9 @@ public final class WindowInspector {
}
/**
- * Adds a listener that is notified whenever the list of global window views changes. If a
- * {@link Consumer} is already registered this method is a no op.
+ * Adds a listener that is notified whenever the value of {@link #getGlobalWindowViews()}
+ * changes. The current value is provided immediately using the provided {@link Executor}.
+ * If this {@link Consumer} is already registered, then this method is a no op.
* @see #getGlobalWindowViews()
*/
@FlaggedApi(android.view.flags.Flags.FLAG_ROOT_VIEW_CHANGED_LISTENER)
diff --git a/core/java/android/view/translation/ListenerGroup.java b/core/java/android/view/translation/ListenerGroup.java
index bf506815f841..5c70805042fa 100644
--- a/core/java/android/view/translation/ListenerGroup.java
+++ b/core/java/android/view/translation/ListenerGroup.java
@@ -48,7 +48,7 @@ public class ListenerGroup<T> {
* is a no op.
*/
public void addListener(@NonNull Executor executor, @NonNull Consumer<T> consumer) {
- if (isContained(consumer)) {
+ if (isConsumerPresent(consumer)) {
return;
}
mListeners.add(new ListenerWrapper<>(executor, consumer));
@@ -69,7 +69,7 @@ public class ListenerGroup<T> {
* Returns {@code true} if the {@link Consumer} is present in the list, {@code false}
* otherwise.
*/
- private boolean isContained(Consumer<T> consumer) {
+ public boolean isConsumerPresent(Consumer<T> consumer) {
return computeIndex(consumer) > -1;
}
diff --git a/core/java/android/webkit/WebSettings.java b/core/java/android/webkit/WebSettings.java
index ab7a4f289d73..ed3f2d12ac78 100644
--- a/core/java/android/webkit/WebSettings.java
+++ b/core/java/android/webkit/WebSettings.java
@@ -1789,17 +1789,4 @@ public abstract class WebSettings {
* @see #setDisabledActionModeMenuItems
*/
public static final int MENU_ITEM_PROCESS_TEXT = 1 << 2;
-
- /**
- * Enable CHIPS for webview.
- * This provides a means to check if partitioned cookies are enabled by default.
- * CHIPS will only be enabled by default for apps targeting Android B or above.
- *
- * @hide
- */
- @ChangeId
- @EnabledAfter(targetSdkVersion = android.os.Build.VERSION_CODES.VANILLA_ICE_CREAM)
- @FlaggedApi(android.webkit.Flags.FLAG_ENABLE_CHIPS)
- @SystemApi
- public static final long ENABLE_CHIPS = 380890146L;
}
diff --git a/core/java/android/webkit/flags.aconfig b/core/java/android/webkit/flags.aconfig
index 16cbb8abc9d6..c5176a2f1f15 100644
--- a/core/java/android/webkit/flags.aconfig
+++ b/core/java/android/webkit/flags.aconfig
@@ -36,14 +36,6 @@ flag {
}
flag {
- name: "enable_chips"
- is_exported: true
- namespace: "webview"
- description: "New feature enable CHIPS for webview"
- bug: "359448044"
-}
-
-flag {
name: "file_system_access"
is_exported: true
namespace: "webview"
diff --git a/core/java/android/widget/DatePickerCalendarDelegate.java b/core/java/android/widget/DatePickerCalendarDelegate.java
index 536b81f77174..4f7eef007a68 100644
--- a/core/java/android/widget/DatePickerCalendarDelegate.java
+++ b/core/java/android/widget/DatePickerCalendarDelegate.java
@@ -34,6 +34,7 @@ import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.DayPickerView.OnDaySelectedListener;
import android.widget.YearPickerView.OnYearSelectedListener;
@@ -76,10 +77,6 @@ class DatePickerCalendarDelegate extends DatePicker.AbstractDatePickerDelegate {
private DayPickerView mDayPickerView;
private YearPickerView mYearPickerView;
- // Accessibility strings.
- private String mSelectDay;
- private String mSelectYear;
-
private int mCurrentView = UNINITIALIZED;
private final Calendar mTempDate;
@@ -118,8 +115,15 @@ class DatePickerCalendarDelegate extends DatePicker.AbstractDatePickerDelegate {
final ViewGroup header = mContainer.findViewById(R.id.date_picker_header);
mHeaderYear = header.findViewById(R.id.date_picker_header_year);
mHeaderYear.setOnClickListener(mOnHeaderClickListener);
+ mHeaderYear.setAccessibilityDelegate(
+ new ClickActionDelegate(context, R.string.select_year));
+ mHeaderYear.setAccessibilityLiveRegion(View.ACCESSIBILITY_LIVE_REGION_POLITE);
+
mHeaderMonthDay = header.findViewById(R.id.date_picker_header_date);
mHeaderMonthDay.setOnClickListener(mOnHeaderClickListener);
+ mHeaderMonthDay.setAccessibilityDelegate(
+ new ClickActionDelegate(context, R.string.select_day));
+ mHeaderMonthDay.setAccessibilityLiveRegion(View.ACCESSIBILITY_LIVE_REGION_POLITE);
// For the sake of backwards compatibility, attempt to extract the text
// color from the header month text appearance. If it's set, we'll let
@@ -170,10 +174,6 @@ class DatePickerCalendarDelegate extends DatePicker.AbstractDatePickerDelegate {
mYearPickerView.setYear(mCurrentDate.get(Calendar.YEAR));
mYearPickerView.setOnYearSelectedListener(mOnYearSelectedListener);
- // Set up content descriptions.
- mSelectDay = res.getString(R.string.select_day);
- mSelectYear = res.getString(R.string.select_year);
-
// Initialize for current locale. This also initializes the date, so no
// need to call onDateChanged.
onLocaleChanged(mCurrentLocale);
@@ -230,6 +230,22 @@ class DatePickerCalendarDelegate extends DatePicker.AbstractDatePickerDelegate {
return srcRgb | (dstAlpha << 24);
}
+ private static class ClickActionDelegate extends View.AccessibilityDelegate {
+ private final AccessibilityNodeInfo.AccessibilityAction mClickAction;
+
+ ClickActionDelegate(Context context, int resId) {
+ mClickAction = new AccessibilityNodeInfo.AccessibilityAction(
+ AccessibilityNodeInfo.ACTION_CLICK, context.getString(resId));
+ }
+
+ @Override
+ public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) {
+ super.onInitializeAccessibilityNodeInfo(host, info);
+
+ info.addAction(mClickAction);
+ }
+ }
+
/**
* Listener called when the user selects a day in the day picker view.
*/
@@ -310,10 +326,10 @@ class DatePickerCalendarDelegate extends DatePicker.AbstractDatePickerDelegate {
mYearFormat = DateFormat.getInstanceForSkeleton("y", locale);
// Update the header text.
- onCurrentDateChanged(false);
+ onCurrentDateChanged();
}
- private void onCurrentDateChanged(boolean announce) {
+ private void onCurrentDateChanged() {
if (mHeaderYear == null) {
// Abort, we haven't initialized yet. This method will get called
// again later after everything has been set up.
@@ -325,11 +341,6 @@ class DatePickerCalendarDelegate extends DatePicker.AbstractDatePickerDelegate {
final String monthDay = mMonthDayFormat.format(mCurrentDate.getTime());
mHeaderMonthDay.setText(monthDay);
-
- // TODO: This should use live regions.
- if (announce) {
- mAnimator.announceForAccessibility(getFormattedCurrentDate());
- }
}
private void setCurrentView(final int viewIndex) {
@@ -343,8 +354,6 @@ class DatePickerCalendarDelegate extends DatePicker.AbstractDatePickerDelegate {
mAnimator.setDisplayedChild(VIEW_MONTH_DAY);
mCurrentView = viewIndex;
}
-
- mAnimator.announceForAccessibility(mSelectDay);
break;
case VIEW_YEAR:
final int year = mCurrentDate.get(Calendar.YEAR);
@@ -364,7 +373,6 @@ class DatePickerCalendarDelegate extends DatePicker.AbstractDatePickerDelegate {
mCurrentView = viewIndex;
}
- mAnimator.announceForAccessibility(mSelectYear);
break;
}
}
@@ -409,7 +417,7 @@ class DatePickerCalendarDelegate extends DatePicker.AbstractDatePickerDelegate {
mDayPickerView.setDate(mCurrentDate.getTimeInMillis());
mYearPickerView.setYear(year);
- onCurrentDateChanged(fromUser);
+ onCurrentDateChanged();
if (fromUser) {
tryVibrate();
@@ -564,7 +572,7 @@ class DatePickerCalendarDelegate extends DatePicker.AbstractDatePickerDelegate {
mMinDate.setTimeInMillis(ss.getMinDate());
mMaxDate.setTimeInMillis(ss.getMaxDate());
- onCurrentDateChanged(false);
+ onCurrentDateChanged();
final int currentView = ss.getCurrentView();
setCurrentView(currentView);
diff --git a/core/java/android/widget/TimePickerClockDelegate.java b/core/java/android/widget/TimePickerClockDelegate.java
index a453c2818566..ebb0f99e988b 100644
--- a/core/java/android/widget/TimePickerClockDelegate.java
+++ b/core/java/android/widget/TimePickerClockDelegate.java
@@ -121,12 +121,8 @@ class TimePickerClockDelegate extends TimePicker.AbstractTimePickerDelegate {
// Localization data.
private boolean mHourFormatShowLeadingZero;
- private boolean mHourFormatStartsAtZero;
-
- // Most recent time announcement values for accessibility.
- private CharSequence mLastAnnouncedText;
- private boolean mLastAnnouncedIsHour;
+ private boolean mHourFormatStartsAtZero;
public TimePickerClockDelegate(TimePicker delegator, Context context, AttributeSet attrs,
int defStyleAttr, int defStyleRes) {
super(delegator, context);
@@ -155,6 +151,7 @@ class TimePickerClockDelegate extends TimePicker.AbstractTimePickerDelegate {
mHourView.setOnDigitEnteredListener(mDigitEnteredListener);
mHourView.setAccessibilityDelegate(
new ClickActionDelegate(context, R.string.select_hours));
+ mHourView.setAccessibilityLiveRegion(View.ACCESSIBILITY_LIVE_REGION_POLITE);
mSeparatorView = (TextView) mainView.findViewById(R.id.separator);
mMinuteView = (NumericTextView) mainView.findViewById(R.id.minutes);
mMinuteView.setOnClickListener(mClickListener);
@@ -162,6 +159,7 @@ class TimePickerClockDelegate extends TimePicker.AbstractTimePickerDelegate {
mMinuteView.setOnDigitEnteredListener(mDigitEnteredListener);
mMinuteView.setAccessibilityDelegate(
new ClickActionDelegate(context, R.string.select_minutes));
+ mMinuteView.setAccessibilityLiveRegion(View.ACCESSIBILITY_LIVE_REGION_POLITE);
mMinuteView.setRange(0, 59);
// Set up AM/PM labels.
@@ -435,7 +433,7 @@ class TimePickerClockDelegate extends TimePicker.AbstractTimePickerDelegate {
private void updateRadialPicker(int index) {
mRadialTimePickerView.initialize(mCurrentHour, mCurrentMinute, mIs24Hour);
- setCurrentItemShowing(index, false, true);
+ setCurrentItemShowing(index, false);
}
private void updateHeaderAmPm() {
@@ -786,18 +784,10 @@ class TimePickerClockDelegate extends TimePicker.AbstractTimePickerDelegate {
private void updateHeaderHour(int hourOfDay, boolean announce) {
final int localizedHour = getLocalizedHour(hourOfDay);
mHourView.setValue(localizedHour);
-
- if (announce) {
- tryAnnounceForAccessibility(mHourView.getText(), true);
- }
}
private void updateHeaderMinute(int minuteOfHour, boolean announce) {
mMinuteView.setValue(minuteOfHour);
-
- if (announce) {
- tryAnnounceForAccessibility(mMinuteView.getText(), false);
- }
}
/**
@@ -876,31 +866,12 @@ class TimePickerClockDelegate extends TimePicker.AbstractTimePickerDelegate {
return -1;
}
- private void tryAnnounceForAccessibility(CharSequence text, boolean isHour) {
- if (mLastAnnouncedIsHour != isHour || !text.equals(mLastAnnouncedText)) {
- // TODO: Find a better solution, potentially live regions?
- mDelegator.announceForAccessibility(text);
- mLastAnnouncedText = text;
- mLastAnnouncedIsHour = isHour;
- }
- }
-
/**
* Show either Hours or Minutes.
*/
- private void setCurrentItemShowing(int index, boolean animateCircle, boolean announce) {
+ private void setCurrentItemShowing(int index, boolean animateCircle) {
mRadialTimePickerView.setCurrentItemShowing(index, animateCircle);
- if (index == HOUR_INDEX) {
- if (announce) {
- mDelegator.announceForAccessibility(mSelectHours);
- }
- } else {
- if (announce) {
- mDelegator.announceForAccessibility(mSelectMinutes);
- }
- }
-
mHourView.setActivated(index == HOUR_INDEX);
mMinuteView.setActivated(index == MINUTE_INDEX);
}
@@ -930,10 +901,7 @@ class TimePickerClockDelegate extends TimePicker.AbstractTimePickerDelegate {
final boolean isTransition = mAllowAutoAdvance && autoAdvance;
setHourInternal(newValue, FROM_RADIAL_PICKER, !isTransition, true);
if (isTransition) {
- setCurrentItemShowing(MINUTE_INDEX, true, false);
-
- final int localizedHour = getLocalizedHour(newValue);
- mDelegator.announceForAccessibility(localizedHour + ". " + mSelectMinutes);
+ setCurrentItemShowing(MINUTE_INDEX, true);
}
break;
case RadialTimePickerView.MINUTES:
@@ -1030,10 +998,10 @@ class TimePickerClockDelegate extends TimePicker.AbstractTimePickerDelegate {
setAmOrPm(PM);
break;
case R.id.hours:
- setCurrentItemShowing(HOUR_INDEX, true, true);
+ setCurrentItemShowing(HOUR_INDEX, true);
break;
case R.id.minutes:
- setCurrentItemShowing(MINUTE_INDEX, true, true);
+ setCurrentItemShowing(MINUTE_INDEX, true);
break;
default:
// Failed to handle this click, don't vibrate.
@@ -1058,10 +1026,10 @@ class TimePickerClockDelegate extends TimePicker.AbstractTimePickerDelegate {
setAmOrPm(PM);
break;
case R.id.hours:
- setCurrentItemShowing(HOUR_INDEX, true, true);
+ setCurrentItemShowing(HOUR_INDEX, true);
break;
case R.id.minutes:
- setCurrentItemShowing(MINUTE_INDEX, true, true);
+ setCurrentItemShowing(MINUTE_INDEX, true);
break;
default:
// Failed to handle this click, don't vibrate.
diff --git a/core/java/android/window/BackMotionEvent.java b/core/java/android/window/BackMotionEvent.java
index d53c787749d9..cc2afbc6aaa3 100644
--- a/core/java/android/window/BackMotionEvent.java
+++ b/core/java/android/window/BackMotionEvent.java
@@ -18,6 +18,7 @@ package android.window;
import android.annotation.FloatRange;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.os.Parcel;
import android.os.Parcelable;
import android.view.RemoteAnimationTarget;
@@ -38,6 +39,8 @@ public final class BackMotionEvent implements Parcelable {
@BackEvent.SwipeEdge
private final int mSwipeEdge;
+ @Nullable
+ private final RemoteAnimationTarget mDepartingAnimationTarget;
/**
* Creates a new {@link BackMotionEvent} instance.
@@ -50,6 +53,8 @@ public final class BackMotionEvent implements Parcelable {
* @param progress Value between 0 and 1 on how far along the back gesture is.
* @param triggerBack Indicates whether the back arrow is in the triggered state or not
* @param swipeEdge Indicates which edge the swipe starts from.
+ * @param departingAnimationTarget The remote animation target of the departing
+ * application window.
*/
public BackMotionEvent(
float touchX,
@@ -57,13 +62,15 @@ public final class BackMotionEvent implements Parcelable {
long frameTimeMillis,
float progress,
boolean triggerBack,
- @BackEvent.SwipeEdge int swipeEdge) {
+ @BackEvent.SwipeEdge int swipeEdge,
+ @Nullable RemoteAnimationTarget departingAnimationTarget) {
mTouchX = touchX;
mTouchY = touchY;
mFrameTimeMillis = frameTimeMillis;
mProgress = progress;
mTriggerBack = triggerBack;
mSwipeEdge = swipeEdge;
+ mDepartingAnimationTarget = departingAnimationTarget;
}
private BackMotionEvent(@NonNull Parcel in) {
@@ -72,6 +79,7 @@ public final class BackMotionEvent implements Parcelable {
mProgress = in.readFloat();
mTriggerBack = in.readBoolean();
mSwipeEdge = in.readInt();
+ mDepartingAnimationTarget = in.readTypedObject(RemoteAnimationTarget.CREATOR);
mFrameTimeMillis = in.readLong();
}
@@ -100,6 +108,7 @@ public final class BackMotionEvent implements Parcelable {
dest.writeFloat(mProgress);
dest.writeBoolean(mTriggerBack);
dest.writeInt(mSwipeEdge);
+ dest.writeTypedObject(mDepartingAnimationTarget, flags);
dest.writeLong(mFrameTimeMillis);
}
@@ -151,6 +160,16 @@ public final class BackMotionEvent implements Parcelable {
return mFrameTimeMillis;
}
+ /**
+ * Returns the {@link RemoteAnimationTarget} of the top departing application window,
+ * or {@code null} if the top window should not be moved for the current type of back
+ * destination.
+ */
+ @Nullable
+ public RemoteAnimationTarget getDepartingAnimationTarget() {
+ return mDepartingAnimationTarget;
+ }
+
@Override
public String toString() {
return "BackMotionEvent{"
@@ -160,6 +179,7 @@ public final class BackMotionEvent implements Parcelable {
+ ", mProgress=" + mProgress
+ ", mTriggerBack=" + mTriggerBack
+ ", mSwipeEdge=" + mSwipeEdge
+ + ", mDepartingAnimationTarget=" + mDepartingAnimationTarget
+ "}";
}
}
diff --git a/core/java/android/window/BackTouchTracker.java b/core/java/android/window/BackTouchTracker.java
index ea1b64066cfe..4908068d51e4 100644
--- a/core/java/android/window/BackTouchTracker.java
+++ b/core/java/android/window/BackTouchTracker.java
@@ -20,6 +20,7 @@ import android.annotation.FloatRange;
import android.os.SystemProperties;
import android.util.MathUtils;
import android.view.MotionEvent;
+import android.view.RemoteAnimationTarget;
import java.io.PrintWriter;
@@ -146,14 +147,15 @@ public class BackTouchTracker {
}
/** Creates a start {@link BackMotionEvent}. */
- public BackMotionEvent createStartEvent() {
+ public BackMotionEvent createStartEvent(RemoteAnimationTarget target) {
return new BackMotionEvent(
/* touchX = */ mInitTouchX,
/* touchY = */ mInitTouchY,
/* frameTimeMillis = */ 0,
/* progress = */ 0,
/* triggerBack = */ mTriggerBack,
- /* swipeEdge = */ mSwipeEdge);
+ /* swipeEdge = */ mSwipeEdge,
+ /* departingAnimationTarget = */ target);
}
/** Creates a progress {@link BackMotionEvent}. */
@@ -237,7 +239,8 @@ public class BackTouchTracker {
/* frameTimeMillis = */ 0,
/* progress = */ progress,
/* triggerBack = */ mTriggerBack,
- /* swipeEdge = */ mSwipeEdge);
+ /* swipeEdge = */ mSwipeEdge,
+ /* departingAnimationTarget = */ null);
}
/** Sets the thresholds for computing progress. */
diff --git a/core/java/android/window/DesktopExperienceFlags.java b/core/java/android/window/DesktopExperienceFlags.java
index 5e8ce5ee557f..04e6c68859c9 100644
--- a/core/java/android/window/DesktopExperienceFlags.java
+++ b/core/java/android/window/DesktopExperienceFlags.java
@@ -64,6 +64,7 @@ public enum DesktopExperienceFlags {
ENABLE_DYNAMIC_RADIUS_COMPUTATION_BUGFIX(Flags::enableDynamicRadiusComputationBugfix, false),
ENABLE_KEYBOARD_SHORTCUTS_TO_SWITCH_DESKS(Flags::keyboardShortcutsToSwitchDesks, false),
ENABLE_MOVE_TO_NEXT_DISPLAY_SHORTCUT(Flags::enableMoveToNextDisplayShortcut, true),
+ ENABLE_MULTIDISPLAY_TRACKPAD_BACK_GESTURE(Flags::enableMultidisplayTrackpadBackGesture, false),
ENABLE_MULTIPLE_DESKTOPS_BACKEND(Flags::enableMultipleDesktopsBackend, false),
ENABLE_MULTIPLE_DESKTOPS_FRONTEND(Flags::enableMultipleDesktopsFrontend, false),
ENABLE_PERSISTING_DISPLAY_SIZE_FOR_CONNECTED_DISPLAYS(
diff --git a/core/java/android/window/DesktopModeFlags.java b/core/java/android/window/DesktopModeFlags.java
index 5b3044e1988a..9b3d6242b213 100644
--- a/core/java/android/window/DesktopModeFlags.java
+++ b/core/java/android/window/DesktopModeFlags.java
@@ -44,10 +44,12 @@ public enum DesktopModeFlags {
// All desktop mode related flags to be overridden by developer option toggle will be added here
// go/keep-sorted start
DISABLE_DESKTOP_LAUNCH_PARAMS_OUTSIDE_DESKTOP_BUG_FIX(
- Flags::disableDesktopLaunchParamsOutsideDesktopBugFix, false),
+ Flags::disableDesktopLaunchParamsOutsideDesktopBugFix, true),
DISABLE_NON_RESIZABLE_APP_SNAP_RESIZE(Flags::disableNonResizableAppSnapResizing, true),
ENABLE_ACCESSIBLE_CUSTOM_HEADERS(Flags::enableAccessibleCustomHeaders, true),
ENABLE_APP_HEADER_WITH_TASK_DENSITY(Flags::enableAppHeaderWithTaskDensity, true),
+ ENABLE_CAMERA_COMPAT_SIMULATE_REQUESTED_ORIENTATION(
+ Flags::enableCameraCompatForDesktopWindowing, true),
ENABLE_CAPTION_COMPAT_INSET_FORCE_CONSUMPTION(Flags::enableCaptionCompatInsetForceConsumption,
true),
ENABLE_CAPTION_COMPAT_INSET_FORCE_CONSUMPTION_ALWAYS(
@@ -64,16 +66,16 @@ public enum DesktopModeFlags {
ENABLE_DESKTOP_INDICATOR_IN_SEPARATE_THREAD_BUGFIX(
Flags::enableDesktopIndicatorInSeparateThreadBugfix, false),
ENABLE_DESKTOP_OPENING_DEEPLINK_MINIMIZE_ANIMATION_BUGFIX(
- Flags::enableDesktopOpeningDeeplinkMinimizeAnimationBugfix, false),
+ Flags::enableDesktopOpeningDeeplinkMinimizeAnimationBugfix, true),
ENABLE_DESKTOP_RECENTS_TRANSITIONS_CORNERS_BUGFIX(
- Flags::enableDesktopRecentsTransitionsCornersBugfix, false),
+ Flags::enableDesktopRecentsTransitionsCornersBugfix, true),
ENABLE_DESKTOP_SKIP_COMPAT_UI_EDUCATION_IN_DESKTOP_MODE_BUGFIX(
Flags::skipCompatUiEducationInDesktopMode, true),
ENABLE_DESKTOP_SYSTEM_DIALOGS_TRANSITIONS(Flags::enableDesktopSystemDialogsTransitions, true),
ENABLE_DESKTOP_TAB_TEARING_MINIMIZE_ANIMATION_BUGFIX(
- Flags::enableDesktopTabTearingMinimizeAnimationBugfix, false),
+ Flags::enableDesktopTabTearingMinimizeAnimationBugfix, true),
ENABLE_DESKTOP_TRAMPOLINE_CLOSE_ANIMATION_BUGFIX(
- Flags::enableDesktopTrampolineCloseAnimationBugfix, false),
+ Flags::enableDesktopTrampolineCloseAnimationBugfix, true),
ENABLE_DESKTOP_WALLPAPER_ACTIVITY_FOR_SYSTEM_USER(
Flags::enableDesktopWallpaperActivityForSystemUser, true),
ENABLE_DESKTOP_WINDOWING_APP_TO_WEB(Flags::enableDesktopWindowingAppToWeb, true),
@@ -83,7 +85,7 @@ public enum DesktopModeFlags {
ENABLE_DESKTOP_WINDOWING_ENTER_TRANSITIONS_BUGFIX(
Flags::enableDesktopWindowingEnterTransitionBugfix, true),
ENABLE_DESKTOP_WINDOWING_EXIT_BY_MINIMIZE_TRANSITION_BUGFIX(
- Flags::enableDesktopWindowingExitByMinimizeTransitionBugfix, false),
+ Flags::enableDesktopWindowingExitByMinimizeTransitionBugfix, true),
ENABLE_DESKTOP_WINDOWING_EXIT_TRANSITIONS_BUGFIX(
Flags::enableDesktopWindowingExitTransitionsBugfix, true),
ENABLE_DESKTOP_WINDOWING_HSUM(Flags::enableDesktopWindowingHsum, true),
@@ -103,23 +105,24 @@ public enum DesktopModeFlags {
ENABLE_DESKTOP_WINDOWING_TASK_LIMIT(Flags::enableDesktopWindowingTaskLimit, true),
ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY(Flags::enableDesktopWindowingWallpaperActivity,
true),
- ENABLE_DRAG_RESIZE_SET_UP_IN_BG_THREAD(Flags::enableDragResizeSetUpInBgThread, false),
+ ENABLE_DRAG_RESIZE_SET_UP_IN_BG_THREAD(Flags::enableDragResizeSetUpInBgThread, true),
ENABLE_DRAG_TO_DESKTOP_INCOMING_TRANSITIONS_BUGFIX(
- Flags::enableDragToDesktopIncomingTransitionsBugfix, false),
+ Flags::enableDragToDesktopIncomingTransitionsBugfix, true),
ENABLE_FULLY_IMMERSIVE_IN_DESKTOP(Flags::enableFullyImmersiveInDesktop, true),
ENABLE_HANDLE_INPUT_FIX(Flags::enableHandleInputFix, true),
ENABLE_HOLD_TO_DRAG_APP_HANDLE(Flags::enableHoldToDragAppHandle, true),
ENABLE_INPUT_LAYER_TRANSITION_FIX(Flags::enableInputLayerTransitionFix, false),
ENABLE_MINIMIZE_BUTTON(Flags::enableMinimizeButton, true),
- ENABLE_MODALS_FULLSCREEN_WITH_PERMISSIONS(Flags::enableModalsFullscreenWithPermission, false),
+ ENABLE_MODALS_FULLSCREEN_WITH_PERMISSIONS(Flags::enableModalsFullscreenWithPermission, true),
ENABLE_OPAQUE_BACKGROUND_FOR_TRANSPARENT_WINDOWS(
Flags::enableOpaqueBackgroundForTransparentWindows, true),
ENABLE_QUICKSWITCH_DESKTOP_SPLIT_BUGFIX(Flags::enableQuickswitchDesktopSplitBugfix, true),
+ ENABLE_REQUEST_FULLSCREEN_BUGFIX(Flags::enableRequestFullscreenBugfix, false),
ENABLE_RESIZING_METRICS(Flags::enableResizingMetrics, true),
ENABLE_RESTORE_TO_PREVIOUS_SIZE_FROM_DESKTOP_IMMERSIVE(
Flags::enableRestoreToPreviousSizeFromDesktopImmersive, true),
ENABLE_SHELL_INITIAL_BOUNDS_REGRESSION_BUG_FIX(
- Flags::enableShellInitialBoundsRegressionBugFix, false),
+ Flags::enableShellInitialBoundsRegressionBugFix, true),
ENABLE_START_LAUNCH_TRANSITION_FROM_TASKBAR_BUGFIX(
Flags::enableStartLaunchTransitionFromTaskbarBugfix, true),
ENABLE_TASKBAR_OVERFLOW(Flags::enableTaskbarOverflow, false),
@@ -131,13 +134,13 @@ public enum DesktopModeFlags {
ENABLE_TOP_VISIBLE_ROOT_TASK_PER_USER_TRACKING(Flags::enableTopVisibleRootTaskPerUserTracking,
true),
ENABLE_VISUAL_INDICATOR_IN_TRANSITION_BUGFIX(
- Flags::enableVisualIndicatorInTransitionBugfix, false),
+ Flags::enableVisualIndicatorInTransitionBugfix, true),
ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS(Flags::enableWindowingDynamicInitialBounds, true),
ENABLE_WINDOWING_EDGE_DRAG_RESIZE(Flags::enableWindowingEdgeDragResize, true),
ENABLE_WINDOWING_SCALED_RESIZING(Flags::enableWindowingScaledResizing, true),
ENABLE_WINDOWING_TRANSITION_HANDLERS_OBSERVERS(
Flags::enableWindowingTransitionHandlersObservers, false),
- EXCLUDE_CAPTION_FROM_APP_BOUNDS(Flags::excludeCaptionFromAppBounds, false),
+ EXCLUDE_CAPTION_FROM_APP_BOUNDS(Flags::excludeCaptionFromAppBounds, true),
FORCE_CLOSE_TOP_TRANSPARENT_FULLSCREEN_TASK(
Flags::forceCloseTopTransparentFullscreenTask, false),
IGNORE_ASPECT_RATIO_RESTRICTIONS_FOR_RESIZEABLE_FREEFORM_ACTIVITIES(
diff --git a/core/java/android/window/ImeOnBackInvokedDispatcher.java b/core/java/android/window/ImeOnBackInvokedDispatcher.java
index 69613a748884..d478108d928a 100644
--- a/core/java/android/window/ImeOnBackInvokedDispatcher.java
+++ b/core/java/android/window/ImeOnBackInvokedDispatcher.java
@@ -270,7 +270,8 @@ public class ImeOnBackInvokedDispatcher implements OnBackInvokedDispatcher, Parc
}
mIOnBackInvokedCallback.onBackStarted(
new BackMotionEvent(backEvent.getTouchX(), backEvent.getTouchY(), frameTime,
- backEvent.getProgress(), false, backEvent.getSwipeEdge()));
+ backEvent.getProgress(), false, backEvent.getSwipeEdge(),
+ null));
} catch (RemoteException e) {
Log.e(TAG, "Exception when invoking forwarded callback. e: ", e);
}
@@ -285,7 +286,8 @@ public class ImeOnBackInvokedDispatcher implements OnBackInvokedDispatcher, Parc
}
mIOnBackInvokedCallback.onBackProgressed(
new BackMotionEvent(backEvent.getTouchX(), backEvent.getTouchY(), frameTime,
- backEvent.getProgress(), false, backEvent.getSwipeEdge()));
+ backEvent.getProgress(), false, backEvent.getSwipeEdge(),
+ null));
} catch (RemoteException e) {
Log.e(TAG, "Exception when invoking forwarded callback. e: ", e);
}
diff --git a/core/java/android/window/TaskSnapshot.java b/core/java/android/window/TaskSnapshot.java
index 53c64bd6e664..4ddd73319840 100644
--- a/core/java/android/window/TaskSnapshot.java
+++ b/core/java/android/window/TaskSnapshot.java
@@ -37,6 +37,7 @@ import com.android.window.flags.Flags;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.function.Consumer;
/**
* Represents a task snapshot.
@@ -76,6 +77,7 @@ public class TaskSnapshot implements Parcelable {
// Must be one of the named color spaces, otherwise, always use SRGB color space.
private final ColorSpace mColorSpace;
private int mInternalReferences;
+ private Consumer<HardwareBuffer> mSafeSnapshotReleaser;
/** Keep in cache, doesn't need reference. */
public static final int REFERENCE_NONE = 0;
@@ -365,8 +367,24 @@ public class TaskSnapshot implements Parcelable {
mInternalReferences &= ~usage;
if (Flags.releaseSnapshotAggressively() && mInternalReferences == 0 && mSnapshot != null
&& !mSnapshot.isClosed()) {
- mSnapshot.close();
+ if (mSafeSnapshotReleaser != null) {
+ mSafeSnapshotReleaser.accept(mSnapshot);
+ } else {
+ mSnapshot.close();
+ }
+ }
+ }
+
+ /**
+ * Register a safe release callback, instead of immediately closing the hardware buffer when
+ * no more reference, to let the system server decide when to close it.
+ * Only used in core.
+ */
+ public synchronized void setSafeRelease(Consumer<HardwareBuffer> releaser) {
+ if (!Flags.safeReleaseSnapshotAggressively()) {
+ return;
}
+ mSafeSnapshotReleaser = releaser;
}
public static final @NonNull Creator<TaskSnapshot> CREATOR = new Creator<TaskSnapshot>() {
diff --git a/core/java/android/window/WindowContainerTransaction.java b/core/java/android/window/WindowContainerTransaction.java
index 2ed9c3a48add..8f7f94196eb1 100644
--- a/core/java/android/window/WindowContainerTransaction.java
+++ b/core/java/android/window/WindowContainerTransaction.java
@@ -16,6 +16,7 @@
package android.window;
+import static android.app.Instrumentation.DEBUG_START_ACTIVITY;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.window.TaskFragmentOperation.OP_TYPE_CLEAR_ADJACENT_TASK_FRAGMENTS;
import static android.window.TaskFragmentOperation.OP_TYPE_CREATE_TASK_FRAGMENT;
@@ -32,6 +33,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SuppressLint;
import android.annotation.TestApi;
+import android.app.Instrumentation;
import android.app.PendingIntent;
import android.app.WindowConfiguration;
import android.app.WindowConfiguration.WindowingMode;
@@ -45,6 +47,7 @@ import android.os.IBinder;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.ArrayMap;
+import android.util.Log;
import android.view.InsetsFrameProvider;
import android.view.InsetsSource;
import android.view.SurfaceControl;
@@ -642,6 +645,10 @@ public final class WindowContainerTransaction implements Parcelable {
*/
@NonNull
public WindowContainerTransaction startTask(int taskId, @Nullable Bundle options) {
+ if (DEBUG_START_ACTIVITY) {
+ Log.d(Instrumentation.TAG, "WCT.startTask: taskId=" + taskId
+ + " options=" + options, new Throwable());
+ }
mHierarchyOps.add(HierarchyOp.createForTaskLaunch(taskId, options));
return this;
}
@@ -655,11 +662,15 @@ public final class WindowContainerTransaction implements Parcelable {
*/
@NonNull
public WindowContainerTransaction sendPendingIntent(@Nullable PendingIntent sender,
- @Nullable Intent intent, @Nullable Bundle options) {
+ @Nullable Intent fillInIntent, @Nullable Bundle options) {
+ if (DEBUG_START_ACTIVITY) {
+ Log.d(Instrumentation.TAG, "WCT.sendPendingIntent: sender=" + sender.getIntent()
+ + " fillInIntent=" + fillInIntent + " options=" + options, new Throwable());
+ }
mHierarchyOps.add(new HierarchyOp.Builder(HierarchyOp.HIERARCHY_OP_TYPE_PENDING_INTENT)
.setLaunchOptions(options)
.setPendingIntent(sender)
- .setActivityIntent(intent)
+ .setActivityIntent(fillInIntent)
.build());
return this;
}
@@ -674,6 +685,10 @@ public final class WindowContainerTransaction implements Parcelable {
@NonNull
public WindowContainerTransaction startShortcut(@NonNull String callingPackage,
@NonNull ShortcutInfo shortcutInfo, @Nullable Bundle options) {
+ if (DEBUG_START_ACTIVITY) {
+ Log.d(Instrumentation.TAG, "WCT.startShortcut: shortcutInfo=" + shortcutInfo
+ + " options=" + options, new Throwable());
+ }
mHierarchyOps.add(HierarchyOp.createForStartShortcut(
callingPackage, shortcutInfo, options));
return this;
diff --git a/core/java/android/window/flags/lse_desktop_experience.aconfig b/core/java/android/window/flags/lse_desktop_experience.aconfig
index afc9660c1a3a..0d87b73a5e03 100644
--- a/core/java/android/window/flags/lse_desktop_experience.aconfig
+++ b/core/java/android/window/flags/lse_desktop_experience.aconfig
@@ -595,6 +595,13 @@ flag {
}
flag {
+ name: "nested_tasks_with_independent_bounds"
+ namespace: "lse_desktop_experience"
+ description: "Allows tasks under a root task to be have independent (non-inherited) bounds"
+ bug: "402825303"
+}
+
+flag {
name: "enable_multiple_desktops_backend"
namespace: "lse_desktop_experience"
description: "Enable multiple desktop sessions for desktop windowing (backend)."
@@ -964,6 +971,16 @@ flag {
}
flag {
+ name: "enable_request_fullscreen_bugfix"
+ namespace: "lse_desktop_experience"
+ description: "Fixes split to fullscreen restoration using the Activity#requestFullscreenMode API"
+ bug: "402973271"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
name: "enable_dynamic_radius_computation_bugfix"
namespace: "lse_desktop_experience"
description: "Enables bugfix to compute the corner/shadow radius of desktop windows dynamically with the current window context."
@@ -979,3 +996,13 @@ flag {
description: "Enables the home to be shown behind the desktop."
bug: "375644149"
}
+
+flag {
+ name: "enable_desktop_ime_bugfix"
+ namespace: "lse_desktop_experience"
+ description: "Enables bugfix to handle IME interactions in desktop windowing."
+ bug: "388570293"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/core/java/android/window/flags/responsible_apis.aconfig b/core/java/android/window/flags/responsible_apis.aconfig
index 36219812c002..7039add0b179 100644
--- a/core/java/android/window/flags/responsible_apis.aconfig
+++ b/core/java/android/window/flags/responsible_apis.aconfig
@@ -88,3 +88,10 @@ flag {
description: "Clear the allowlist duration when clearAllowBgActivityStarts is called"
bug: "322159724"
}
+
+flag {
+ name: "bal_additional_logging"
+ namespace: "responsible_apis"
+ description: "Enable additional logging."
+ bug: "403398176"
+}
diff --git a/core/java/android/window/flags/windowing_frontend.aconfig b/core/java/android/window/flags/windowing_frontend.aconfig
index 816270235446..59dd32258d8c 100644
--- a/core/java/android/window/flags/windowing_frontend.aconfig
+++ b/core/java/android/window/flags/windowing_frontend.aconfig
@@ -287,6 +287,17 @@ flag {
}
flag {
+ name: "use_visible_requested_for_process_tracker"
+ namespace: "windowing_frontend"
+ description: "Do not count closing activity as visible process"
+ bug: "396653764"
+ is_fixed_read_only: true
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
name: "ensure_wallpaper_in_transitions"
namespace: "windowing_frontend"
description: "Ensure that wallpaper window tokens are always present/available for collection in transitions"
@@ -505,6 +516,17 @@ flag {
}
flag {
+ name: "safe_release_snapshot_aggressively"
+ namespace: "windowing_frontend"
+ description: "Protect task snapshot memory from premature release, which can occur when a local variable holds a reference while the snapshot is removed from the cache."
+ bug: "238206323"
+ is_fixed_read_only: true
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
name: "scramble_snapshot_file_name"
namespace: "windowing_frontend"
description: "Scramble the file name of task snapshot."
@@ -513,4 +535,22 @@ flag {
metadata {
purpose: PURPOSE_BUGFIX
}
+}
+
+flag {
+ name: "delegate_back_gesture_to_shell"
+ namespace: "windowing_frontend"
+ description: "Delegate back gesture event to back animation controller."
+ bug: "394599430"
+ is_fixed_read_only: true
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
+ name: "enable_multidisplay_trackpad_back_gesture"
+ namespace: "lse_desktop_experience"
+ description: "Adds support for trackpad back gestures on connected displays"
+ bug: "382774299"
} \ No newline at end of file
diff --git a/core/java/com/android/internal/app/AssistUtils.java b/core/java/com/android/internal/app/AssistUtils.java
index 4261a0f14767..dd9cf9d7718e 100644
--- a/core/java/com/android/internal/app/AssistUtils.java
+++ b/core/java/com/android/internal/app/AssistUtils.java
@@ -61,6 +61,8 @@ public class AssistUtils {
public static final int INVOCATION_TYPE_ASSIST_BUTTON = 7;
/** value for INVOCATION_TYPE_KEY: long press on nav handle */
public static final int INVOCATION_TYPE_NAV_HANDLE_LONG_PRESS = 8;
+ /** value for INVOCATION_TYPE_KEY: sysui launcher */
+ public static final int INVOCATION_TYPE_LAUNCHER_SYSTEM_SHORTCUT = 9;
private final Context mContext;
private final IVoiceInteractionManagerService mVoiceInteractionManagerService;
diff --git a/core/java/com/android/internal/app/MediaRouteDialogPresenter.java b/core/java/com/android/internal/app/MediaRouteDialogPresenter.java
index 5628b7ed9d15..76b289416076 100644
--- a/core/java/com/android/internal/app/MediaRouteDialogPresenter.java
+++ b/core/java/com/android/internal/app/MediaRouteDialogPresenter.java
@@ -86,10 +86,7 @@ public abstract class MediaRouteDialogPresenter {
public static Dialog createDialog(Context context, int routeTypes,
View.OnClickListener extendedSettingsClickListener, int theme,
boolean showProgressBarWhenEmpty) {
- final MediaRouter router = context.getSystemService(MediaRouter.class);
-
- MediaRouter.RouteInfo route = router.getSelectedRoute();
- if (route.isDefault() || !route.matchesTypes(routeTypes)) {
+ if (shouldShowChooserDialog(context, routeTypes)) {
final MediaRouteChooserDialog d = new MediaRouteChooserDialog(context, theme,
showProgressBarWhenEmpty);
d.setRouteTypes(routeTypes);
@@ -99,4 +96,11 @@ public abstract class MediaRouteDialogPresenter {
return new MediaRouteControllerDialog(context, theme);
}
}
+
+ /** Whether we should show the chooser dialog or the controller dialog.. */
+ public static boolean shouldShowChooserDialog(Context context, int routeTypes) {
+ final MediaRouter router = context.getSystemService(MediaRouter.class);
+ MediaRouter.RouteInfo route = router.getSelectedRoute();
+ return route.isDefault() || !route.matchesTypes(routeTypes);
+ }
}
diff --git a/core/java/com/android/internal/jank/Cuj.java b/core/java/com/android/internal/jank/Cuj.java
index 41e2ca9cdfad..c25f6b1dcacb 100644
--- a/core/java/com/android/internal/jank/Cuj.java
+++ b/core/java/com/android/internal/jank/Cuj.java
@@ -313,8 +313,27 @@ public class Cuj {
*/
public static final int CUJ_DEFAULT_TASK_TO_TASK_ANIMATION = 128;
+ /**
+ * Track moving a window to another display in Desktop Windowing mode.
+ *
+ * <p>Tracking starts when the DesktopModeMoveToDisplayTransitionHandler starts animating the
+ * task to move it to another display. This is triggered when the user presses a keyboard
+ * shortcut or clicks the menu in the overview. Tracking ends when the animation completes.</p>
+ */
+ public static final int CUJ_DESKTOP_MODE_MOVE_WINDOW_TO_DISPLAY = 129;
+
+ /**
+ * Track the animation of an ongoing call app back into its status bar chip (displaying the call
+ * icon and timer) when returning Home.
+ *
+ * <p>Tracking starts when the RemoteTransition registered to handle the transition from the app
+ * to Home is sent the onAnimationStart() signal and start the animation. Tracking ends when
+ * the animation is fully settled and the transition is complete.</p>
+ */
+ public static final int CUJ_STATUS_BAR_APP_RETURN_TO_CALL_CHIP = 130;
+
// When adding a CUJ, update this and make sure to also update CUJ_TO_STATSD_INTERACTION_TYPE.
- @VisibleForTesting static final int LAST_CUJ = CUJ_DEFAULT_TASK_TO_TASK_ANIMATION;
+ @VisibleForTesting static final int LAST_CUJ = CUJ_STATUS_BAR_APP_RETURN_TO_CALL_CHIP;
/** @hide */
@IntDef({
@@ -434,7 +453,9 @@ public class Cuj {
CUJ_DESKTOP_MODE_KEYBOARD_QUICK_SWITCH_APP_LAUNCH,
CUJ_LAUNCHER_WORK_UTILITY_VIEW_EXPAND,
CUJ_LAUNCHER_WORK_UTILITY_VIEW_SHRINK,
- CUJ_DEFAULT_TASK_TO_TASK_ANIMATION
+ CUJ_DEFAULT_TASK_TO_TASK_ANIMATION,
+ CUJ_DESKTOP_MODE_MOVE_WINDOW_TO_DISPLAY,
+ CUJ_STATUS_BAR_APP_RETURN_TO_CALL_CHIP
})
@Retention(RetentionPolicy.SOURCE)
public @interface CujType {}
@@ -565,6 +586,8 @@ public class Cuj {
CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LAUNCHER_WORK_UTILITY_VIEW_EXPAND] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_WORK_UTILITY_VIEW_EXPAND;
CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LAUNCHER_WORK_UTILITY_VIEW_SHRINK] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_WORK_UTILITY_VIEW_SHRINK;
CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_DEFAULT_TASK_TO_TASK_ANIMATION] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__DEFAULT_TASK_TO_TASK_ANIMATION;
+ CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_DESKTOP_MODE_MOVE_WINDOW_TO_DISPLAY] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__DESKTOP_MODE_MOVE_WINDOW_TO_DISPLAY;
+ CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_STATUS_BAR_APP_RETURN_TO_CALL_CHIP] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__STATUS_BAR_APP_RETURN_TO_CALL_CHIP;
}
private Cuj() {
@@ -817,6 +840,10 @@ public class Cuj {
return "LAUNCHER_WORK_UTILITY_VIEW_SHRINK";
case CUJ_DEFAULT_TASK_TO_TASK_ANIMATION:
return "DEFAULT_TASK_TO_TASK_ANIMATION";
+ case CUJ_DESKTOP_MODE_MOVE_WINDOW_TO_DISPLAY:
+ return "DESKTOP_MODE_MOVE_WINDOW_TO_DISPLAY";
+ case CUJ_STATUS_BAR_APP_RETURN_TO_CALL_CHIP:
+ return "STATUS_BAR_APP_RETURN_TO_CALL_CHIP";
}
return "UNKNOWN";
}
diff --git a/core/java/com/android/internal/jank/DisplayResolutionTracker.java b/core/java/com/android/internal/jank/DisplayResolutionTracker.java
index 5d66b3c10197..74153157df4d 100644
--- a/core/java/com/android/internal/jank/DisplayResolutionTracker.java
+++ b/core/java/com/android/internal/jank/DisplayResolutionTracker.java
@@ -142,14 +142,23 @@ public class DisplayResolutionTracker {
public interface DisplayInterface {
/** Reurns an implementation wrapping {@link DisplayManagerGlobal}. */
static DisplayInterface getDefault(@Nullable Handler handler) {
+ long displayEventsToBeSubscribed;
+ if (com.android.server.display.feature.flags.Flags
+ .displayListenerPerformanceImprovements()
+ && com.android.server.display.feature.flags.Flags
+ .delayImplicitRrRegistrationUntilRrAccessed()) {
+ displayEventsToBeSubscribed = DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_ADDED
+ | DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_BASIC_CHANGED;
+ } else {
+ displayEventsToBeSubscribed = DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_ADDED
+ | DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_BASIC_CHANGED
+ | DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_REFRESH_RATE;
+ }
DisplayManagerGlobal manager = DisplayManagerGlobal.getInstance();
return new DisplayInterface() {
@Override
public void registerDisplayListener(DisplayManager.DisplayListener listener) {
- manager.registerDisplayListener(listener, handler,
- DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_ADDED
- | DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_BASIC_CHANGED
- | DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_REFRESH_RATE,
+ manager.registerDisplayListener(listener, handler, displayEventsToBeSubscribed,
ActivityThread.currentPackageName());
}
diff --git a/core/java/com/android/internal/os/BatteryStatsHistory.java b/core/java/com/android/internal/os/BatteryStatsHistory.java
index 8151429f9139..3fc74c9f1f54 100644
--- a/core/java/com/android/internal/os/BatteryStatsHistory.java
+++ b/core/java/com/android/internal/os/BatteryStatsHistory.java
@@ -79,7 +79,7 @@ public class BatteryStatsHistory {
private static final String TAG = "BatteryStatsHistory";
// Current on-disk Parcel version. Must be updated when the format of the parcelable changes
- private static final int VERSION = 213;
+ private static final int VERSION = 214;
// Part of initial delta int that specifies the time delta.
static final int DELTA_TIME_MASK = 0x7ffff;
@@ -316,7 +316,6 @@ public class BatteryStatsHistory {
}
private final Parcel mHistoryBuffer;
- private final HistoryStepDetailsCalculator mStepDetailsCalculator;
private final Clock mClock;
private int mMaxHistoryBufferSize;
@@ -337,25 +336,6 @@ public class BatteryStatsHistory {
*/
private List<Parcel> mHistoryParcels = null;
- /**
- * When iterating history files, the current file index.
- */
- private BatteryHistoryFragment mCurrentFragment;
-
- /**
- * When iterating history files, the current file parcel.
- */
- private Parcel mCurrentParcel;
- /**
- * When iterating history file, the current parcel's Parcel.dataSize().
- */
- private int mCurrentParcelEnd;
- /**
- * Used when BatteryStatsImpl object is created from deserialization of a parcel,
- * such as Settings app or checkin file, to iterate over history parcels.
- */
- private int mParcelIndex = 0;
-
private final ReentrantLock mWriteLock = new ReentrantLock();
private final HistoryItem mHistoryCur = new HistoryItem();
@@ -384,28 +364,11 @@ public class BatteryStatsHistory {
// Monotonically increasing size of written history
private long mMonotonicHistorySize;
private final ArraySet<PowerStats.Descriptor> mWrittenPowerStatsDescriptors = new ArraySet<>();
- private byte mLastHistoryStepLevel = 0;
private boolean mMutable = true;
private int mIteratorCookie;
private final BatteryStatsHistory mWritableHistory;
/**
- * A delegate responsible for computing additional details for a step in battery history.
- */
- public interface HistoryStepDetailsCalculator {
- /**
- * Returns additional details for the current history step or null.
- */
- @Nullable
- HistoryStepDetails getHistoryStepDetails();
-
- /**
- * Resets the calculator to get ready for a new battery session
- */
- void clear();
- }
-
- /**
* A delegate for android.os.Trace to allow testing static calls. Due to
* limitations in Android Tracing (b/153319140), the delegate also records
* counter values in system properties which allows reading the value at the
@@ -472,21 +435,17 @@ public class BatteryStatsHistory {
* @param maxHistoryBufferSize the most amount of RAM to used for buffering of history steps
*/
public BatteryStatsHistory(Parcel historyBuffer, int maxHistoryBufferSize,
- @Nullable BatteryHistoryStore store, HistoryStepDetailsCalculator stepDetailsCalculator,
- Clock clock, MonotonicClock monotonicClock, TraceDelegate tracer,
- EventLogger eventLogger) {
- this(historyBuffer, maxHistoryBufferSize, store,
- stepDetailsCalculator,
- clock, monotonicClock, tracer, eventLogger, null);
+ @Nullable BatteryHistoryStore store, Clock clock, MonotonicClock monotonicClock,
+ TraceDelegate tracer, EventLogger eventLogger) {
+ this(historyBuffer, maxHistoryBufferSize, store, clock, monotonicClock, tracer, eventLogger,
+ null);
}
private BatteryStatsHistory(@Nullable Parcel historyBuffer, int maxHistoryBufferSize,
- @Nullable BatteryHistoryStore store,
- @NonNull HistoryStepDetailsCalculator stepDetailsCalculator, @NonNull Clock clock,
+ @Nullable BatteryHistoryStore store, @NonNull Clock clock,
@NonNull MonotonicClock monotonicClock, @NonNull TraceDelegate tracer,
@NonNull EventLogger eventLogger, @Nullable BatteryStatsHistory writableHistory) {
mMaxHistoryBufferSize = maxHistoryBufferSize;
- mStepDetailsCalculator = stepDetailsCalculator;
mTracer = tracer;
mClock = clock;
mMonotonicClock = monotonicClock;
@@ -527,7 +486,6 @@ public class BatteryStatsHistory {
mClock = Clock.SYSTEM_CLOCK;
mTracer = null;
mStore = null;
- mStepDetailsCalculator = null;
mEventLogger = new EventLogger();
mWritableHistory = null;
mMutable = false;
@@ -556,9 +514,6 @@ public class BatteryStatsHistory {
mNextHistoryTagIdx = 0;
mNumHistoryTagChars = 0;
mHistoryBufferLastPos = -1;
- if (mStepDetailsCalculator != null) {
- mStepDetailsCalculator.clear();
- }
}
/**
@@ -596,7 +551,7 @@ public class BatteryStatsHistory {
Parcel historyBufferCopy = Parcel.obtain();
historyBufferCopy.appendFrom(mHistoryBuffer, 0, mHistoryBuffer.dataSize());
- return new BatteryStatsHistory(historyBufferCopy, 0, mStore, null,
+ return new BatteryStatsHistory(historyBufferCopy, 0, mStore,
null, null, null, mEventLogger, this);
}
} finally {
@@ -712,10 +667,6 @@ public class BatteryStatsHistory {
if (mStore != null) {
mStore.lock();
}
- mCurrentFragment = null;
- mCurrentParcel = null;
- mCurrentParcelEnd = 0;
- mParcelIndex = 0;
BatteryStatsHistoryIterator iterator = new BatteryStatsHistoryIterator(
this, startTimeMs, endTimeMs);
mIteratorCookie = System.identityHashCode(iterator);
@@ -760,9 +711,20 @@ public class BatteryStatsHistory {
break;
}
- if (fragment.monotonicTimeMs >= startTimeMs && fragment != mActiveFragment) {
- containers.add(new BatteryHistoryParcelContainer(fragment));
+ if (fragment.monotonicTimeMs >= mHistoryBufferStartTime) {
+ // Do not include the backup of the current buffer, which is explicitly
+ // included later
+ continue;
}
+
+ if (i < fragments.size() - 1
+ && fragments.get(i + 1).monotonicTimeMs < startTimeMs) {
+ // Since fragments are ordered, an early start of next fragment implies an
+ // early end for this one.
+ continue;
+ }
+
+ containers.add(new BatteryHistoryParcelContainer(fragment));
}
}
@@ -1603,6 +1565,21 @@ public class BatteryStatsHistory {
}
/**
+ * Records an update containing HistoryStepDetails, except if the details are empty.
+ */
+ public void recordHistoryStepDetails(HistoryStepDetails details, long elapsedRealtimeMs,
+ long uptimeMs) {
+ if (details.isEmpty()) {
+ return;
+ }
+ synchronized (this) {
+ mHistoryCur.stepDetails = details;
+ writeHistoryItem(elapsedRealtimeMs, uptimeMs);
+ mHistoryCur.stepDetails = null;
+ }
+ }
+
+ /**
* Writes the current history item to history.
*/
public void writeHistoryItem(long elapsedRealtimeMs, long uptimeMs) {
@@ -1621,8 +1598,8 @@ public class BatteryStatsHistory {
mHistoryAddTmp.processStateChange = null;
mHistoryAddTmp.eventCode = HistoryItem.EVENT_NONE;
mHistoryAddTmp.states &= ~HistoryItem.STATE_CPU_RUNNING_FLAG;
+ mHistoryAddTmp.stepDetails = null;
writeHistoryItem(wakeElapsedTimeMs, uptimeMs, mHistoryAddTmp);
-
}
}
mHistoryCur.states |= HistoryItem.STATE_CPU_RUNNING_FLAG;
@@ -1941,15 +1918,8 @@ public class BatteryStatsHistory {
}
int firstToken = deltaTimeToken | (cur.states & BatteryStatsHistory.DELTA_STATE_MASK);
- if (cur.batteryLevel < mLastHistoryStepLevel || mLastHistoryStepLevel == 0) {
- cur.stepDetails = mStepDetailsCalculator.getHistoryStepDetails();
- if (cur.stepDetails != null) {
- batteryLevelInt |= BatteryStatsHistory.BATTERY_LEVEL_DETAILS_FLAG;
- mLastHistoryStepLevel = cur.batteryLevel;
- }
- } else {
- cur.stepDetails = null;
- mLastHistoryStepLevel = cur.batteryLevel;
+ if (cur.stepDetails != null) {
+ batteryLevelInt |= BatteryStatsHistory.BATTERY_LEVEL_DETAILS_FLAG;
}
final boolean batteryLevelIntChanged = batteryLevelInt != 0;
@@ -2044,6 +2014,7 @@ public class BatteryStatsHistory {
+ Integer.toHexString(cur.states2));
}
}
+ cur.tagsFirstOccurrence = false;
if (cur.wakelockTag != null || cur.wakeReasonTag != null) {
int wakeLockIndex;
int wakeReasonIndex;
diff --git a/core/java/com/android/internal/protolog/PerfettoProtoLogImpl.java b/core/java/com/android/internal/protolog/PerfettoProtoLogImpl.java
index 6f7e5ad51b89..7cecc39dacde 100644
--- a/core/java/com/android/internal/protolog/PerfettoProtoLogImpl.java
+++ b/core/java/com/android/internal/protolog/PerfettoProtoLogImpl.java
@@ -66,6 +66,7 @@ import com.android.internal.protolog.IProtoLogConfigurationService.RegisterClien
import com.android.internal.protolog.common.ILogger;
import com.android.internal.protolog.common.IProtoLog;
import com.android.internal.protolog.common.IProtoLogGroup;
+import com.android.internal.protolog.common.InvalidFormatStringException;
import com.android.internal.protolog.common.LogDataType;
import com.android.internal.protolog.common.LogLevel;
@@ -207,7 +208,12 @@ public abstract class PerfettoProtoLogImpl extends IProtoLogClient.Stub implemen
@Override
public void log(LogLevel logLevel, IProtoLogGroup group, String messageString, Object... args) {
- log(logLevel, group, new Message(messageString), args);
+ try {
+ log(logLevel, group, new Message(messageString), args);
+ } catch (InvalidFormatStringException e) {
+ Slog.e(LOG_TAG, "Invalid protolog string format", e);
+ log(logLevel, group, new Message("INVALID MESSAGE"), new Object[0]);
+ }
}
/**
@@ -831,7 +837,7 @@ public abstract class PerfettoProtoLogImpl extends IProtoLogClient.Stub implemen
this.mMessageString = null;
}
- private Message(@NonNull String messageString) {
+ private Message(@NonNull String messageString) throws InvalidFormatStringException {
this.mMessageHash = null;
final List<Integer> argTypes = LogDataType.parseFormatString(messageString);
this.mMessageMask = LogDataType.logDataTypesToBitMask(argTypes);
diff --git a/core/java/com/android/internal/protolog/common/LogDataType.java b/core/java/com/android/internal/protolog/common/LogDataType.java
index c05824a58a77..10b07d9387de 100644
--- a/core/java/com/android/internal/protolog/common/LogDataType.java
+++ b/core/java/com/android/internal/protolog/common/LogDataType.java
@@ -86,8 +86,8 @@ public class LogDataType {
case '%':
break;
default:
- throw new InvalidFormatStringException("Invalid format string field"
- + " %${messageString[i + 1]}");
+ throw new InvalidFormatStringException("Invalid Protolog message format in "
+ + "\"" + messageString + "\" at index " + i + ".");
}
i += 2;
} else {
diff --git a/core/java/com/android/internal/statusbar/DisableStates.aidl b/core/java/com/android/internal/statusbar/DisableStates.aidl
new file mode 100644
index 000000000000..fd9882f0f7c2
--- /dev/null
+++ b/core/java/com/android/internal/statusbar/DisableStates.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2025 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.internal.statusbar;
+
+parcelable DisableStates;
diff --git a/core/java/com/android/internal/statusbar/DisableStates.java b/core/java/com/android/internal/statusbar/DisableStates.java
new file mode 100644
index 000000000000..ca2fd6c03558
--- /dev/null
+++ b/core/java/com/android/internal/statusbar/DisableStates.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2025 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.internal.statusbar;
+
+import android.app.StatusBarManager.Disable2Flags;
+import android.app.StatusBarManager.DisableFlags;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.Pair;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Holds display ids with their disable flags.
+ */
+public class DisableStates implements Parcelable {
+
+ /**
+ * A map of display IDs (integers) with corresponding disable flags.
+ */
+ public Map<Integer, Pair<@DisableFlags Integer, @Disable2Flags Integer>> displaysWithStates;
+
+ /**
+ * Whether the disable state change should be animated.
+ */
+ public boolean animate;
+
+ public DisableStates(
+ Map<Integer, Pair<@DisableFlags Integer, @Disable2Flags Integer>> displaysWithStates,
+ boolean animate) {
+ this.displaysWithStates = displaysWithStates;
+ this.animate = animate;
+ }
+
+ public DisableStates(
+ Map<Integer, Pair<@DisableFlags Integer, @Disable2Flags Integer>> displaysWithStates) {
+ this(displaysWithStates, true);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(displaysWithStates.size()); // Write the size of the map
+ for (Map.Entry<Integer, Pair<Integer, Integer>> entry : displaysWithStates.entrySet()) {
+ dest.writeInt(entry.getKey());
+ dest.writeInt(entry.getValue().first);
+ dest.writeInt(entry.getValue().second);
+ }
+ dest.writeBoolean(animate);
+ }
+
+ /**
+ * Used to make this class parcelable.
+ */
+ public static final Parcelable.Creator<DisableStates> CREATOR = new Parcelable.Creator<>() {
+ @Override
+ public DisableStates createFromParcel(Parcel source) {
+ int size = source.readInt(); // Read the size of the map
+ Map<Integer, Pair<Integer, Integer>> displaysWithStates = new HashMap<>(size);
+ for (int i = 0; i < size; i++) {
+ int key = source.readInt();
+ int first = source.readInt();
+ int second = source.readInt();
+ displaysWithStates.put(key, new Pair<>(first, second));
+ }
+ final boolean animate = source.readBoolean();
+ return new DisableStates(displaysWithStates, animate);
+ }
+
+ @Override
+ public DisableStates[] newArray(int size) {
+ return new DisableStates[size];
+ }
+ };
+}
+
diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl
index 5a180d7358dd..ce9b036f2fd7 100644
--- a/core/java/com/android/internal/statusbar/IStatusBar.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl
@@ -32,6 +32,7 @@ import android.os.UserHandle;
import android.view.KeyEvent;
import android.service.notification.StatusBarNotification;
+import com.android.internal.statusbar.DisableStates;
import com.android.internal.statusbar.IAddTileResultCallback;
import com.android.internal.statusbar.IUndoMediaTransferCallback;
import com.android.internal.statusbar.LetterboxDetails;
@@ -44,6 +45,7 @@ oneway interface IStatusBar
void setIcon(String slot, in StatusBarIcon icon);
void removeIcon(String slot);
void disable(int displayId, int state1, int state2);
+ void disableForAllDisplays(in DisableStates disableStates);
void animateExpandNotificationsPanel();
void animateExpandSettingsPanel(String subPanel);
void animateCollapsePanels();
diff --git a/core/java/com/android/internal/util/LatencyTracker.java b/core/java/com/android/internal/util/LatencyTracker.java
index 33794a59fa21..ac2c51cfe888 100644
--- a/core/java/com/android/internal/util/LatencyTracker.java
+++ b/core/java/com/android/internal/util/LatencyTracker.java
@@ -24,6 +24,7 @@ import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPOR
import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_CHECK_CREDENTIAL_UNLOCKED;
import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_DESKTOP_MODE_ENTER_APP_HANDLE_DRAG;
import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_DESKTOP_MODE_ENTER_APP_HANDLE_MENU;
+import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_DESKTOP_MODE_EXIT_MODE;
import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_EXPAND_PANEL;
import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_FACE_WAKE_AND_UNLOCK;
import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_FINGERPRINT_WAKE_AND_UNLOCK;
@@ -290,6 +291,16 @@ public class LatencyTracker {
*/
public static final int ACTION_DESKTOP_MODE_ENTER_APP_HANDLE_MENU = 31;
+ /**
+ * Time it takes for the "exit desktop" mode animation to begin after the user provides input.
+ * <p>
+ * Starts when the user provides input to exit desktop mode and enter full screen mode for an
+ * app. This including selecting the full screen button in an app handle's menu, dragging an
+ * app's window handle to the top of the screen, and using the appropriate keyboard shortcut.
+ * Ends when the animation to exit desktop mode begins.
+ */
+ public static final int ACTION_DESKTOP_MODE_EXIT_MODE = 32;
+
private static final int[] ACTIONS_ALL = {
ACTION_EXPAND_PANEL,
ACTION_TOGGLE_RECENTS,
@@ -323,6 +334,7 @@ public class LatencyTracker {
ACTION_SHADE_WINDOW_DISPLAY_CHANGE,
ACTION_DESKTOP_MODE_ENTER_APP_HANDLE_DRAG,
ACTION_DESKTOP_MODE_ENTER_APP_HANDLE_MENU,
+ ACTION_DESKTOP_MODE_EXIT_MODE,
};
/** @hide */
@@ -359,6 +371,7 @@ public class LatencyTracker {
ACTION_SHADE_WINDOW_DISPLAY_CHANGE,
ACTION_DESKTOP_MODE_ENTER_APP_HANDLE_DRAG,
ACTION_DESKTOP_MODE_ENTER_APP_HANDLE_MENU,
+ ACTION_DESKTOP_MODE_EXIT_MODE,
})
@Retention(RetentionPolicy.SOURCE)
public @interface Action {}
@@ -397,6 +410,7 @@ public class LatencyTracker {
UIACTION_LATENCY_REPORTED__ACTION__ACTION_SHADE_WINDOW_DISPLAY_CHANGE,
UIACTION_LATENCY_REPORTED__ACTION__ACTION_DESKTOP_MODE_ENTER_APP_HANDLE_DRAG,
UIACTION_LATENCY_REPORTED__ACTION__ACTION_DESKTOP_MODE_ENTER_APP_HANDLE_MENU,
+ UIACTION_LATENCY_REPORTED__ACTION__ACTION_DESKTOP_MODE_EXIT_MODE,
};
private final Object mLock = new Object();
@@ -601,6 +615,8 @@ public class LatencyTracker {
return "ACTION_DESKTOP_MODE_ENTER_APP_HANDLE_DRAG";
case UIACTION_LATENCY_REPORTED__ACTION__ACTION_DESKTOP_MODE_ENTER_APP_HANDLE_MENU:
return "ACTION_DESKTOP_MODE_ENTER_APP_HANDLE_MENU";
+ case UIACTION_LATENCY_REPORTED__ACTION__ACTION_DESKTOP_MODE_EXIT_MODE:
+ return "ACTION_DESKTOP_MODE_EXIT_MODE";
default:
throw new IllegalArgumentException("Invalid action");
}
diff --git a/core/java/com/android/internal/widget/ConversationLayout.java b/core/java/com/android/internal/widget/ConversationLayout.java
index 3da19220248b..822ef34ad925 100644
--- a/core/java/com/android/internal/widget/ConversationLayout.java
+++ b/core/java/com/android/internal/widget/ConversationLayout.java
@@ -401,9 +401,19 @@ public class ConversationLayout extends FrameLayout
@RemotableViewMethod(asyncImpl = "setIsCollapsedAsync")
public void setIsCollapsed(boolean isCollapsed) {
mIsCollapsed = isCollapsed;
- mMessagingLinearLayout.setMaxDisplayedLines(isCollapsed
- ? TextUtils.isEmpty(mSummarizedContent) ? 1 : MAX_SUMMARIZATION_LINES
- : Integer.MAX_VALUE);
+ int maxLines = Integer.MAX_VALUE;
+ if (isCollapsed) {
+ if (!TextUtils.isEmpty(mSummarizedContent)) {
+ maxLines = MAX_SUMMARIZATION_LINES;
+ } else {
+ if (android.app.Flags.nmCollapsedLines()) {
+ maxLines = 2;
+ } else {
+ maxLines = 1;
+ }
+ }
+ }
+ mMessagingLinearLayout.setMaxDisplayedLines(maxLines);
updateExpandButton();
updateContentEndPaddings();
}
@@ -1177,7 +1187,9 @@ public class ConversationLayout extends FrameLayout
nameOverride = mNameReplacement;
}
newGroup.setShowingAvatar(!mIsOneToOne && !mIsCollapsed);
- newGroup.setSingleLine(mIsCollapsed && TextUtils.isEmpty(mSummarizedContent));
+ newGroup.setSingleLine(mIsCollapsed
+ ? !android.app.Flags.nmCollapsedLines() && TextUtils.isEmpty(mSummarizedContent)
+ : false);
newGroup.setIsCollapsed(mIsCollapsed);
newGroup.setSender(sender, nameOverride);
newGroup.setSending(groupIndex == (groups.size() - 1) && showSpinner);
diff --git a/core/java/com/android/internal/widget/MessagingLayout.java b/core/java/com/android/internal/widget/MessagingLayout.java
index 4cc4b38f95a5..f9c8228e455d 100644
--- a/core/java/com/android/internal/widget/MessagingLayout.java
+++ b/core/java/com/android/internal/widget/MessagingLayout.java
@@ -157,6 +157,10 @@ public class MessagingLayout extends FrameLayout
@RemotableViewMethod(asyncImpl = "setIsCollapsedAsync")
public void setIsCollapsed(boolean isCollapsed) {
mIsCollapsed = isCollapsed;
+ if (mIsCollapsed) {
+ mMessagingLinearLayout.setMaxDisplayedLines(
+ android.app.Flags.nmCollapsedLines() ? 2 : 1);
+ }
}
/**
@@ -549,7 +553,9 @@ public class MessagingLayout extends FrameLayout
if (sender != mUser && mNameReplacement != null) {
nameOverride = mNameReplacement;
}
- newGroup.setSingleLine(mIsCollapsed && TextUtils.isEmpty(mSummarizedContent));
+ newGroup.setSingleLine(mIsCollapsed
+ ? !android.app.Flags.nmCollapsedLines() && TextUtils.isEmpty(mSummarizedContent)
+ : false);
newGroup.setShowingAvatar(!mIsCollapsed);
newGroup.setIsCollapsed(mIsCollapsed);
newGroup.setSender(sender, nameOverride);
diff --git a/core/java/com/android/internal/widget/NotificationProgressBar.java b/core/java/com/android/internal/widget/NotificationProgressBar.java
index 3472d681a486..f6e2a4df8cca 100644
--- a/core/java/com/android/internal/widget/NotificationProgressBar.java
+++ b/core/java/com/android/internal/widget/NotificationProgressBar.java
@@ -78,6 +78,13 @@ public final class NotificationProgressBar extends ProgressBar implements
@Nullable
private List<DrawablePart> mProgressDrawableParts = null;
+ /** @see R.styleable#NotificationProgressBar_segMinWidth */
+ private final float mSegMinWidth;
+ /** @see R.styleable#NotificationProgressBar_segSegGap */
+ private final float mSegSegGap;
+ /** @see R.styleable#NotificationProgressBar_segPointGap */
+ private final float mSegPointGap;
+
@Nullable
private Drawable mTracker = null;
private boolean mHasTrackerIcon = false;
@@ -128,6 +135,10 @@ public final class NotificationProgressBar extends ProgressBar implements
Log.e(TAG, "Can't get NotificationProgressDrawable", ex);
}
+ mSegMinWidth = a.getDimension(R.styleable.NotificationProgressBar_segMinWidth, 0f);
+ mSegSegGap = a.getDimension(R.styleable.NotificationProgressBar_segSegGap, 0f);
+ mSegPointGap = a.getDimension(R.styleable.NotificationProgressBar_segPointGap, 0f);
+
// Supports setting the tracker in xml, but ProgressStyle notifications set/override it
// via {@code #setProgressTrackerIcon}.
final Drawable tracker = a.getDrawable(R.styleable.NotificationProgressBar_tracker);
@@ -444,30 +455,26 @@ public final class NotificationProgressBar extends ProgressBar implements
return;
}
- final float segSegGap = mNotificationProgressDrawable.getSegSegGap();
- final float segPointGap = mNotificationProgressDrawable.getSegPointGap();
final float pointRadius = mNotificationProgressDrawable.getPointRadius();
mProgressDrawableParts = processPartsAndConvertToDrawableParts(
mParts,
width,
- segSegGap,
- segPointGap,
+ mSegSegGap,
+ mSegPointGap,
pointRadius,
mHasTrackerIcon,
mTrackerDrawWidth
);
- final float segmentMinWidth = mNotificationProgressDrawable.getSegmentMinWidth();
final float progressFraction = getProgressFraction();
final boolean isStyledByProgress = mProgressModel.isStyledByProgress();
- final float progressGap =
- mHasTrackerIcon ? 0F : mNotificationProgressDrawable.getSegSegGap();
+ final float progressGap = mHasTrackerIcon ? 0F : mSegSegGap;
Pair<List<DrawablePart>, Float> p = null;
try {
p = maybeStretchAndRescaleSegments(
mParts,
mProgressDrawableParts,
- segmentMinWidth,
+ mSegMinWidth,
pointRadius,
progressFraction,
isStyledByProgress,
@@ -492,11 +499,11 @@ public final class NotificationProgressBar extends ProgressBar implements
mProgressModel.getProgress(),
getMax(),
width,
- segSegGap,
- segPointGap,
+ mSegSegGap,
+ mSegPointGap,
pointRadius,
mHasTrackerIcon,
- segmentMinWidth,
+ mSegMinWidth,
isStyledByProgress,
mTrackerDrawWidth);
} catch (NotEnoughWidthToFitAllPartsException ex) {
@@ -521,11 +528,11 @@ public final class NotificationProgressBar extends ProgressBar implements
mProgressModel.getProgress(),
getMax(),
width,
- segSegGap,
- segPointGap,
+ mSegSegGap,
+ mSegPointGap,
pointRadius,
mHasTrackerIcon,
- segmentMinWidth,
+ mSegMinWidth,
isStyledByProgress,
mTrackerDrawWidth);
} catch (NotEnoughWidthToFitAllPartsException ex) {
diff --git a/core/java/com/android/internal/widget/NotificationProgressDrawable.java b/core/java/com/android/internal/widget/NotificationProgressDrawable.java
index b1096107f04b..32b283af29c5 100644
--- a/core/java/com/android/internal/widget/NotificationProgressDrawable.java
+++ b/core/java/com/android/internal/widget/NotificationProgressDrawable.java
@@ -84,27 +84,6 @@ public final class NotificationProgressDrawable extends Drawable {
}
/**
- * Returns the gap between two segments.
- */
- public float getSegSegGap() {
- return mState.mSegSegGap;
- }
-
- /**
- * Returns the gap between a segment and a point.
- */
- public float getSegPointGap() {
- return mState.mSegPointGap;
- }
-
- /**
- * Returns the gap between a segment and a point.
- */
- public float getSegmentMinWidth() {
- return mState.mSegmentMinWidth;
- }
-
- /**
* Returns the radius for the points.
*/
public float getPointRadius() {
@@ -241,11 +220,6 @@ public final class NotificationProgressDrawable extends Drawable {
mState.setDensity(resolveDensity(r, 0));
- final TypedArray a = obtainAttributes(r, theme, attrs,
- R.styleable.NotificationProgressDrawable);
- updateStateFromTypedArray(a);
- a.recycle();
-
inflateChildElements(r, parser, attrs, theme);
updateLocalState();
@@ -262,13 +236,6 @@ public final class NotificationProgressDrawable extends Drawable {
state.setDensity(resolveDensity(t.getResources(), 0));
- if (state.mThemeAttrs != null) {
- final TypedArray a = t.resolveAttributes(
- state.mThemeAttrs, R.styleable.NotificationProgressDrawable);
- updateStateFromTypedArray(a);
- a.recycle();
- }
-
applyThemeChildElements(t);
updateLocalState();
@@ -279,21 +246,6 @@ public final class NotificationProgressDrawable extends Drawable {
return (mState.canApplyTheme()) || super.canApplyTheme();
}
- private void updateStateFromTypedArray(TypedArray a) {
- final State state = mState;
-
- // Account for any configuration changes.
- state.mChangingConfigurations |= a.getChangingConfigurations();
-
- // Extract the theme attributes, if any.
- state.mThemeAttrs = a.extractThemeAttrs();
-
- state.mSegSegGap = a.getDimension(R.styleable.NotificationProgressDrawable_segSegGap,
- state.mSegSegGap);
- state.mSegPointGap = a.getDimension(R.styleable.NotificationProgressDrawable_segPointGap,
- state.mSegPointGap);
- }
-
private void inflateChildElements(Resources r, XmlPullParser parser, AttributeSet attrs,
Theme theme) throws XmlPullParserException, IOException {
TypedArray a;
@@ -357,8 +309,6 @@ public final class NotificationProgressDrawable extends Drawable {
// Extract the theme attributes, if any.
state.mThemeAttrsSegments = a.extractThemeAttrs();
- state.mSegmentMinWidth = a.getDimension(
- R.styleable.NotificationProgressDrawableSegments_minWidth, state.mSegmentMinWidth);
state.mSegmentHeight = a.getDimension(
R.styleable.NotificationProgressDrawableSegments_height, state.mSegmentHeight);
state.mFadedSegmentHeight = a.getDimension(
@@ -588,9 +538,6 @@ public final class NotificationProgressDrawable extends Drawable {
static final class State extends ConstantState {
@Config
int mChangingConfigurations;
- float mSegSegGap = 0.0f;
- float mSegPointGap = 0.0f;
- float mSegmentMinWidth = 0.0f;
float mSegmentHeight;
float mFadedSegmentHeight;
float mSegmentCornerRadius;
@@ -610,9 +557,6 @@ public final class NotificationProgressDrawable extends Drawable {
State(@NonNull State orig, @Nullable Resources res) {
mChangingConfigurations = orig.mChangingConfigurations;
- mSegSegGap = orig.mSegSegGap;
- mSegPointGap = orig.mSegPointGap;
- mSegmentMinWidth = orig.mSegmentMinWidth;
mSegmentHeight = orig.mSegmentHeight;
mFadedSegmentHeight = orig.mFadedSegmentHeight;
mSegmentCornerRadius = orig.mSegmentCornerRadius;
@@ -631,18 +575,6 @@ public final class NotificationProgressDrawable extends Drawable {
}
private void applyDensityScaling(int sourceDensity, int targetDensity) {
- if (mSegSegGap > 0) {
- mSegSegGap = scaleFromDensity(
- mSegSegGap, sourceDensity, targetDensity);
- }
- if (mSegPointGap > 0) {
- mSegPointGap = scaleFromDensity(
- mSegPointGap, sourceDensity, targetDensity);
- }
- if (mSegmentMinWidth > 0) {
- mSegmentMinWidth = scaleFromDensity(
- mSegmentMinWidth, sourceDensity, targetDensity);
- }
if (mSegmentHeight > 0) {
mSegmentHeight = scaleFromDensity(
mSegmentHeight, sourceDensity, targetDensity);
diff --git a/core/java/com/android/internal/widget/remotecompose/core/CoreDocument.java b/core/java/com/android/internal/widget/remotecompose/core/CoreDocument.java
index 766fbf1a80f5..d62538b6d1ed 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/CoreDocument.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/CoreDocument.java
@@ -73,7 +73,7 @@ public class CoreDocument implements Serializable {
// We also keep a more fine-grained BUILD number, exposed as
// ID_API_LEVEL = DOCUMENT_API_LEVEL + BUILD
- static final float BUILD = 0.7f;
+ static final float BUILD = 0.8f;
private static final boolean UPDATE_VARIABLES_BEFORE_LAYOUT = false;
diff --git a/core/java/com/android/internal/widget/remotecompose/core/Operations.java b/core/java/com/android/internal/widget/remotecompose/core/Operations.java
index add9d5bae552..20252366e264 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/Operations.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/Operations.java
@@ -30,6 +30,7 @@ import com.android.internal.widget.remotecompose.core.operations.DataListFloat;
import com.android.internal.widget.remotecompose.core.operations.DataListIds;
import com.android.internal.widget.remotecompose.core.operations.DataMapIds;
import com.android.internal.widget.remotecompose.core.operations.DataMapLookup;
+import com.android.internal.widget.remotecompose.core.operations.DebugMessage;
import com.android.internal.widget.remotecompose.core.operations.DrawArc;
import com.android.internal.widget.remotecompose.core.operations.DrawBitmap;
import com.android.internal.widget.remotecompose.core.operations.DrawBitmapFontText;
@@ -231,6 +232,7 @@ public class Operations {
public static final int PATH_COMBINE = 175;
public static final int HAPTIC_FEEDBACK = 177;
public static final int CONDITIONAL_OPERATIONS = 178;
+ public static final int DEBUG_MESSAGE = 179;
///////////////////////////////////////// ======================
@@ -443,6 +445,7 @@ public class Operations {
map.put(PATH_COMBINE, PathCombine::read);
map.put(HAPTIC_FEEDBACK, HapticFeedback::read);
map.put(CONDITIONAL_OPERATIONS, ConditionalOperations::read);
+ map.put(DEBUG_MESSAGE, DebugMessage::read);
// map.put(ACCESSIBILITY_CUSTOM_ACTION, CoreSemantics::read);
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeBuffer.java b/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeBuffer.java
index 1f026687680f..eb7399afd2b7 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeBuffer.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeBuffer.java
@@ -33,6 +33,7 @@ import com.android.internal.widget.remotecompose.core.operations.DataListFloat;
import com.android.internal.widget.remotecompose.core.operations.DataListIds;
import com.android.internal.widget.remotecompose.core.operations.DataMapIds;
import com.android.internal.widget.remotecompose.core.operations.DataMapLookup;
+import com.android.internal.widget.remotecompose.core.operations.DebugMessage;
import com.android.internal.widget.remotecompose.core.operations.DrawArc;
import com.android.internal.widget.remotecompose.core.operations.DrawBitmap;
import com.android.internal.widget.remotecompose.core.operations.DrawBitmapFontText;
@@ -1870,6 +1871,46 @@ public class RemoteComposeBuffer {
}
/**
+ * Add a scroll modifier
+ *
+ * @param direction HORIZONTAL(0) or VERTICAL(1)
+ * @param positionId the position id as a NaN
+ */
+ public void addModifierScroll(int direction, float positionId) {
+ float max = this.reserveFloatVariable();
+ float notchMax = this.reserveFloatVariable();
+ float touchExpressionDirection =
+ direction != 0 ? RemoteContext.FLOAT_TOUCH_POS_X : RemoteContext.FLOAT_TOUCH_POS_Y;
+
+ ScrollModifierOperation.apply(mBuffer, direction, positionId, max, notchMax);
+ this.addTouchExpression(
+ positionId,
+ 0f,
+ 0f,
+ max,
+ 0f,
+ 3,
+ new float[] {
+ touchExpressionDirection, -1, MUL,
+ },
+ TouchExpression.STOP_GENTLY,
+ null,
+ null);
+ ContainerEnd.apply(mBuffer);
+ }
+
+ /**
+ * Add a scroll modifier
+ *
+ * @param direction HORIZONTAL(0) or VERTICAL(1)
+ */
+ public void addModifierScroll(int direction) {
+ float max = this.reserveFloatVariable();
+ ScrollModifierOperation.apply(mBuffer, direction, 0f, max, 0f);
+ ContainerEnd.apply(mBuffer);
+ }
+
+ /**
* Add a background modifier of provided color
*
* @param color the color of the background
@@ -2464,4 +2505,15 @@ public class RemoteComposeBuffer {
public void addConditionalOperations(byte type, float a, float b) {
ConditionalOperations.apply(mBuffer, type, a, b);
}
+
+ /**
+ * Add a debug message
+ *
+ * @param textId text id
+ * @param value value
+ * @param flags flags
+ */
+ public void addDebugMessage(int textId, float value, int flags) {
+ DebugMessage.apply(mBuffer, textId, value, flags);
+ }
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DebugMessage.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DebugMessage.java
new file mode 100644
index 000000000000..c27bd8b577bf
--- /dev/null
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DebugMessage.java
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.widget.remotecompose.core.operations;
+
+import android.annotation.NonNull;
+
+import com.android.internal.widget.remotecompose.core.Operation;
+import com.android.internal.widget.remotecompose.core.Operations;
+import com.android.internal.widget.remotecompose.core.RemoteContext;
+import com.android.internal.widget.remotecompose.core.VariableSupport;
+import com.android.internal.widget.remotecompose.core.WireBuffer;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder;
+import com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation;
+
+import java.util.List;
+
+/**
+ * This prints debugging message useful for debugging. It should not be use in production documents
+ */
+public class DebugMessage extends Operation implements VariableSupport {
+ private static final int OP_CODE = Operations.DEBUG_MESSAGE;
+ private static final String CLASS_NAME = "DebugMessage";
+ int mTextID;
+ float mFloatValue;
+ float mOutFloatValue;
+ int mFlags = 0;
+
+ public DebugMessage(int textID, float value, int flags) {
+ mTextID = textID;
+ mFloatValue = value;
+ mFlags = flags;
+ }
+
+ @Override
+ public void updateVariables(@NonNull RemoteContext context) {
+ System.out.println("Debug message : updateVariables ");
+ mOutFloatValue =
+ Float.isNaN(mFloatValue)
+ ? context.getFloat(Utils.idFromNan(mFloatValue))
+ : mFloatValue;
+ System.out.println(
+ "Debug message : updateVariables "
+ + Utils.floatToString(mFloatValue, mOutFloatValue));
+ }
+
+ @Override
+ public void registerListening(@NonNull RemoteContext context) {
+ System.out.println("Debug message : registerListening ");
+
+ if (Float.isNaN(mFloatValue)) {
+ System.out.println("Debug message : registerListening " + mFloatValue);
+ context.listensTo(Utils.idFromNan(mFloatValue), this);
+ }
+ }
+
+ @Override
+ public void write(@NonNull WireBuffer buffer) {
+ apply(buffer, mTextID, mFloatValue, mFlags);
+ }
+
+ @NonNull
+ @Override
+ public String toString() {
+ return "DebugMessage "
+ + mTextID
+ + ", "
+ + Utils.floatToString(mFloatValue, mOutFloatValue)
+ + ", "
+ + mFlags;
+ }
+
+ /**
+ * Read this operation and add it to the list of operations
+ *
+ * @param buffer the buffer to read
+ * @param operations the list of operations that will be added to
+ */
+ public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) {
+ int text = buffer.readInt();
+ float floatValue = buffer.readFloat();
+ int flags = buffer.readInt();
+ DebugMessage op = new DebugMessage(text, floatValue, flags);
+ operations.add(op);
+ }
+
+ /**
+ * The name of the class
+ *
+ * @return the name
+ */
+ @NonNull
+ public static String name() {
+ return CLASS_NAME;
+ }
+
+ /**
+ * The OP_CODE for this command
+ *
+ * @return the opcode
+ */
+ public static int id() {
+ return OP_CODE;
+ }
+
+ /**
+ * Writes out the operation to the buffer
+ *
+ * @param buffer write the command to the buffer
+ * @param textID id of the text
+ * @param value value to print
+ * @param flags flags to print
+ */
+ public static void apply(@NonNull WireBuffer buffer, int textID, float value, int flags) {
+ buffer.start(OP_CODE);
+ buffer.writeInt(textID);
+ buffer.writeFloat(value);
+ buffer.writeInt(flags);
+ }
+
+ /**
+ * Populate the documentation with a description of this operation
+ *
+ * @param doc to append the description to.
+ */
+ public static void documentation(@NonNull DocumentationBuilder doc) {
+ doc.operation("DebugMessage Operations", id(), CLASS_NAME)
+ .description("Print debugging messages")
+ .field(DocumentedOperation.INT, "textId", "test to print")
+ .field(DocumentedOperation.FLOAT, "value", "value of a float to print")
+ .field(DocumentedOperation.INT, "flags", "print additional information");
+ }
+
+ @Override
+ public void apply(@NonNull RemoteContext context) {
+ String str = context.getText(mTextID);
+ System.out.println("Debug message : " + str + " " + mOutFloatValue + " " + mFlags);
+ }
+
+ @NonNull
+ @Override
+ public String deepToString(@NonNull String indent) {
+ return indent + toString();
+ }
+}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/TimeAttribute.java b/core/java/com/android/internal/widget/remotecompose/core/operations/TimeAttribute.java
index dee79a45de3d..67d3a6584897 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/TimeAttribute.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/TimeAttribute.java
@@ -266,10 +266,12 @@ public class TimeAttribute extends PaintOperation {
case TIME_FROM_NOW_SEC:
case TIME_FROM_ARG_SEC:
ctx.loadFloat(mId, (delta) * 1E-3f);
+ ctx.needsRepaint();
break;
case TIME_FROM_ARG_MIN:
case TIME_FROM_NOW_MIN:
ctx.loadFloat(mId, (float) (delta * 1E-3 / 60));
+ ctx.needsRepaint();
break;
case TIME_FROM_ARG_HR:
case TIME_FROM_NOW_HR:
@@ -298,6 +300,7 @@ public class TimeAttribute extends PaintOperation {
break;
case TIME_FROM_LOAD_SEC:
ctx.loadFloat(mId, (value - load_time) * 1E-3f);
+ ctx.needsRepaint();
break;
}
}
diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/TouchExpression.java b/core/java/com/android/internal/widget/remotecompose/core/operations/TouchExpression.java
index f24672922367..3e5dff8ad277 100644
--- a/core/java/com/android/internal/widget/remotecompose/core/operations/TouchExpression.java
+++ b/core/java/com/android/internal/widget/remotecompose/core/operations/TouchExpression.java
@@ -494,7 +494,7 @@ public class TouchExpression extends Operation
mTouchUpTime = context.getAnimationTime();
float dest = getStopPosition(value, slope);
- float time = mMaxTime * Math.abs(dest - value) / (2 * mMaxVelocity);
+ float time = Math.min(2, mMaxTime * Math.abs(dest - value) / (2 * mMaxVelocity));
mEasyTouch.config(value, dest, slope, time, mMaxAcceleration, mMaxVelocity, null);
mEasingToStop = true;
context.needsRepaint();
diff --git a/core/java/com/android/internal/widget/remotecompose/player/platform/RemoteComposeCanvas.java b/core/java/com/android/internal/widget/remotecompose/player/platform/RemoteComposeCanvas.java
index 17f4fc82af5f..e76fb0654df6 100644
--- a/core/java/com/android/internal/widget/remotecompose/player/platform/RemoteComposeCanvas.java
+++ b/core/java/com/android/internal/widget/remotecompose/player/platform/RemoteComposeCanvas.java
@@ -42,6 +42,9 @@ import java.util.Set;
public class RemoteComposeCanvas extends FrameLayout implements View.OnAttachStateChangeListener {
static final boolean USE_VIEW_AREA_CLICK = true; // Use views to represent click areas
+ static final float DEFAULT_FRAME_RATE = 60f;
+ static final float POST_TO_NEXT_FRAME_THRESHOLD = 60f;
+
RemoteComposeDocument mDocument = null;
int mTheme = Theme.LIGHT;
boolean mInActionDown = false;
@@ -53,9 +56,11 @@ public class RemoteComposeCanvas extends FrameLayout implements View.OnAttachSta
long mStart = System.nanoTime();
long mLastFrameDelay = 1;
- float mMaxFrameRate = 60f; // frames per seconds
+ float mMaxFrameRate = DEFAULT_FRAME_RATE; // frames per seconds
long mMaxFrameDelay = (long) (1000 / mMaxFrameRate);
+ long mLastFrameCall = System.currentTimeMillis();
+
private Choreographer mChoreographer;
private Choreographer.FrameCallback mFrameCallback =
new Choreographer.FrameCallback() {
@@ -100,6 +105,7 @@ public class RemoteComposeCanvas extends FrameLayout implements View.OnAttachSta
public void setDocument(RemoteComposeDocument value) {
mDocument = value;
+ mMaxFrameRate = DEFAULT_FRAME_RATE;
mDocument.initializeContext(mARContext);
mDisable = false;
mARContext.setDocLoadTime();
@@ -546,8 +552,25 @@ public class RemoteComposeCanvas extends FrameLayout implements View.OnAttachSta
}
int nextFrame = mDocument.needsRepaint();
if (nextFrame > 0) {
- mLastFrameDelay = Math.max(mMaxFrameDelay, nextFrame);
+ if (mMaxFrameRate >= POST_TO_NEXT_FRAME_THRESHOLD) {
+ mLastFrameDelay = nextFrame;
+ } else {
+ mLastFrameDelay = Math.max(mMaxFrameDelay, nextFrame);
+ }
if (mChoreographer != null) {
+ if (mDebug == 1) {
+ System.err.println(
+ "RC : POST CHOREOGRAPHER WITH "
+ + mLastFrameDelay
+ + " (nextFrame was "
+ + nextFrame
+ + ", max delay "
+ + mMaxFrameDelay
+ + ", "
+ + " max framerate is "
+ + mMaxFrameRate
+ + ")");
+ }
mChoreographer.postFrameCallbackDelayed(mFrameCallback, mLastFrameDelay);
}
if (!mARContext.useChoreographer()) {
@@ -567,6 +590,16 @@ public class RemoteComposeCanvas extends FrameLayout implements View.OnAttachSta
mDisable = true;
invalidate();
}
+ if (mDebug == 1) {
+ long frameDelay = System.currentTimeMillis() - mLastFrameCall;
+ System.err.println(
+ "RC : Delay since last frame "
+ + frameDelay
+ + " ms ("
+ + (1000f / (float) frameDelay)
+ + " fps)");
+ mLastFrameCall = System.currentTimeMillis();
+ }
}
private void drawDisable(Canvas canvas) {
diff --git a/core/java/com/android/server/servicewatcher/OWNERS b/core/java/com/android/server/servicewatcher/OWNERS
index ced619f05f1d..dbc598e1547f 100644
--- a/core/java/com/android/server/servicewatcher/OWNERS
+++ b/core/java/com/android/server/servicewatcher/OWNERS
@@ -1,5 +1,5 @@
# Bug component: 25692
-sooniln@google.com
+dnchrist@google.com
wyattriley@google.com
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index bfa0aa9638a9..7ed73d7668b9 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -210,7 +210,6 @@ cc_library_shared_for_libandroid_runtime {
"android_media_AudioAttributes.cpp",
"android_media_AudioProductStrategies.cpp",
"android_media_AudioVolumeGroups.cpp",
- "android_media_AudioVolumeGroupCallback.cpp",
"android_media_DeviceCallback.cpp",
"android_media_MediaMetricsJNI.cpp",
"android_media_MicrophoneInfo.cpp",
@@ -311,6 +310,7 @@ cc_library_shared_for_libandroid_runtime {
"audioflinger-aidl-cpp",
"audiopolicy-types-aidl-cpp",
"spatializer-aidl-cpp",
+ "volumegroupcallback-aidl-cpp",
"av-types-aidl-cpp",
"android.hardware.camera.device@3.2",
"camera_platform_flags_c_lib",
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index b2b826391e1d..1ff07745e904 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -101,7 +101,6 @@ extern int register_android_media_AudioTrack(JNIEnv *env);
extern int register_android_media_AudioAttributes(JNIEnv *env);
extern int register_android_media_AudioProductStrategies(JNIEnv *env);
extern int register_android_media_AudioVolumeGroups(JNIEnv *env);
-extern int register_android_media_AudioVolumeGroupChangeHandler(JNIEnv *env);
extern int register_android_media_ImageReader(JNIEnv *env);
extern int register_android_media_ImageWriter(JNIEnv *env);
extern int register_android_media_MicrophoneInfo(JNIEnv *env);
@@ -1660,7 +1659,6 @@ static const RegJNIRec gRegJNI[] = {
REG_JNI(register_android_media_AudioAttributes),
REG_JNI(register_android_media_AudioProductStrategies),
REG_JNI(register_android_media_AudioVolumeGroups),
- REG_JNI(register_android_media_AudioVolumeGroupChangeHandler),
REG_JNI(register_android_media_ImageReader),
REG_JNI(register_android_media_ImageWriter),
REG_JNI(register_android_media_MediaMetrics),
diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp
index b679688959b1..1bbf811dc373 100644
--- a/core/jni/android_media_AudioSystem.cpp
+++ b/core/jni/android_media_AudioSystem.cpp
@@ -20,16 +20,17 @@
#include <atomic>
#define LOG_TAG "AudioSystem-JNI"
+#include <android-base/properties.h>
#include <android/binder_ibinder_jni.h>
#include <android/binder_libbinder.h>
#include <android/media/AudioVibratorInfo.h>
+#include <android/media/INativeAudioVolumeGroupCallback.h>
#include <android/media/INativeSpatializerCallback.h>
#include <android/media/ISpatializer.h>
#include <android/media/audio/common/AudioConfigBase.h>
#include <android_media_audiopolicy.h>
#include <android_os_Parcel.h>
#include <audiomanager/AudioManager.h>
-#include <android-base/properties.h>
#include <binder/IBinder.h>
#include <jni.h>
#include <media/AidlConversion.h>
@@ -41,14 +42,14 @@
#include <nativehelper/ScopedLocalRef.h>
#include <nativehelper/ScopedPrimitiveArray.h>
#include <nativehelper/jni_macros.h>
+#include <sys/system_properties.h>
#include <system/audio.h>
#include <system/audio_policy.h>
-#include <sys/system_properties.h>
#include <utils/Log.h>
+#include <memory>
#include <optional>
#include <sstream>
-#include <memory>
#include <vector>
#include "android_media_AudioAttributes.h"
@@ -59,8 +60,8 @@
#include "android_media_AudioFormat.h"
#include "android_media_AudioMixerAttributes.h"
#include "android_media_AudioProfile.h"
-#include "android_media_MicrophoneInfo.h"
#include "android_media_JNIUtils.h"
+#include "android_media_MicrophoneInfo.h"
#include "android_util_Binder.h"
#include "core_jni_helpers.h"
@@ -3442,6 +3443,21 @@ static void android_media_AudioSystem_triggerSystemPropertyUpdate(JNIEnv *env,
}
}
+static int android_media_AudioSystem_registerAudioVolumeGroupCallback(
+ JNIEnv *env, jobject thiz, jobject jIAudioVolumeGroupCallback) {
+ sp<media::INativeAudioVolumeGroupCallback> nIAudioVolumeGroupCallback =
+ interface_cast<media::INativeAudioVolumeGroupCallback>(
+ ibinderForJavaObject(env, jIAudioVolumeGroupCallback));
+ return AudioSystem::addAudioVolumeGroupCallback(nIAudioVolumeGroupCallback);
+}
+
+static int android_media_AudioSystem_unregisterAudioVolumeGroupCallback(
+ JNIEnv *env, jobject thiz, jobject jIAudioVolumeGroupCallback) {
+ sp<media::INativeAudioVolumeGroupCallback> nIAudioVolumeGroupCallback =
+ interface_cast<media::INativeAudioVolumeGroupCallback>(
+ ibinderForJavaObject(env, jIAudioVolumeGroupCallback));
+ return AudioSystem::removeAudioVolumeGroupCallback(nIAudioVolumeGroupCallback);
+}
// ----------------------------------------------------------------------------
@@ -3612,6 +3628,12 @@ static const JNINativeMethod gMethods[] = {
MAKE_JNI_NATIVE_METHOD("clearPreferredMixerAttributes",
"(Landroid/media/AudioAttributes;II)I",
android_media_AudioSystem_clearPreferredMixerAttributes),
+ MAKE_JNI_NATIVE_METHOD("registerAudioVolumeGroupCallback",
+ "(Landroid/media/INativeAudioVolumeGroupCallback;)I",
+ android_media_AudioSystem_registerAudioVolumeGroupCallback),
+ MAKE_JNI_NATIVE_METHOD("unregisterAudioVolumeGroupCallback",
+ "(Landroid/media/INativeAudioVolumeGroupCallback;)I",
+ android_media_AudioSystem_unregisterAudioVolumeGroupCallback),
MAKE_AUDIO_SYSTEM_METHOD(supportsBluetoothVariableLatency),
MAKE_AUDIO_SYSTEM_METHOD(setBluetoothVariableLatencyEnabled),
MAKE_AUDIO_SYSTEM_METHOD(isBluetoothVariableLatencyEnabled),
diff --git a/core/jni/android_media_AudioVolumeGroupCallback.cpp b/core/jni/android_media_AudioVolumeGroupCallback.cpp
deleted file mode 100644
index d130a4bc68fa..000000000000
--- a/core/jni/android_media_AudioVolumeGroupCallback.cpp
+++ /dev/null
@@ -1,176 +0,0 @@
-/*
- * Copyright (C) 2018 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.
- */
-#undef ANDROID_UTILS_REF_BASE_DISABLE_IMPLICIT_CONSTRUCTION // TODO:remove this and fix code
-
-//#define LOG_NDEBUG 0
-
-#define LOG_TAG "AudioVolumeGroupCallback-JNI"
-
-#include <utils/Log.h>
-#include <nativehelper/JNIHelp.h>
-#include "core_jni_helpers.h"
-
-#include "android_media_AudioVolumeGroupCallback.h"
-
-
-// ----------------------------------------------------------------------------
-using namespace android;
-
-static const char* const kAudioVolumeGroupChangeHandlerClassPathName =
- "android/media/audiopolicy/AudioVolumeGroupChangeHandler";
-
-static struct {
- jfieldID mJniCallback;
-} gAudioVolumeGroupChangeHandlerFields;
-
-static struct {
- jmethodID postEventFromNative;
-} gAudioVolumeGroupChangeHandlerMethods;
-
-static Mutex gLock;
-
-JNIAudioVolumeGroupCallback::JNIAudioVolumeGroupCallback(JNIEnv* env,
- jobject thiz,
- jobject weak_thiz)
-{
- jclass clazz = env->GetObjectClass(thiz);
- if (clazz == NULL) {
- ALOGE("Can't find class %s", kAudioVolumeGroupChangeHandlerClassPathName);
- return;
- }
- mClass = (jclass)env->NewGlobalRef(clazz);
-
- // We use a weak reference so the AudioVolumeGroupChangeHandler object can be garbage collected.
- // The reference is only used as a proxy for callbacks.
- mObject = env->NewGlobalRef(weak_thiz);
-}
-
-JNIAudioVolumeGroupCallback::~JNIAudioVolumeGroupCallback()
-{
- // remove global references
- JNIEnv *env = AndroidRuntime::getJNIEnv();
- if (env == NULL) {
- return;
- }
- env->DeleteGlobalRef(mObject);
- env->DeleteGlobalRef(mClass);
-}
-
-void JNIAudioVolumeGroupCallback::onAudioVolumeGroupChanged(volume_group_t group, int flags)
-{
- JNIEnv *env = AndroidRuntime::getJNIEnv();
- if (env == NULL) {
- return;
- }
- ALOGV("%s volume group id %d", __FUNCTION__, group);
- env->CallStaticVoidMethod(mClass,
- gAudioVolumeGroupChangeHandlerMethods.postEventFromNative,
- mObject,
- AUDIOVOLUMEGROUP_EVENT_VOLUME_CHANGED, group, flags, NULL);
- if (env->ExceptionCheck()) {
- ALOGW("An exception occurred while notifying an event.");
- env->ExceptionClear();
- }
-}
-
-void JNIAudioVolumeGroupCallback::onServiceDied()
-{
- JNIEnv *env = AndroidRuntime::getJNIEnv();
- if (env == NULL) {
- return;
- }
- env->CallStaticVoidMethod(mClass,
- gAudioVolumeGroupChangeHandlerMethods.postEventFromNative,
- mObject,
- AUDIOVOLUMEGROUP_EVENT_SERVICE_DIED, 0, 0, NULL);
- if (env->ExceptionCheck()) {
- ALOGW("An exception occurred while notifying an event.");
- env->ExceptionClear();
- }
-}
-
-static
-sp<JNIAudioVolumeGroupCallback> setJniCallback(JNIEnv* env,
- jobject thiz,
- const sp<JNIAudioVolumeGroupCallback>& callback)
-{
- Mutex::Autolock l(gLock);
- sp<JNIAudioVolumeGroupCallback> old = (JNIAudioVolumeGroupCallback*)env->GetLongField(
- thiz, gAudioVolumeGroupChangeHandlerFields.mJniCallback);
- if (callback.get()) {
- callback->incStrong((void*)setJniCallback);
- }
- if (old != 0) {
- old->decStrong((void*)setJniCallback);
- }
- env->SetLongField(thiz, gAudioVolumeGroupChangeHandlerFields.mJniCallback,
- (jlong)callback.get());
- return old;
-}
-
-static void
-android_media_AudioVolumeGroupChangeHandler_eventHandlerSetup(JNIEnv *env,
- jobject thiz,
- jobject weak_this)
-{
- ALOGV("%s", __FUNCTION__);
- sp<JNIAudioVolumeGroupCallback> callback =
- new JNIAudioVolumeGroupCallback(env, thiz, weak_this);
-
- if (AudioSystem::addAudioVolumeGroupCallback(callback) == NO_ERROR) {
- setJniCallback(env, thiz, callback);
- }
-}
-
-static void
-android_media_AudioVolumeGroupChangeHandler_eventHandlerFinalize(JNIEnv *env, jobject thiz)
-{
- ALOGV("%s", __FUNCTION__);
- sp<JNIAudioVolumeGroupCallback> callback = setJniCallback(env, thiz, 0);
- if (callback != 0) {
- AudioSystem::removeAudioVolumeGroupCallback(callback);
- }
-}
-
-/*
- * JNI registration.
- */
-static const JNINativeMethod gMethods[] = {
- {"native_setup", "(Ljava/lang/Object;)V",
- (void *)android_media_AudioVolumeGroupChangeHandler_eventHandlerSetup},
- {"native_finalize", "()V",
- (void *)android_media_AudioVolumeGroupChangeHandler_eventHandlerFinalize},
-};
-
-int register_android_media_AudioVolumeGroupChangeHandler(JNIEnv *env)
-{
- jclass audioVolumeGroupChangeHandlerClass =
- FindClassOrDie(env, kAudioVolumeGroupChangeHandlerClassPathName);
- gAudioVolumeGroupChangeHandlerMethods.postEventFromNative =
- GetStaticMethodIDOrDie(env, audioVolumeGroupChangeHandlerClass, "postEventFromNative",
- "(Ljava/lang/Object;IIILjava/lang/Object;)V");
-
- gAudioVolumeGroupChangeHandlerFields.mJniCallback =
- GetFieldIDOrDie(env, audioVolumeGroupChangeHandlerClass, "mJniCallback", "J");
-
- env->DeleteLocalRef(audioVolumeGroupChangeHandlerClass);
-
- return RegisterMethodsOrDie(env,
- kAudioVolumeGroupChangeHandlerClassPathName,
- gMethods,
- NELEM(gMethods));
-}
-
diff --git a/core/jni/android_media_AudioVolumeGroupCallback.h b/core/jni/android_media_AudioVolumeGroupCallback.h
deleted file mode 100644
index de06549621b9..000000000000
--- a/core/jni/android_media_AudioVolumeGroupCallback.h
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright (C) 2018 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.
- */
-
-#pragma once
-
-#include <system/audio.h>
-#include <media/AudioSystem.h>
-
-namespace android {
-
-// keep in sync with AudioManager.AudioVolumeGroupChangeHandler.java
-#define AUDIOVOLUMEGROUP_EVENT_VOLUME_CHANGED 1000
-#define AUDIOVOLUMEGROUP_EVENT_SERVICE_DIED 1001
-
-class JNIAudioVolumeGroupCallback: public AudioSystem::AudioVolumeGroupCallback
-{
-public:
- JNIAudioVolumeGroupCallback(JNIEnv* env, jobject thiz, jobject weak_thiz);
- ~JNIAudioVolumeGroupCallback();
-
- void onAudioVolumeGroupChanged(volume_group_t group, int flags) override;
- void onServiceDied() override;
-
-private:
- void sendEvent(int event);
-
- jclass mClass; /**< Reference to AudioVolumeGroupChangeHandler class. */
- jobject mObject; /**< Weak ref to AudioVolumeGroupChangeHandler object to call on. */
-};
-
-} // namespace android
diff --git a/core/jni/android_util_Binder.cpp b/core/jni/android_util_Binder.cpp
index a0c8f30c9356..36bda61c94a6 100644
--- a/core/jni/android_util_Binder.cpp
+++ b/core/jni/android_util_Binder.cpp
@@ -73,6 +73,7 @@ static struct bindernative_offsets_t
jmethodID mExecTransact;
jmethodID mGetInterfaceDescriptor;
jmethodID mTransactionCallback;
+ jmethodID mGetExtension;
// Object state.
jfieldID mObject;
@@ -488,8 +489,12 @@ public:
if (mVintf) {
::android::internal::Stability::markVintf(b.get());
}
- if (mExtension != nullptr) {
- b.get()->setExtension(mExtension);
+ if (mSetExtensionCalled) {
+ jobject javaIBinderObject = env->CallObjectMethod(obj, gBinderOffsets.mGetExtension);
+ sp<IBinder> extensionFromJava = ibinderForJavaObject(env, javaIBinderObject);
+ if (extensionFromJava != nullptr) {
+ b.get()->setExtension(extensionFromJava);
+ }
}
mBinder = b;
ALOGV("Creating JavaBinder %p (refs %p) for Object %p, weakCount=%" PRId32 "\n",
@@ -515,21 +520,12 @@ public:
mVintf = false;
}
- sp<IBinder> getExtension() {
- AutoMutex _l(mLock);
- sp<JavaBBinder> b = mBinder.promote();
- if (b != nullptr) {
- return b.get()->getExtension();
- }
- return mExtension;
- }
-
void setExtension(const sp<IBinder>& extension) {
AutoMutex _l(mLock);
- mExtension = extension;
+ mSetExtensionCalled = true;
sp<JavaBBinder> b = mBinder.promote();
if (b != nullptr) {
- b.get()->setExtension(mExtension);
+ b.get()->setExtension(extension);
}
}
@@ -541,8 +537,7 @@ private:
// is too much binder state here, we can think about making JavaBBinder an
// sp here (avoid recreating it)
bool mVintf = false;
-
- sp<IBinder> mExtension;
+ bool mSetExtensionCalled = false;
};
// ----------------------------------------------------------------------------
@@ -1254,10 +1249,6 @@ static void android_os_Binder_blockUntilThreadAvailable(JNIEnv* env, jobject cla
return IPCThreadState::self()->blockUntilThreadAvailable();
}
-static jobject android_os_Binder_getExtension(JNIEnv* env, jobject obj) {
- JavaBBinderHolder* jbh = (JavaBBinderHolder*) env->GetLongField(obj, gBinderOffsets.mObject);
- return javaObjectForIBinder(env, jbh->getExtension());
-}
static void android_os_Binder_setExtension(JNIEnv* env, jobject obj, jobject extensionObject) {
JavaBBinderHolder* jbh = (JavaBBinderHolder*) env->GetLongField(obj, gBinderOffsets.mObject);
@@ -1300,8 +1291,7 @@ static const JNINativeMethod gBinderMethods[] = {
{ "getNativeBBinderHolder", "()J", (void*)android_os_Binder_getNativeBBinderHolder },
{ "getNativeFinalizer", "()J", (void*)android_os_Binder_getNativeFinalizer },
{ "blockUntilThreadAvailable", "()V", (void*)android_os_Binder_blockUntilThreadAvailable },
- { "getExtension", "()Landroid/os/IBinder;", (void*)android_os_Binder_getExtension },
- { "setExtension", "(Landroid/os/IBinder;)V", (void*)android_os_Binder_setExtension },
+ { "setExtensionNative", "(Landroid/os/IBinder;)V", (void*)android_os_Binder_setExtension },
};
// clang-format on
@@ -1318,6 +1308,8 @@ static int int_register_android_os_Binder(JNIEnv* env)
gBinderOffsets.mTransactionCallback =
GetStaticMethodIDOrDie(env, clazz, "transactionCallback", "(IIII)V");
gBinderOffsets.mObject = GetFieldIDOrDie(env, clazz, "mObject", "J");
+ gBinderOffsets.mGetExtension = GetMethodIDOrDie(env, clazz, "getExtension",
+ "()Landroid/os/IBinder;");
return RegisterMethodsOrDie(
env, kBinderPathName,
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index e16ce9849ff2..9e0200481421 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -5394,13 +5394,13 @@
corresponding permission such as {@link #HEAD_TRACKING} or
{@link #FACE_TRACKING} for the data being accessed.
- <p>Protection level: normal|appop
+ <p>Protection level: signature|privileged
@SystemApi
@FlaggedApi(android.xr.Flags.FLAG_XR_MANIFEST_ENTRIES)
@hide -->
<permission android:name="android.permission.XR_TRACKING_IN_BACKGROUND"
- android:protectionLevel="normal|appop"
+ android:protectionLevel="signature|privileged"
android:description="@string/permdesc_xr_tracking_in_background"
android:label="@string/permlab_xr_tracking_in_background"
android:featureFlag="android.xr.xr_manifest_entries" />
diff --git a/core/res/res/drawable-w192dp/loader_horizontal_watch.xml b/core/res/res/drawable-w192dp/loader_horizontal_watch.xml
new file mode 100644
index 000000000000..18cea6e0d87d
--- /dev/null
+++ b/core/res/res/drawable-w192dp/loader_horizontal_watch.xml
@@ -0,0 +1,97 @@
+<!--
+ ~ Copyright (C) 2025 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.
+ -->
+
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android" xmlns:aapt="http://schemas.android.com/aapt">
+ <aapt:attr name="android:drawable">
+ <vector android:height="15dp" android:width="67dp" android:viewportHeight="15" android:viewportWidth="67">
+ <group android:name="_R_G">
+ <group android:name="_R_G_L_1_G" android:translateX="33.5" android:translateY="7.5">
+ <path android:name="_R_G_L_1_G_D_0_P_0" android:fillColor="#000000" android:fillAlpha="1" android:fillType="nonZero" android:pathData=" M33.5 -7.5 C33.5,-7.5 33.5,7.5 33.5,7.5 C33.5,7.5 -33.5,7.5 -33.5,7.5 C-33.5,7.5 -33.5,-7.5 -33.5,-7.5 C-33.5,-7.5 33.5,-7.5 33.5,-7.5c "/>
+ </group>
+ <group android:name="_R_G_L_0_G" android:translateX="-296.5" android:translateY="-62.5" android:pivotX="330" android:pivotY="70" android:scaleX="0.1" android:scaleY="0.1">
+ <group android:name="_R_G_L_0_G_L_6_G" android:translateX="-224.84700000000004" android:translateY="-321.948" android:pivotX="555.09" android:pivotY="-329" android:rotation="28.9" android:scaleY="0">
+ <path android:name="_R_G_L_0_G_L_6_G_D_0_P_0" android:fillColor="#303030" android:fillAlpha="1" android:fillType="nonZero" android:pathData=" M194.88 359 C190,359 185.05,357.81 180.48,355.3 C59.86,289.14 -41.55,191.9 -112.79,74.11 C-186.14,-47.16 -224.91,-186.55 -224.91,-329 C-224.91,-345.57 -211.48,-359 -194.91,-359 C-178.34,-359 -164.91,-345.57 -164.91,-329 C-164.91,-197.5 -129.13,-68.84 -61.45,43.06 C4.33,151.82 97.97,241.6 209.33,302.69 C223.86,310.66 229.18,328.9 221.21,343.42 C215.75,353.37 205.48,359 194.88,359c "/>
+ </group>
+ <group android:name="_R_G_L_0_G_L_5_G" android:translateX="744.323" android:translateY="-277.96299999999997" android:pivotX="-414.08" android:pivotY="-372.985" android:rotation="28.9">
+ <path android:name="_R_G_L_0_G_L_5_G_D_0_P_0" android:fillColor="#303030" android:fillAlpha="1" android:fillType="nonZero" android:pathData=" M-335.95 402.99 C-351.13,402.99 -364.16,391.5 -365.76,376.07 C-367.46,359.59 -355.49,344.85 -339.01,343.14 C-162.93,324.91 -0.15,242.33 119.34,110.62 C239.66,-22.01 305.92,-193.76 305.92,-372.98 C305.92,-389.55 319.35,-402.98 335.92,-402.98 C352.49,-402.98 365.92,-389.55 365.92,-372.98 C365.92,-178.82 294.13,7.24 163.78,150.93 C34.34,293.61 -142.03,383.07 -332.83,402.82 C-333.88,402.93 -334.92,402.99 -335.95,402.99c "/>
+ </group>
+ <group android:name="_R_G_L_0_G_L_2_G" android:translateX="185.385" android:translateY="70.09100000000001" android:pivotX="144.858" android:pivotY="-721.039" android:rotation="28.9" android:scaleY="0">
+ <path android:name="_R_G_L_0_G_L_2_G_D_0_P_0" android:fillColor="#ffffff" android:fillAlpha="1" android:fillType="nonZero" android:pathData=" M144.62 58.96 C144.61,58.96 144.61,58.96 144.6,58.96 C40.39,58.93 -60.82,38.66 -156.19,-1.28 C-171.48,-7.68 -178.68,-25.26 -172.28,-40.54 C-165.88,-55.82 -148.3,-63.02 -133.02,-56.62 C-45.02,-19.77 48.4,-1.07 144.63,-1.04 C161.19,-1.03 174.62,12.4 174.62,28.97 C174.61,45.53 161.18,58.96 144.62,58.96c "/>
+ </group>
+ <group android:name="_R_G_L_0_G_L_0_G" android:translateX="330" android:translateY="70">
+ <path android:name="_R_G_L_0_G_L_0_G_D_0_P_0" android:fillColor="#000000" android:fillAlpha="1" android:fillType="nonZero" android:pathData=" M-660 -313 C-660,-313 -660,313 -660,313 C-660,313 660,313 660,313 C660,313 660,-313 660,-313 C660,-313 -660,-313 -660,-313c M300.74 -1.16 C205.46,38.62 103.22,59.09 -0.03,59.05 C-103.28,59.01 -205.51,38.48 -300.76,-1.37 C-316.05,-7.76 -323.26,-25.34 -316.86,-40.62 C-310.47,-55.91 -292.9,-63.12 -277.61,-56.72 C-189.68,-19.94 -95.32,-0.98 -0.01,-0.95 C95.3,-0.92 189.67,-19.81 277.63,-56.53 C292.92,-62.91 310.49,-55.69 316.87,-40.4 C323.25,-25.11 316.03,-7.54 300.74,-1.16c "/>
+ </group>
+ </group>
+ </group>
+ <group android:name="time_group"/>
+ </vector>
+ </aapt:attr>
+ <target android:name="_R_G_L_0_G_L_6_G">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:repeatCount="infinite" android:propertyName="rotation" android:duration="983" android:startOffset="0" android:valueFrom="28.9" android:valueTo="-51.4" android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.402,0.135 0.202,0.848 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G_L_5_G">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:repeatCount="infinite" android:propertyName="rotation" android:duration="983" android:startOffset="0" android:valueFrom="28.9" android:valueTo="-51.4" android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.402,0.135 0.202,0.848 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G_L_5_G">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:repeatCount="infinite" android:propertyName="scaleY" android:duration="0" android:startOffset="0" android:valueFrom="0" android:valueTo="1" android:valueType="floatType"/>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G_L_2_G">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:repeatCount="infinite" android:propertyName="rotation" android:duration="983" android:startOffset="0" android:valueFrom="28.9" android:valueTo="-51.4" android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.402,0.135 0.202,0.848 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G_L_2_G">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:repeatCount="infinite" android:propertyName="scaleY" android:duration="0" android:startOffset="133" android:valueFrom="0" android:valueTo="1" android:valueType="floatType"/>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="time_group">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:repeatCount="infinite" android:propertyName="translateX" android:duration="1000" android:startOffset="0" android:valueFrom="0" android:valueTo="1" android:valueType="floatType"/>
+ </set>
+ </aapt:attr>
+ </target>
+</animated-vector>
+
diff --git a/core/res/res/drawable-w204dp/loader_horizontal_watch.xml b/core/res/res/drawable-w204dp/loader_horizontal_watch.xml
new file mode 100644
index 000000000000..fbc6eab320eb
--- /dev/null
+++ b/core/res/res/drawable-w204dp/loader_horizontal_watch.xml
@@ -0,0 +1,104 @@
+<!--
+ ~ Copyright (C) 2025 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.
+ -->
+
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android" xmlns:aapt="http://schemas.android.com/aapt">
+ <aapt:attr name="android:drawable">
+ <vector android:height="15dp" android:width="70dp" android:viewportHeight="15" android:viewportWidth="70">
+ <group android:name="_R_G">
+ <group android:name="_R_G_L_1_G" android:translateX="35" android:translateY="7.5">
+ <path android:name="_R_G_L_1_G_D_0_P_0" android:fillColor="#000000" android:fillAlpha="1" android:fillType="nonZero" android:pathData=" M35 -7.5 C35,-7.5 35,7.5 35,7.5 C35,7.5 -35,7.5 -35,7.5 C-35,7.5 -35,-7.5 -35,-7.5 C-35,-7.5 35,-7.5 35,-7.5c "/>
+ </group>
+ <group android:name="_R_G_L_0_G" android:translateX="-310" android:translateY="-64" android:pivotX="345" android:pivotY="71.5" android:scaleX="0.1" android:scaleY="0.1">
+ <group android:name="_R_G_L_0_G_L_6_G" android:translateX="-239.44799999999998" android:translateY="-341.45" android:pivotX="584.448" android:pivotY="-346.55" android:rotation="28.8" android:scaleY="0">
+ <path android:name="_R_G_L_0_G_L_6_G_D_0_P_0" android:fillColor="#303030" android:fillAlpha="1" android:fillType="nonZero" android:pathData=" M205.28 376.55 C200.4,376.55 195.46,375.36 190.88,372.85 C64.08,303.29 -42.54,201.07 -117.44,77.24 C-194.55,-50.25 -235.31,-196.79 -235.31,-346.55 C-235.31,-363.12 -221.88,-376.55 -205.31,-376.55 C-188.74,-376.55 -175.31,-363.12 -175.31,-346.55 C-175.31,-207.74 -137.54,-71.93 -66.1,46.19 C3.34,160.99 102.18,255.76 219.73,320.24 C234.26,328.21 239.58,346.45 231.61,360.97 C226.15,370.92 215.88,376.55 205.28,376.55c "/>
+ </group>
+ <group android:name="_R_G_L_0_G_L_5_G" android:translateX="781.413" android:translateY="-295.124" android:pivotX="-436.413" android:pivotY="-392.876" android:rotation="28.8">
+ <path android:name="_R_G_L_0_G_L_5_G_D_0_P_0" android:fillColor="#303030" android:fillAlpha="1" android:fillType="nonZero" android:pathData=" M-353.86 422.88 C-369.04,422.88 -382.07,411.4 -383.67,395.97 C-385.37,379.49 -373.4,364.74 -356.92,363.03 C-171.06,343.79 0.76,256.62 126.89,117.59 C253.89,-22.41 323.83,-203.7 323.83,-392.88 C323.83,-409.44 337.26,-422.88 353.83,-422.88 C370.4,-422.88 383.83,-409.44 383.83,-392.88 C383.83,-188.76 308.36,6.84 171.32,157.9 C35.25,307.89 -150.15,401.94 -350.74,422.72 C-351.79,422.82 -352.83,422.88 -353.86,422.88c "/>
+ </group>
+ <group android:name="_R_G_L_0_G_L_2_G" android:translateX="192.671" android:translateY="71.49599999999998" android:pivotX="152.329" android:pivotY="-759.496" android:rotation="28.8" android:scaleY="0">
+ <path android:name="_R_G_L_0_G_L_2_G_D_0_P_0" android:fillColor="#ffffff" android:fillAlpha="1" android:fillType="nonZero" android:pathData=" M152.33 60.5 C152.33,60.5 152.32,60.5 152.32,60.5 C42.76,60.47 -63.64,39.16 -163.91,-2.82 C-179.19,-9.22 -186.39,-26.8 -179.99,-42.08 C-173.59,-57.36 -156.02,-64.57 -140.73,-58.16 C-47.84,-19.27 50.77,0.47 152.34,0.5 C168.91,0.51 182.33,13.94 182.33,30.51 C182.32,47.08 168.89,60.5 152.33,60.5c "/>
+ </group>
+ <group android:name="_R_G_L_0_G_L_0_G" android:translateX="345" android:translateY="71.5">
+ <path android:name="_R_G_L_0_G_L_0_G_D_0_P_0" android:fillColor="#000000" android:fillAlpha="1" android:fillType="nonZero" android:pathData=" M-579 -259.5 C-579,-259.5 -579,259.5 -579,259.5 C-579,259.5 579,259.5 579,259.5 C579,259.5 579,-259.5 579,-259.5 C579,-259.5 -579,-259.5 -579,-259.5c M316.17 -2.8 C216,39.02 108.52,60.54 -0.03,60.5 C-108.58,60.46 -216.04,38.87 -316.18,-3.02 C-331.47,-9.41 -338.68,-26.99 -332.28,-42.27 C-325.89,-57.56 -308.32,-64.76 -293.03,-58.37 C-200.22,-19.55 -100.61,0.46 -0.01,0.5 C100.6,0.54 200.22,-19.41 293.06,-58.17 C308.35,-64.55 325.92,-57.33 332.3,-42.04 C338.68,-26.75 331.46,-9.18 316.17,-2.8c "/>
+ </group>
+ </group>
+ </group>
+ <group android:name="time_group"/>
+ </vector>
+ </aapt:attr>
+ <target android:name="_R_G_L_0_G_L_6_G">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:repeatCount="infinite" android:propertyName="rotation" android:duration="983" android:startOffset="0" android:valueFrom="28.8" android:valueTo="-51.4" android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.402,0.136 0.202,0.847 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G_L_6_G">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:repeatCount="infinite" android:propertyName="scaleY" android:duration="0" android:startOffset="333" android:valueFrom="0" android:valueTo="1" android:valueType="floatType"/>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G_L_5_G">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:repeatCount="infinite" android:propertyName="rotation" android:duration="983" android:startOffset="0" android:valueFrom="28.8" android:valueTo="-51.4" android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.402,0.136 0.202,0.847 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G_L_5_G">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:repeatCount="infinite" android:propertyName="scaleY" android:duration="0" android:startOffset="0" android:valueFrom="0" android:valueTo="1" android:valueType="floatType"/>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G_L_2_G">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:repeatCount="infinite" android:propertyName="rotation" android:duration="983" android:startOffset="0" android:valueFrom="28.8" android:valueTo="-51.4" android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.402,0.136 0.202,0.847 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G_L_2_G">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:repeatCount="infinite" android:propertyName="scaleY" android:duration="0" android:startOffset="133" android:valueFrom="0" android:valueTo="1" android:valueType="floatType"/>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="time_group">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:repeatCount="infinite" android:propertyName="translateX" android:duration="1000" android:startOffset="0" android:valueFrom="0" android:valueTo="1" android:valueType="floatType"/>
+ </set>
+ </aapt:attr>
+ </target>
+</animated-vector>
+
diff --git a/core/res/res/drawable-w216dp/loader_horizontal_watch.xml b/core/res/res/drawable-w216dp/loader_horizontal_watch.xml
new file mode 100644
index 000000000000..ed4b7ea0ff02
--- /dev/null
+++ b/core/res/res/drawable-w216dp/loader_horizontal_watch.xml
@@ -0,0 +1,105 @@
+<!--
+ ~ Copyright (C) 2025 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.
+ -->
+
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android" xmlns:aapt="http://schemas.android.com/aapt">
+ <aapt:attr name="android:drawable">
+ <vector android:height="16dp" android:width="74dp" android:viewportHeight="16" android:viewportWidth="74">
+ <group android:name="_R_G">
+ <group android:name="_R_G_L_1_G" android:translateX="37" android:translateY="8">
+ <path android:name="_R_G_L_1_G_D_0_P_0" android:fillColor="#000000" android:fillAlpha="1" android:fillType="nonZero" android:pathData=" M37 -8 C37,-8 37,8 37,8 C37,8 -37,8 -37,8 C-37,8 -37,-8 -37,-8 C-37,-8 37,-8 37,-8c "/>
+ </group>
+ <group android:name="_R_G_L_0_G" android:translateX="-328" android:translateY="-65.5" android:pivotX="365" android:pivotY="73.5" android:scaleX="0.1" android:scaleY="0.1">
+ <group android:name="_R_G_L_0_G_L_6_G" android:translateX="-256.447" android:translateY="-365.014" android:pivotX="621.447" android:pivotY="-368.486" android:rotation="28.8" android:scaleY="0">
+ <path android:name="_R_G_L_0_G_L_6_G_D_0_P_0" android:fillColor="#303030" android:fillAlpha="1" android:fillType="nonZero" android:pathData=" M218.28 398.49 C213.4,398.49 208.46,397.3 203.88,394.78 C69.34,320.99 -43.78,212.53 -123.25,81.15 C-205.06,-54.11 -248.31,-209.59 -248.31,-368.49 C-248.31,-385.05 -234.88,-398.49 -218.31,-398.49 C-201.74,-398.49 -188.31,-385.05 -188.31,-368.49 C-188.31,-220.54 -148.06,-75.8 -71.91,50.09 C2.1,172.45 107.45,273.45 232.73,342.18 C247.26,350.15 252.58,368.38 244.61,382.91 C239.15,392.86 228.88,398.49 218.28,398.49c "/>
+ </group>
+ <group android:name="_R_G_L_0_G_L_5_G" android:translateX="829.0260000000001" android:translateY="-315.759" android:pivotX="-464.026" android:pivotY="-417.741" android:rotation="28.8">
+ <path android:name="_R_G_L_0_G_L_5_G_D_0_P_0" android:fillColor="#303030" android:fillAlpha="1" android:fillType="nonZero" android:pathData=" M-376.25 447.74 C-391.43,447.74 -404.46,436.26 -406.05,420.83 C-407.76,404.35 -395.78,389.61 -379.3,387.9 C-181.22,367.38 1.9,274.48 136.32,126.3 C271.67,-22.9 346.22,-216.12 346.22,-417.74 C346.22,-434.31 359.65,-447.74 376.22,-447.74 C392.79,-447.74 406.22,-434.31 406.22,-417.74 C406.22,-201.18 326.15,6.35 180.76,166.61 C36.39,325.75 -160.31,425.54 -373.12,447.58 C-374.17,447.69 -375.22,447.74 -376.25,447.74c "/>
+ </group>
+ <group android:name="_R_G_L_0_G_L_2_G" android:translateX="203.029" android:translateY="74.06899999999996" android:pivotX="161.971" android:pivotY="-807.569" android:rotation="28.8" android:scaleY="0">
+ <path android:name="_R_G_L_0_G_L_2_G_D_0_P_0" android:fillColor="#ffffff" android:fillAlpha="1" android:fillType="nonZero" android:pathData=" M161.97 62.43 C161.97,62.43 161.96,62.43 161.96,62.43 C45.71,62.4 -67.17,39.79 -173.55,-4.75 C-188.83,-11.15 -196.03,-28.72 -189.63,-44.01 C-183.24,-59.29 -165.66,-66.49 -150.38,-60.09 C-51.37,-18.64 53.72,2.4 161.98,2.43 C178.55,2.44 191.98,15.87 191.97,32.44 C191.97,49 178.54,62.43 161.97,62.43c "/>
+ </group>
+ <group android:name="_R_G_L_0_G_L_0_G" android:translateX="365" android:translateY="73.5">
+ <path android:name="_R_G_L_0_G_L_0_G_D_0_P_0" android:fillColor="#000000" android:fillAlpha="1" android:fillType="nonZero" android:pathData=" M-609 -244.5 C-609,-244.5 -609,244.5 -609,244.5 C-609,244.5 609,244.5 609,244.5 C609,244.5 609,-244.5 609,-244.5 C609,-244.5 -609,-244.5 -609,-244.5c M335.44 -4.16 C229.17,40.21 115.13,63.04 -0.04,63 C-115.21,62.96 -229.22,40.05 -335.47,-4.39 C-350.76,-10.79 -357.95,-28.36 -351.56,-43.65 C-345.17,-58.93 -327.59,-66.14 -312.31,-59.74 C-213.39,-18.36 -107.24,2.96 -0.02,3 C107.21,3.04 213.38,-18.22 312.33,-59.53 C327.62,-65.91 345.19,-58.69 351.57,-43.4 C357.95,-28.11 350.73,-10.54 335.44,-4.16c "/>
+ </group>
+ </group>
+ </group>
+ <group android:name="time_group"/>
+ </vector>
+ </aapt:attr>
+ <target android:name="_R_G_L_0_G_L_6_G">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:repeatCount="infinite" android:propertyName="rotation" android:duration="983" android:startOffset="0" android:valueFrom="28.8" android:valueTo="-51.3" android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.402,0.136 0.202,0.847 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G_L_6_G">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:repeatCount="infinite" android:propertyName="scaleY" android:duration="0" android:startOffset="333" android:valueFrom="0" android:valueTo="1" android:valueType="floatType"/>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G_L_5_G">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:repeatCount="infinite" android:propertyName="rotation" android:duration="983" android:startOffset="0" android:valueFrom="28.8" android:valueTo="-51.3" android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.402,0.136 0.202,0.847 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G_L_5_G">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:repeatCount="infinite" android:propertyName="scaleY" android:duration="0" android:startOffset="0" android:valueFrom="0" android:valueTo="1" android:valueType="floatType"/>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G_L_2_G">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:repeatCount="infinite" android:propertyName="rotation" android:duration="983" android:startOffset="0" android:valueFrom="28.8" android:valueTo="-51.3" android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.402,0.136 0.202,0.847 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G_L_2_G">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:repeatCount="infinite" android:propertyName="scaleY" android:duration="0" android:startOffset="133" android:valueFrom="0" android:valueTo="1" android:valueType="floatType"/>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="time_group">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:repeatCount="infinite" android:propertyName="translateX" android:duration="1000" android:startOffset="0" android:valueFrom="0" android:valueTo="1" android:valueType="floatType"/>
+ </set>
+ </aapt:attr>
+ </target>
+</animated-vector>
+
+
diff --git a/core/res/res/drawable-w228dp/loader_horizontal_watch.xml b/core/res/res/drawable-w228dp/loader_horizontal_watch.xml
new file mode 100644
index 000000000000..c4574d8af46c
--- /dev/null
+++ b/core/res/res/drawable-w228dp/loader_horizontal_watch.xml
@@ -0,0 +1,103 @@
+<!--
+ ~ Copyright (C) 2025 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.
+ -->
+
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android" xmlns:aapt="http://schemas.android.com/aapt">
+ <aapt:attr name="android:drawable">
+ <vector android:height="16dp" android:width="78dp" android:viewportHeight="16" android:viewportWidth="78">
+ <group android:name="_R_G">
+ <group android:name="_R_G_L_1_G" android:translateX="39" android:translateY="8">
+ <path android:name="_R_G_L_1_G_D_0_P_0" android:fillColor="#000000" android:fillAlpha="1" android:fillType="nonZero" android:pathData=" M39 -8 C39,-8 39,8 39,8 C39,8 -39,8 -39,8 C-39,8 -39,-8 -39,-8 C-39,-8 39,-8 39,-8c "/>
+ </group>
+ <group android:name="_R_G_L_0_G" android:translateX="-345" android:translateY="-67" android:pivotX="384" android:pivotY="75" android:scaleX="0.1" android:scaleY="0.1">
+ <group android:name="_R_G_L_0_G_L_6_G" android:translateX="-274.19" android:translateY="-390.077" android:pivotX="658.448" android:pivotY="-390.423" android:rotation="28.7" android:scaleY="0">
+ <path android:name="_R_G_L_0_G_L_6_G_D_0_P_0" android:fillColor="#303030" android:fillAlpha="1" android:fillType="nonZero" android:pathData=" M231.28 420.42 C226.4,420.42 221.45,419.23 216.88,416.72 C74.61,338.68 -45.02,224 -129.06,85.06 C-171.54,14.82 -204.38,-60.73 -226.66,-139.5 C-249.65,-220.76 -261.31,-305.18 -261.31,-390.42 C-261.31,-406.99 -247.88,-420.42 -231.31,-420.42 C-214.74,-420.42 -201.31,-406.99 -201.31,-390.42 C-201.31,-310.71 -190.42,-231.78 -168.93,-155.83 C-148.11,-82.23 -117.42,-11.63 -77.72,54 C0.86,183.92 112.71,291.15 245.73,364.12 C260.26,372.08 265.58,390.32 257.61,404.85 C252.15,414.79 241.88,420.42 231.28,420.42c "/>
+ </group>
+ <group android:name="_R_G_L_0_G_L_5_G" android:translateX="875.8979999999999" android:translateY="-337.894" android:pivotX="-491.64" android:pivotY="-442.606" android:rotation="28.7">
+ <path android:name="_R_G_L_0_G_L_5_G_D_0_P_0" android:fillColor="#303030" android:fillAlpha="1" android:fillType="nonZero" android:pathData=" M-398.64 472.61 C-413.82,472.61 -426.84,461.13 -428.44,445.7 C-430.15,429.22 -418.17,414.47 -401.69,412.77 C-191.38,390.98 3.04,292.33 145.75,135.01 C289.46,-23.4 368.6,-228.54 368.6,-442.61 C368.6,-459.17 382.04,-472.61 398.6,-472.61 C415.17,-472.61 428.6,-459.17 428.6,-442.61 C428.6,-213.6 343.93,5.85 190.19,175.33 C37.53,343.61 -170.48,449.13 -395.51,472.44 C-396.56,472.55 -397.6,472.61 -398.64,472.61c "/>
+ </group>
+ <group android:name="_R_G_L_0_G_L_2_G" android:translateX="212.64499999999998" android:translateY="75.14200000000005" android:pivotX="171.613" android:pivotY="-855.642" android:rotation="28.7" android:scaleY="0">
+ <path android:name="_R_G_L_0_G_L_2_G_D_0_P_0" android:fillColor="#ffffff" android:fillAlpha="1" android:fillType="nonZero" android:pathData=" M171.61 64.36 C171.61,64.36 171.61,64.36 171.61,64.36 C48.68,64.32 -70.7,40.42 -183.19,-6.68 C-198.47,-13.07 -205.68,-30.65 -199.28,-45.93 C-192.88,-61.22 -175.3,-68.42 -160.02,-62.02 C-54.9,-18.01 56.68,4.33 171.62,4.36 C188.19,4.36 201.62,17.8 201.61,34.36 C201.61,50.93 188.18,64.36 171.61,64.36c "/>
+ </group>
+ <group android:name="_R_G_L_0_G_L_0_G" android:translateX="384" android:translateY="75">
+ <path android:name="_R_G_L_0_G_L_0_G_D_0_P_0" android:fillColor="#000000" android:fillAlpha="1" android:fillType="nonZero" android:pathData=" M-611 -259 C-611,-259 -611,259 -611,259 C-611,259 611,259 611,259 C611,259 611,-259 611,-259 C611,-259 -611,-259 -611,-259c M354.66 -6.52 C242.36,40.4 121.76,64.54 -0.04,64.5 C-121.84,64.46 -242.44,40.23 -354.74,-6.76 C-370.04,-13.16 -377.24,-30.73 -370.84,-46.02 C-364.44,-61.3 -346.94,-68.51 -331.64,-62.12 C-226.54,-18.18 -113.84,4.46 -0.04,4.5 C113.76,4.54 226.56,-18.02 331.56,-61.89 C346.86,-68.27 364.46,-61.05 370.86,-45.76 C377.26,-30.47 369.96,-12.9 354.66,-6.52c "/>
+ </group>
+ </group>
+ </group>
+ <group android:name="time_group"/>
+ </vector>
+ </aapt:attr>
+ <target android:name="_R_G_L_0_G_L_6_G">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:repeatCount="infinite" android:propertyName="rotation" android:duration="983" android:startOffset="0" android:valueFrom="28.7" android:valueTo="-51.4" android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.402,0.136 0.202,0.847 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G_L_6_G">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:repeatCount="infinite" android:propertyName="scaleY" android:duration="0" android:startOffset="333" android:valueFrom="0" android:valueTo="1" android:valueType="floatType"/>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G_L_5_G">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:repeatCount="infinite" android:propertyName="rotation" android:duration="983" android:startOffset="0" android:valueFrom="28.7" android:valueTo="-51.4" android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.402,0.136 0.202,0.847 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G_L_5_G">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:repeatCount="infinite" android:propertyName="scaleY" android:duration="0" android:startOffset="0" android:valueFrom="0" android:valueTo="1" android:valueType="floatType"/>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G_L_2_G">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:repeatCount="infinite" android:propertyName="rotation" android:duration="983" android:startOffset="0" android:valueFrom="28.7" android:valueTo="-51.4" android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.402,0.136 0.202,0.847 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G_L_2_G">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:repeatCount="infinite" android:propertyName="scaleY" android:duration="0" android:startOffset="133" android:valueFrom="0" android:valueTo="1" android:valueType="floatType"/>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="time_group">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:repeatCount="infinite" android:propertyName="translateX" android:duration="1000" android:startOffset="0" android:valueFrom="0" android:valueTo="1" android:valueType="floatType"/>
+ </set>
+ </aapt:attr>
+ </target>
+</animated-vector>
diff --git a/core/res/res/drawable-w240dp/loader_horizontal_watch.xml b/core/res/res/drawable-w240dp/loader_horizontal_watch.xml
new file mode 100644
index 000000000000..ad60bbdc420c
--- /dev/null
+++ b/core/res/res/drawable-w240dp/loader_horizontal_watch.xml
@@ -0,0 +1,104 @@
+<!--
+ ~ Copyright (C) 2025 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.
+ -->
+
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android" xmlns:aapt="http://schemas.android.com/aapt">
+ <aapt:attr name="android:drawable">
+ <vector android:height="17dp" android:width="82dp" android:viewportHeight="17" android:viewportWidth="82">
+ <group android:name="_R_G">
+ <group android:name="_R_G_L_1_G" android:translateX="41" android:translateY="8.5">
+ <path android:name="_R_G_L_1_G_D_0_P_0" android:fillColor="#000000" android:fillAlpha="1" android:fillType="nonZero" android:pathData=" M41 -8.5 C41,-8.5 41,8.5 41,8.5 C41,8.5 -41,8.5 -41,8.5 C-41,8.5 -41,-8.5 -41,-8.5 C-41,-8.5 41,-8.5 41,-8.5c "/>
+ </group>
+ <group android:name="_R_G_L_0_G" android:translateX="-362.5" android:translateY="-69" android:pivotX="403.5" android:pivotY="77.5" android:scaleX="0.1" android:scaleY="0.1">
+ <group android:name="_R_G_L_0_G_L_6_G" android:translateX="-291.64799999999997" android:translateY="-414.141" android:pivotX="695.448" android:pivotY="-412.359" android:rotation="28.7" android:scaleY="0">
+ <path android:name="_R_G_L_0_G_L_6_G_D_0_P_0" android:fillColor="#303030" android:fillAlpha="1" android:fillType="nonZero" android:pathData=" M244.28 442.36 C239.4,442.36 234.45,441.17 229.88,438.66 C79.87,356.38 -46.26,235.46 -134.87,88.96 C-179.66,14.91 -214.28,-64.74 -237.78,-147.79 C-262.02,-233.47 -274.31,-322.49 -274.31,-412.36 C-274.31,-428.93 -260.88,-442.36 -244.31,-442.36 C-227.74,-442.36 -214.31,-428.93 -214.31,-412.36 C-214.31,-328.01 -202.78,-244.49 -180.05,-164.12 C-158.01,-86.24 -125.54,-11.54 -83.53,57.91 C-0.38,195.38 117.97,308.85 258.73,386.05 C273.26,394.02 278.58,412.26 270.61,426.78 C265.15,436.73 254.88,442.36 244.28,442.36c "/>
+ </group>
+ <group android:name="_R_G_L_0_G_L_5_G" android:translateX="923.0530000000001" android:translateY="-359.029" android:pivotX="-519.253" android:pivotY="-467.471" android:rotation="28.7">
+ <path android:name="_R_G_L_0_G_L_5_G_D_0_P_0" android:fillColor="#303030" android:fillAlpha="1" android:fillType="nonZero" android:pathData=" M-421.02 497.47 C-436.2,497.47 -449.23,485.99 -450.83,470.56 C-452.53,454.08 -440.56,439.34 -424.08,437.63 C-201.54,414.57 4.18,310.19 155.19,143.73 C229.54,61.77 287.7,-31.75 328.04,-134.22 C369.81,-240.3 390.99,-352.42 390.99,-467.47 C390.99,-484.04 404.42,-497.47 420.99,-497.47 C437.56,-497.47 450.99,-484.04 450.99,-467.47 C450.99,-226.02 361.72,5.35 199.63,184.04 C38.67,361.47 -180.63,472.73 -417.89,497.31 C-418.94,497.42 -419.99,497.47 -421.02,497.47c "/>
+ </group>
+ <group android:name="_R_G_L_0_G_L_2_G" android:translateX="222.54600000000002" android:translateY="77.21400000000006" android:pivotX="181.254" android:pivotY="-903.714" android:rotation="28.7" android:scaleY="0">
+ <path android:name="_R_G_L_0_G_L_2_G_D_0_P_0" android:fillColor="#ffffff" android:fillAlpha="1" android:fillType="nonZero" android:pathData=" M181.26 66.28 C181.25,66.28 181.25,66.28 181.25,66.28 C51.64,66.25 -74.22,41.06 -192.83,-8.6 C-208.12,-15 -215.32,-32.58 -208.92,-47.86 C-202.52,-63.15 -184.94,-70.35 -169.66,-63.95 C-58.42,-17.38 59.64,6.25 181.26,6.28 C197.83,6.29 211.26,19.72 211.26,36.29 C211.25,52.86 197.82,66.28 181.26,66.28c "/>
+ </group>
+ <group android:name="_R_G_L_0_G_L_0_G" android:translateX="403.5" android:translateY="77.5">
+ <path android:name="_R_G_L_0_G_L_0_G_D_0_P_0" android:fillColor="#000000" android:fillAlpha="1" android:fillType="nonZero" android:pathData=" M-630.5 -255.5 C-630.5,-255.5 -630.5,255.5 -630.5,255.5 C-630.5,255.5 630.5,255.5 630.5,255.5 C630.5,255.5 630.5,-255.5 630.5,-255.5 C630.5,-255.5 -630.5,-255.5 -630.5,-255.5c M374 -8.88 C255.5,40.59 128.4,66.04 0,66 C-128.4,65.95 -255.6,40.42 -374,-9.14 C-389.3,-15.53 -396.5,-33.11 -390.1,-48.39 C-383.7,-63.68 -366.2,-70.88 -350.9,-64.49 C-239.7,-18 -120.5,5.96 0,6 C120.4,6.04 239.7,-17.84 350.9,-64.25 C366.2,-70.63 383.7,-63.41 390.1,-48.12 C396.5,-32.83 389.3,-15.26 374,-8.88c "/>
+ </group>
+ </group>
+ </group>
+ <group android:name="time_group"/>
+ </vector>
+ </aapt:attr>
+ <target android:name="_R_G_L_0_G_L_6_G">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:repeatCount="infinite" android:propertyName="rotation" android:duration="983" android:startOffset="0" android:valueFrom="28.7" android:valueTo="-51.4" android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.402,0.136 0.202,0.847 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G_L_6_G">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:repeatCount="infinite" android:propertyName="scaleY" android:duration="0" android:startOffset="333" android:valueFrom="0" android:valueTo="1" android:valueType="floatType"/>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G_L_5_G">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:repeatCount="infinite" android:propertyName="rotation" android:duration="983" android:startOffset="0" android:valueFrom="28.7" android:valueTo="-51.4" android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.402,0.136 0.202,0.847 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G_L_5_G">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:repeatCount="infinite" android:propertyName="scaleY" android:duration="0" android:startOffset="0" android:valueFrom="0" android:valueTo="1" android:valueType="floatType"/>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G_L_2_G">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:repeatCount="infinite" android:propertyName="rotation" android:duration="983" android:startOffset="0" android:valueFrom="28.7" android:valueTo="-51.4" android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.402,0.136 0.202,0.847 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G_L_2_G">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:repeatCount="infinite" android:propertyName="scaleY" android:duration="0" android:startOffset="133" android:valueFrom="0" android:valueTo="1" android:valueType="floatType"/>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="time_group">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:repeatCount="infinite" android:propertyName="translateX" android:duration="1000" android:startOffset="0" android:valueFrom="0" android:valueTo="1" android:valueType="floatType"/>
+ </set>
+ </aapt:attr>
+ </target>
+</animated-vector>
+
diff --git a/core/res/res/drawable/loader_horizontal_watch.xml b/core/res/res/drawable/loader_horizontal_watch.xml
new file mode 100644
index 000000000000..6b86c634d554
--- /dev/null
+++ b/core/res/res/drawable/loader_horizontal_watch.xml
@@ -0,0 +1,103 @@
+<!--
+ ~ Copyright (C) 2025 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.
+ -->
+
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android" xmlns:aapt="http://schemas.android.com/aapt">
+ <aapt:attr name="android:drawable">
+ <vector android:height="14dp" android:width="76dp" android:viewportHeight="14" android:viewportWidth="76">
+ <group android:name="_R_G">
+ <group android:name="_R_G_L_1_G" android:translateX="39" android:translateY="8">
+ <path android:name="_R_G_L_1_G_D_0_P_0" android:fillColor="#000000" android:fillAlpha="1" android:fillType="nonZero" android:pathData=" M39 -8 C39,-8 39,8 39,8 C39,8 -39,8 -39,8 C-39,8 -39,-8 -39,-8 C-39,-8 39,-8 39,-8c "/>
+ </group>
+ <group android:name="_R_G_L_0_G" android:translateX="-345" android:translateY="-67" android:pivotX="384" android:pivotY="75" android:scaleX="0.1" android:scaleY="0.1">
+ <group android:name="_R_G_L_0_G_L_6_G" android:translateX="-274.19" android:translateY="-390.077" android:pivotX="658.448" android:pivotY="-390.423" android:rotation="28.7" android:scaleY="0">
+ <path android:name="_R_G_L_0_G_L_6_G_D_0_P_0" android:fillColor="#303030" android:fillAlpha="1" android:fillType="nonZero" android:pathData=" M231.28 420.42 C226.4,420.42 221.45,419.23 216.88,416.72 C74.61,338.68 -45.02,224 -129.06,85.06 C-171.54,14.82 -204.38,-60.73 -226.66,-139.5 C-249.65,-220.76 -261.31,-305.18 -261.31,-390.42 C-261.31,-406.99 -247.88,-420.42 -231.31,-420.42 C-214.74,-420.42 -201.31,-406.99 -201.31,-390.42 C-201.31,-310.71 -190.42,-231.78 -168.93,-155.83 C-148.11,-82.23 -117.42,-11.63 -77.72,54 C0.86,183.92 112.71,291.15 245.73,364.12 C260.26,372.08 265.58,390.32 257.61,404.85 C252.15,414.79 241.88,420.42 231.28,420.42c "/>
+ </group>
+ <group android:name="_R_G_L_0_G_L_5_G" android:translateX="875.8979999999999" android:translateY="-337.894" android:pivotX="-491.64" android:pivotY="-442.606" android:rotation="28.7">
+ <path android:name="_R_G_L_0_G_L_5_G_D_0_P_0" android:fillColor="#303030" android:fillAlpha="1" android:fillType="nonZero" android:pathData=" M-398.64 472.61 C-413.82,472.61 -426.84,461.13 -428.44,445.7 C-430.15,429.22 -418.17,414.47 -401.69,412.77 C-191.38,390.98 3.04,292.33 145.75,135.01 C289.46,-23.4 368.6,-228.54 368.6,-442.61 C368.6,-459.17 382.04,-472.61 398.6,-472.61 C415.17,-472.61 428.6,-459.17 428.6,-442.61 C428.6,-213.6 343.93,5.85 190.19,175.33 C37.53,343.61 -170.48,449.13 -395.51,472.44 C-396.56,472.55 -397.6,472.61 -398.64,472.61c "/>
+ </group>
+ <group android:name="_R_G_L_0_G_L_2_G" android:translateX="212.64499999999998" android:translateY="75.14200000000005" android:pivotX="171.613" android:pivotY="-855.642" android:rotation="28.7" android:scaleY="0">
+ <path android:name="_R_G_L_0_G_L_2_G_D_0_P_0" android:fillColor="#ffffff" android:fillAlpha="1" android:fillType="nonZero" android:pathData=" M171.61 64.36 C171.61,64.36 171.61,64.36 171.61,64.36 C48.68,64.32 -70.7,40.42 -183.19,-6.68 C-198.47,-13.07 -205.68,-30.65 -199.28,-45.93 C-192.88,-61.22 -175.3,-68.42 -160.02,-62.02 C-54.9,-18.01 56.68,4.33 171.62,4.36 C188.19,4.36 201.62,17.8 201.61,34.36 C201.61,50.93 188.18,64.36 171.61,64.36c "/>
+ </group>
+ <group android:name="_R_G_L_0_G_L_0_G" android:translateX="384" android:translateY="75">
+ <path android:name="_R_G_L_0_G_L_0_G_D_0_P_0" android:fillColor="#000000" android:fillAlpha="1" android:fillType="nonZero" android:pathData=" M-611 -259 C-611,-259 -611,259 -611,259 C-611,259 611,259 611,259 C611,259 611,-259 611,-259 C611,-259 -611,-259 -611,-259c M354.66 -6.52 C242.36,40.4 121.76,64.54 -0.04,64.5 C-121.84,64.46 -242.44,40.23 -354.74,-6.76 C-370.04,-13.16 -377.24,-30.73 -370.84,-46.02 C-364.44,-61.3 -346.94,-68.51 -331.64,-62.12 C-226.54,-18.18 -113.84,4.46 -0.04,4.5 C113.76,4.54 226.56,-18.02 331.56,-61.89 C346.86,-68.27 364.46,-61.05 370.86,-45.76 C377.26,-30.47 369.96,-12.9 354.66,-6.52c "/>
+ </group>
+ </group>
+ </group>
+ <group android:name="time_group"/>
+ </vector>
+ </aapt:attr>
+ <target android:name="_R_G_L_0_G_L_6_G">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:repeatCount="infinite" android:propertyName="rotation" android:duration="983" android:startOffset="0" android:valueFrom="28.7" android:valueTo="-51.4" android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.402,0.136 0.202,0.847 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G_L_6_G">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:repeatCount="infinite" android:propertyName="scaleY" android:duration="0" android:startOffset="333" android:valueFrom="0" android:valueTo="1" android:valueType="floatType"/>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G_L_5_G">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:repeatCount="infinite" android:propertyName="rotation" android:duration="983" android:startOffset="0" android:valueFrom="28.7" android:valueTo="-51.4" android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.402,0.136 0.202,0.847 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G_L_5_G">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:repeatCount="infinite" android:propertyName="scaleY" android:duration="0" android:startOffset="0" android:valueFrom="0" android:valueTo="1" android:valueType="floatType"/>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G_L_2_G">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:repeatCount="infinite" android:propertyName="rotation" android:duration="983" android:startOffset="0" android:valueFrom="28.7" android:valueTo="-51.4" android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.402,0.136 0.202,0.847 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G_L_2_G">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:repeatCount="infinite" android:propertyName="scaleY" android:duration="0" android:startOffset="133" android:valueFrom="0" android:valueTo="1" android:valueType="floatType"/>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="time_group">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:repeatCount="infinite" android:propertyName="translateX" android:duration="1000" android:startOffset="0" android:valueFrom="0" android:valueTo="1" android:valueType="floatType"/>
+ </set>
+ </aapt:attr>
+ </target>
+</animated-vector>
diff --git a/core/res/res/drawable/notification_progress.xml b/core/res/res/drawable/notification_progress.xml
index ff5450ee106f..92a0a6a4e21b 100644
--- a/core/res/res/drawable/notification_progress.xml
+++ b/core/res/res/drawable/notification_progress.xml
@@ -19,12 +19,9 @@
android:gravity="center_vertical|fill_horizontal">
<com.android.internal.widget.NotificationProgressDrawable
android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:segSegGap="@dimen/notification_progress_segSeg_gap"
- android:segPointGap="@dimen/notification_progress_segPoint_gap">
+ android:layout_height="wrap_content">
<segments
android:color="?attr/colorProgressBackgroundNormal"
- android:minWidth="@dimen/notification_progress_segments_min_width"
android:height="@dimen/notification_progress_segments_height"
android:fadedHeight="@dimen/notification_progress_segments_faded_height"
android:cornerRadius="@dimen/notification_progress_segments_corner_radius"/>
diff --git a/core/res/res/layout/notification_2025_messaging_group.xml b/core/res/res/layout/notification_2025_messaging_group.xml
index ecaf0b9a785f..ba0ce7b21b80 100644
--- a/core/res/res/layout/notification_2025_messaging_group.xml
+++ b/core/res/res/layout/notification_2025_messaging_group.xml
@@ -53,7 +53,6 @@
android:id="@+id/group_message_container"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_marginTop="@dimen/notification_text_margin_top"
android:spacing="2dp" />
</com.android.internal.widget.RemeasuringLinearLayout>
<FrameLayout
diff --git a/core/res/res/layout/notification_2025_template_collapsed_base.xml b/core/res/res/layout/notification_2025_template_collapsed_base.xml
index 57c89b91cff7..201f46025286 100644
--- a/core/res/res/layout/notification_2025_template_collapsed_base.xml
+++ b/core/res/res/layout/notification_2025_template_collapsed_base.xml
@@ -74,6 +74,7 @@
android:id="@+id/notification_headerless_view_column"
android:layout_width="0px"
android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
android:layout_weight="1"
android:layout_marginVertical="@dimen/notification_2025_reduced_margin"
android:orientation="vertical"
diff --git a/core/res/res/layout/notification_2025_template_collapsed_media.xml b/core/res/res/layout/notification_2025_template_collapsed_media.xml
index de82f9feb512..e599de12097a 100644
--- a/core/res/res/layout/notification_2025_template_collapsed_media.xml
+++ b/core/res/res/layout/notification_2025_template_collapsed_media.xml
@@ -76,6 +76,7 @@
android:id="@+id/notification_headerless_view_column"
android:layout_width="0px"
android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
android:layout_weight="1"
android:layout_marginVertical="@dimen/notification_2025_reduced_margin"
android:orientation="vertical"
diff --git a/core/res/res/layout/notification_2025_template_expanded_base.xml b/core/res/res/layout/notification_2025_template_expanded_base.xml
index 287110766dc7..58b26c905162 100644
--- a/core/res/res/layout/notification_2025_template_expanded_base.xml
+++ b/core/res/res/layout/notification_2025_template_expanded_base.xml
@@ -49,7 +49,7 @@
android:orientation="vertical"
>
- <include layout="@layout/notification_template_part_line1" />
+ <include layout="@layout/notification_2025_title" />
<include layout="@layout/notification_template_text_multiline" />
diff --git a/core/res/res/layout/notification_2025_template_expanded_big_picture.xml b/core/res/res/layout/notification_2025_template_expanded_big_picture.xml
index ead6d4cbc034..f6a17ef6d46f 100644
--- a/core/res/res/layout/notification_2025_template_expanded_big_picture.xml
+++ b/core/res/res/layout/notification_2025_template_expanded_big_picture.xml
@@ -46,7 +46,7 @@
android:orientation="vertical"
>
- <include layout="@layout/notification_template_part_line1" />
+ <include layout="@layout/notification_2025_title" />
<include
layout="@layout/notification_template_progress"
diff --git a/core/res/res/layout/notification_2025_template_expanded_big_text.xml b/core/res/res/layout/notification_2025_template_expanded_big_text.xml
index de5e71d2015f..540444ea4da4 100644
--- a/core/res/res/layout/notification_2025_template_expanded_big_text.xml
+++ b/core/res/res/layout/notification_2025_template_expanded_big_text.xml
@@ -47,7 +47,7 @@
android:layout_weight="1"
>
- <include layout="@layout/notification_template_part_line1" />
+ <include layout="@layout/notification_2025_title" />
<include
layout="@layout/notification_template_progress"
diff --git a/core/res/res/layout/notification_2025_template_expanded_call.xml b/core/res/res/layout/notification_2025_template_expanded_call.xml
index c096bc8a4d15..60b32002d484 100644
--- a/core/res/res/layout/notification_2025_template_expanded_call.xml
+++ b/core/res/res/layout/notification_2025_template_expanded_call.xml
@@ -50,7 +50,7 @@
android:clipChildren="false"
>
- <include layout="@layout/notification_template_part_line1"/>
+ <include layout="@layout/notification_2025_title"/>
<include layout="@layout/notification_template_text_multiline" />
diff --git a/core/res/res/layout/notification_2025_template_expanded_conversation.xml b/core/res/res/layout/notification_2025_template_expanded_conversation.xml
index 6eea8cc93aeb..e085d47d4579 100644
--- a/core/res/res/layout/notification_2025_template_expanded_conversation.xml
+++ b/core/res/res/layout/notification_2025_template_expanded_conversation.xml
@@ -48,14 +48,15 @@
android:clipChildren="false"
>
- <include layout="@layout/notification_template_part_line1"/>
+ <include layout="@layout/notification_2025_title"/>
<com.android.internal.widget.MessagingLinearLayout
android:id="@+id/notification_messaging"
android:layout_width="match_parent"
android:layout_height="wrap_content"
+ android:layout_marginTop="@dimen/notification_2025_margin"
android:clipChildren="false"
- android:spacing="@dimen/notification_messaging_spacing" />
+ android:spacing="@dimen/notification_2025_messaging_spacing" />
</com.android.internal.widget.RemeasuringLinearLayout>
<include layout="@layout/notification_template_smart_reply_container"
diff --git a/core/res/res/layout/notification_2025_template_expanded_inbox.xml b/core/res/res/layout/notification_2025_template_expanded_inbox.xml
index 327cd7ac71bb..55253d0ce5f8 100644
--- a/core/res/res/layout/notification_2025_template_expanded_inbox.xml
+++ b/core/res/res/layout/notification_2025_template_expanded_inbox.xml
@@ -43,7 +43,7 @@
android:clipToPadding="false"
android:orientation="vertical"
>
- <include layout="@layout/notification_template_part_line1"
+ <include layout="@layout/notification_2025_title"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<include layout="@layout/notification_template_progress"
diff --git a/core/res/res/layout/notification_2025_template_expanded_media.xml b/core/res/res/layout/notification_2025_template_expanded_media.xml
index 565a558a5115..5e97607dc65b 100644
--- a/core/res/res/layout/notification_2025_template_expanded_media.xml
+++ b/core/res/res/layout/notification_2025_template_expanded_media.xml
@@ -44,7 +44,7 @@
android:layout_marginEnd="@dimen/notification_2025_margin"
android:orientation="vertical"
>
- <include layout="@layout/notification_template_part_line1"/>
+ <include layout="@layout/notification_2025_title"/>
<include layout="@layout/notification_template_text"/>
</LinearLayout>
diff --git a/core/res/res/layout/notification_2025_template_expanded_messaging.xml b/core/res/res/layout/notification_2025_template_expanded_messaging.xml
index df48479e5ec3..14ed536a9a5c 100644
--- a/core/res/res/layout/notification_2025_template_expanded_messaging.xml
+++ b/core/res/res/layout/notification_2025_template_expanded_messaging.xml
@@ -48,14 +48,15 @@
android:clipChildren="false"
>
- <include layout="@layout/notification_template_part_line1"/>
+ <include layout="@layout/notification_2025_title"/>
<com.android.internal.widget.MessagingLinearLayout
android:id="@+id/notification_messaging"
android:layout_width="match_parent"
android:layout_height="wrap_content"
+ android:layout_marginTop="@dimen/notification_2025_margin"
android:clipChildren="false"
- android:spacing="@dimen/notification_messaging_spacing" />
+ android:spacing="@dimen/notification_2025_messaging_spacing" />
</com.android.internal.widget.RemeasuringLinearLayout>
<include layout="@layout/notification_template_smart_reply_container"
diff --git a/core/res/res/layout/notification_2025_template_expanded_progress.xml b/core/res/res/layout/notification_2025_template_expanded_progress.xml
index b929b9ed20b7..1315481d86c5 100644
--- a/core/res/res/layout/notification_2025_template_expanded_progress.xml
+++ b/core/res/res/layout/notification_2025_template_expanded_progress.xml
@@ -50,7 +50,7 @@
android:orientation="vertical"
>
- <include layout="@layout/notification_template_part_line1" />
+ <include layout="@layout/notification_2025_title" />
<include layout="@layout/notification_template_text_multiline" />
diff --git a/core/res/res/layout/notification_2025_title.xml b/core/res/res/layout/notification_2025_title.xml
new file mode 100644
index 000000000000..7cea5ea08449
--- /dev/null
+++ b/core/res/res/layout/notification_2025_title.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2025 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
+ -->
+<TextView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/title"
+ android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Title"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:maxLines="2"
+ android:ellipsize="end"
+ android:textAlignment="viewStart"
+ />
diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml
index 568468b51f47..1cab0dc1b851 100644
--- a/core/res/res/values-af/strings.xml
+++ b/core/res/res/values-af/strings.xml
@@ -2273,18 +2273,12 @@
<string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Rollees"</string>
<string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Onderbreek"</string>
<string name="accessibility_autoclick_position" msgid="2933660969907663545">"Posisie"</string>
- <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) -->
- <skip />
+ <string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"Rollees op"</string>
+ <string name="accessibility_autoclick_scroll_down" msgid="3733401063292018116">"Rollees af"</string>
+ <string name="accessibility_autoclick_scroll_left" msgid="8564421367992824198">"Rollees na links"</string>
+ <string name="accessibility_autoclick_scroll_right" msgid="8932417330753984265">"Rollees na regs"</string>
+ <string name="accessibility_autoclick_scroll_exit" msgid="3788610039146769696">"Gaan uit Rolleesmodus"</string>
+ <string name="accessibility_autoclick_scroll_panel_title" msgid="7120598166296447036">"Rolleespaneel"</string>
<string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> is in die BEPERK-groep geplaas"</string>
<string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string>
<string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"het \'n prent gestuur"</string>
@@ -2492,8 +2486,7 @@
<string name="profile_label_work_3" msgid="4834572253956798917">"Werk 3"</string>
<string name="profile_label_test" msgid="9168641926186071947">"Toets"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"Gemeenskaplik"</string>
- <!-- no translation found for profile_label_supervising (5649312778545745371) -->
- <skip />
+ <string name="profile_label_supervising" msgid="5649312778545745371">"Toesighoudend"</string>
<string name="accessibility_label_managed_profile" msgid="3366526886209832641">"Werkprofiel"</string>
<string name="accessibility_label_private_profile" msgid="1436459319135548969">"Privaat ruimte"</string>
<string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Kloon"</string>
diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml
index 20d5336959d2..bda4da077204 100644
--- a/core/res/res/values-am/strings.xml
+++ b/core/res/res/values-am/strings.xml
@@ -2273,18 +2273,12 @@
<string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"ሸብልል"</string>
<string name="accessibility_autoclick_pause" msgid="3272200156172573568">"ባለበት አቁም"</string>
<string name="accessibility_autoclick_position" msgid="2933660969907663545">"አቀማመጥ"</string>
- <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) -->
- <skip />
+ <string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"ወደ ላይ ሸብልል"</string>
+ <string name="accessibility_autoclick_scroll_down" msgid="3733401063292018116">"ወደ ታች ሸብልል"</string>
+ <string name="accessibility_autoclick_scroll_left" msgid="8564421367992824198">"ወደ ግራ ሸብልል"</string>
+ <string name="accessibility_autoclick_scroll_right" msgid="8932417330753984265">"ወደ ቀኝ ሸብልል"</string>
+ <string name="accessibility_autoclick_scroll_exit" msgid="3788610039146769696">"ከሸብልል ሁነታ ውጣ"</string>
+ <string name="accessibility_autoclick_scroll_panel_title" msgid="7120598166296447036">"የመሸብለል ፓነል"</string>
<string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> ወደ የRESTRICTED ባልዲ ተከትቷል"</string>
<string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>፦"</string>
<string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"አንድ ምስል ልከዋል"</string>
@@ -2492,8 +2486,7 @@
<string name="profile_label_work_3" msgid="4834572253956798917">"ሥራ 3"</string>
<string name="profile_label_test" msgid="9168641926186071947">"ሙከራ"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"የጋራ"</string>
- <!-- no translation found for profile_label_supervising (5649312778545745371) -->
- <skip />
+ <string name="profile_label_supervising" msgid="5649312778545745371">"ክትትል በማድረግ ላይ"</string>
<string name="accessibility_label_managed_profile" msgid="3366526886209832641">"የሥራ መገለጫ"</string>
<string name="accessibility_label_private_profile" msgid="1436459319135548969">"የግል ቦታ"</string>
<string name="accessibility_label_clone_profile" msgid="7579118375042398784">"አባዛ"</string>
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index ffc144416d91..03a31ddb2129 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -1808,9 +1808,9 @@
<string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"زيادة تعتيم الشاشة"</string>
<string name="hearing_aids_feature_name" msgid="1125892105105852542">"سماعات الأذن الطبية"</string>
<string name="autoclick_feature_name" msgid="8149248738736949630">"النقر التلقائي"</string>
- <string name="hearing_device_status_disconnected" msgid="497547752953543832">"غير متّصل"</string>
- <string name="hearing_device_status_connected" msgid="2149385149669918764">"متّصل"</string>
- <string name="hearing_device_status_active" msgid="4770378695482566032">"متّصل حاليًا"</string>
+ <string name="hearing_device_status_disconnected" msgid="497547752953543832">"غير متّصلة"</string>
+ <string name="hearing_device_status_connected" msgid="2149385149669918764">"متّصلة"</string>
+ <string name="hearing_device_status_active" msgid="4770378695482566032">"متّصلة حاليًا"</string>
<string name="hearing_device_status_loading" msgid="5717083847663109747">"غير متاح بعد"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"تم الضغط مع الاستمرار على مفتاحَي التحكّم في مستوى الصوت. تم تفعيل <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"تم الضغط مع الاستمرار على مفتاحَي التحكّم في مستوى الصوت. تم إيقاف <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string>
@@ -2490,8 +2490,7 @@
<string name="profile_label_work_3" msgid="4834572253956798917">"ملف العمل 3"</string>
<string name="profile_label_test" msgid="9168641926186071947">"ملف شخصي تجريبي"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"ملف شخصي مشترك"</string>
- <!-- no translation found for profile_label_supervising (5649312778545745371) -->
- <skip />
+ <string name="profile_label_supervising" msgid="5649312778545745371">"المُشرف"</string>
<string name="accessibility_label_managed_profile" msgid="3366526886209832641">"ملف العمل"</string>
<string name="accessibility_label_private_profile" msgid="1436459319135548969">"المساحة الخاصة"</string>
<string name="accessibility_label_clone_profile" msgid="7579118375042398784">"نسخة طبق الأصل"</string>
diff --git a/core/res/res/values-as/strings.xml b/core/res/res/values-as/strings.xml
index 5c78cded7df6..5f7ef0078a0f 100644
--- a/core/res/res/values-as/strings.xml
+++ b/core/res/res/values-as/strings.xml
@@ -2273,18 +2273,12 @@
<string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"স্ক্ৰ’ল কৰক"</string>
<string name="accessibility_autoclick_pause" msgid="3272200156172573568">"পজ কৰক"</string>
<string name="accessibility_autoclick_position" msgid="2933660969907663545">"স্থান"</string>
- <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) -->
- <skip />
+ <string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"ওপৰলৈ স্ক্ৰ’ল কৰক"</string>
+ <string name="accessibility_autoclick_scroll_down" msgid="3733401063292018116">"তললৈ স্ক্ৰ’ল কৰক"</string>
+ <string name="accessibility_autoclick_scroll_left" msgid="8564421367992824198">"বাওঁফাললৈ স্ক্ৰ’ল কৰক"</string>
+ <string name="accessibility_autoclick_scroll_right" msgid="8932417330753984265">"সোঁফাললৈ স্ক্ৰ’ল কৰক"</string>
+ <string name="accessibility_autoclick_scroll_exit" msgid="3788610039146769696">"স্ক্ৰ’ল ম’ডৰ পৰা বাহিৰ হওক"</string>
+ <string name="accessibility_autoclick_scroll_panel_title" msgid="7120598166296447036">"স্ক্ৰ’ল পেনেল"</string>
<string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g>ক সীমাবদ্ধ বাকেটটোত ৰখা হৈছে"</string>
<string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string>
<string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"এখন প্ৰতিচ্ছবি পঠিয়াইছে"</string>
@@ -2492,8 +2486,7 @@
<string name="profile_label_work_3" msgid="4834572253956798917">"কৰ্মস্থান ৩"</string>
<string name="profile_label_test" msgid="9168641926186071947">"পৰীক্ষা"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"শ্বেয়াৰ কৰা"</string>
- <!-- no translation found for profile_label_supervising (5649312778545745371) -->
- <skip />
+ <string name="profile_label_supervising" msgid="5649312778545745371">"তদাৰক কৰি থকা হৈছে"</string>
<string name="accessibility_label_managed_profile" msgid="3366526886209832641">"কৰ্মস্থানৰ প্ৰ’ফাইল"</string>
<string name="accessibility_label_private_profile" msgid="1436459319135548969">"প্ৰাইভেট স্পে’চ"</string>
<string name="accessibility_label_clone_profile" msgid="7579118375042398784">"ক্ল’ন"</string>
diff --git a/core/res/res/values-az/strings.xml b/core/res/res/values-az/strings.xml
index c8b500be22c6..bc8a3f337f10 100644
--- a/core/res/res/values-az/strings.xml
+++ b/core/res/res/values-az/strings.xml
@@ -2486,8 +2486,7 @@
<string name="profile_label_work_3" msgid="4834572253956798917">"İş 3"</string>
<string name="profile_label_test" msgid="9168641926186071947">"Test"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"Kommunal"</string>
- <!-- no translation found for profile_label_supervising (5649312778545745371) -->
- <skip />
+ <string name="profile_label_supervising" msgid="5649312778545745371">"Nəzarət edilir"</string>
<string name="accessibility_label_managed_profile" msgid="3366526886209832641">"İş profili"</string>
<string name="accessibility_label_private_profile" msgid="1436459319135548969">"Məxfi sahə"</string>
<string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Klon"</string>
diff --git a/core/res/res/values-b+sr+Latn/strings.xml b/core/res/res/values-b+sr+Latn/strings.xml
index d1889b84e90f..8b742006075f 100644
--- a/core/res/res/values-b+sr+Latn/strings.xml
+++ b/core/res/res/values-b+sr+Latn/strings.xml
@@ -2274,18 +2274,12 @@
<string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Skrolujte"</string>
<string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Pauziraj"</string>
<string name="accessibility_autoclick_position" msgid="2933660969907663545">"Pozicija"</string>
- <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) -->
- <skip />
+ <string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"Skroluj nagore"</string>
+ <string name="accessibility_autoclick_scroll_down" msgid="3733401063292018116">"Skroluj nadole"</string>
+ <string name="accessibility_autoclick_scroll_left" msgid="8564421367992824198">"Skroluj ulevo"</string>
+ <string name="accessibility_autoclick_scroll_right" msgid="8932417330753984265">"Skroluj udesno"</string>
+ <string name="accessibility_autoclick_scroll_exit" msgid="3788610039146769696">"Izađi iz režima skrolovanja"</string>
+ <string name="accessibility_autoclick_scroll_panel_title" msgid="7120598166296447036">"Okno za skrolovanje"</string>
<string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"Paket <xliff:g id="PACKAGE_NAME">%1$s</xliff:g> je dodat u segment OGRANIČENO"</string>
<string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string>
<string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"je poslao/la sliku"</string>
@@ -2493,8 +2487,7 @@
<string name="profile_label_work_3" msgid="4834572253956798917">"Posao 3"</string>
<string name="profile_label_test" msgid="9168641926186071947">"Test"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"Zajedničko"</string>
- <!-- no translation found for profile_label_supervising (5649312778545745371) -->
- <skip />
+ <string name="profile_label_supervising" msgid="5649312778545745371">"Nadzire se"</string>
<string name="accessibility_label_managed_profile" msgid="3366526886209832641">"Poslovni profil"</string>
<string name="accessibility_label_private_profile" msgid="1436459319135548969">"Privatan prostor"</string>
<string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Klonirano"</string>
diff --git a/core/res/res/values-be/strings.xml b/core/res/res/values-be/strings.xml
index 68fe703644a9..cbdbc23e0028 100644
--- a/core/res/res/values-be/strings.xml
+++ b/core/res/res/values-be/strings.xml
@@ -2275,18 +2275,12 @@
<string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Гартанне"</string>
<string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Прыпыніць"</string>
<string name="accessibility_autoclick_position" msgid="2933660969907663545">"Пазіцыя"</string>
- <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) -->
- <skip />
+ <string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"Прагартаць уверх"</string>
+ <string name="accessibility_autoclick_scroll_down" msgid="3733401063292018116">"Прагартаць уніз"</string>
+ <string name="accessibility_autoclick_scroll_left" msgid="8564421367992824198">"Прагартаць улева"</string>
+ <string name="accessibility_autoclick_scroll_right" msgid="8932417330753984265">"Прагартаць управа"</string>
+ <string name="accessibility_autoclick_scroll_exit" msgid="3788610039146769696">"Выйсці з рэжыму гартання"</string>
+ <string name="accessibility_autoclick_scroll_panel_title" msgid="7120598166296447036">"Панэль прагортвання"</string>
<string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"Пакет \"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g>\" дададзены ў АБМЕЖАВАНУЮ групу"</string>
<string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string>
<string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"адпраўлены відарыс"</string>
@@ -2494,8 +2488,7 @@
<string name="profile_label_work_3" msgid="4834572253956798917">"Працоўны 3"</string>
<string name="profile_label_test" msgid="9168641926186071947">"Тэставы"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"Супольны"</string>
- <!-- no translation found for profile_label_supervising (5649312778545745371) -->
- <skip />
+ <string name="profile_label_supervising" msgid="5649312778545745371">"Бацькоўскі кантроль"</string>
<string name="accessibility_label_managed_profile" msgid="3366526886209832641">"Працоўны профіль"</string>
<string name="accessibility_label_private_profile" msgid="1436459319135548969">"Прыватная прастора"</string>
<string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Клон"</string>
diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml
index 67e711f115df..a1140b930ec1 100644
--- a/core/res/res/values-bg/strings.xml
+++ b/core/res/res/values-bg/strings.xml
@@ -2486,8 +2486,7 @@
<string name="profile_label_work_3" msgid="4834572253956798917">"Служебни 3"</string>
<string name="profile_label_test" msgid="9168641926186071947">"Тестване"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"Общи"</string>
- <!-- no translation found for profile_label_supervising (5649312778545745371) -->
- <skip />
+ <string name="profile_label_supervising" msgid="5649312778545745371">"Контролиране"</string>
<string name="accessibility_label_managed_profile" msgid="3366526886209832641">"Служебен потребителски профил"</string>
<string name="accessibility_label_private_profile" msgid="1436459319135548969">"Частно пространство"</string>
<string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Клониране"</string>
diff --git a/core/res/res/values-bn/strings.xml b/core/res/res/values-bn/strings.xml
index 12bf5dab6561..426c711adbf1 100644
--- a/core/res/res/values-bn/strings.xml
+++ b/core/res/res/values-bn/strings.xml
@@ -2273,18 +2273,12 @@
<string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"স্ক্রল করুন"</string>
<string name="accessibility_autoclick_pause" msgid="3272200156172573568">"পজ করুন"</string>
<string name="accessibility_autoclick_position" msgid="2933660969907663545">"পজিশন"</string>
- <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) -->
- <skip />
+ <string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"উপর দিকে স্ক্রল করুন"</string>
+ <string name="accessibility_autoclick_scroll_down" msgid="3733401063292018116">"নিচে স্ক্রল করুন"</string>
+ <string name="accessibility_autoclick_scroll_left" msgid="8564421367992824198">"বাঁদিকে স্ক্রল করুন"</string>
+ <string name="accessibility_autoclick_scroll_right" msgid="8932417330753984265">"ডানদিকে স্ক্রল করুন"</string>
+ <string name="accessibility_autoclick_scroll_exit" msgid="3788610039146769696">"স্ক্রল মোড থেকে বেরিয়ে আসুন"</string>
+ <string name="accessibility_autoclick_scroll_panel_title" msgid="7120598166296447036">"স্ক্রল প্যানেল"</string>
<string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> সীমাবদ্ধ গ্রুপে অন্তর্ভুক্ত করা হয়েছে"</string>
<string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string>
<string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"একটি ছবি পাঠানো হয়েছে"</string>
@@ -2492,8 +2486,7 @@
<string name="profile_label_work_3" msgid="4834572253956798917">"৩য় অফিস"</string>
<string name="profile_label_test" msgid="9168641926186071947">"পরীক্ষা"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"কমিউনাল"</string>
- <!-- no translation found for profile_label_supervising (5649312778545745371) -->
- <skip />
+ <string name="profile_label_supervising" msgid="5649312778545745371">"তত্ত্বাবধান করা"</string>
<string name="accessibility_label_managed_profile" msgid="3366526886209832641">"অফিস প্রোফাইল"</string>
<string name="accessibility_label_private_profile" msgid="1436459319135548969">"প্রাইভেট স্পেস"</string>
<string name="accessibility_label_clone_profile" msgid="7579118375042398784">"ক্লোন"</string>
diff --git a/core/res/res/values-bs/strings.xml b/core/res/res/values-bs/strings.xml
index 441d51461f08..947f6bacafc7 100644
--- a/core/res/res/values-bs/strings.xml
+++ b/core/res/res/values-bs/strings.xml
@@ -2274,18 +2274,12 @@
<string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Klizanje"</string>
<string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Pauziraj"</string>
<string name="accessibility_autoclick_position" msgid="2933660969907663545">"Položaj"</string>
- <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) -->
- <skip />
+ <string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"Klizanje nagore"</string>
+ <string name="accessibility_autoclick_scroll_down" msgid="3733401063292018116">"Klizanje nadolje"</string>
+ <string name="accessibility_autoclick_scroll_left" msgid="8564421367992824198">"Klizanje ulijevo"</string>
+ <string name="accessibility_autoclick_scroll_right" msgid="8932417330753984265">"Klizanje udesno"</string>
+ <string name="accessibility_autoclick_scroll_exit" msgid="3788610039146769696">"Izlaz iz načina rada za klizanje"</string>
+ <string name="accessibility_autoclick_scroll_panel_title" msgid="7120598166296447036">"Ploča za klizanje"</string>
<string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"Paket <xliff:g id="PACKAGE_NAME">%1$s</xliff:g> je stavljen u odjeljak OGRANIČENO"</string>
<string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string>
<string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"je poslao/la sliku"</string>
@@ -2493,8 +2487,7 @@
<string name="profile_label_work_3" msgid="4834572253956798917">"3. poslovno"</string>
<string name="profile_label_test" msgid="9168641926186071947">"Testno"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"Opće"</string>
- <!-- no translation found for profile_label_supervising (5649312778545745371) -->
- <skip />
+ <string name="profile_label_supervising" msgid="5649312778545745371">"Nadzor"</string>
<string name="accessibility_label_managed_profile" msgid="3366526886209832641">"Radni profil"</string>
<string name="accessibility_label_private_profile" msgid="1436459319135548969">"Privatni prostor"</string>
<string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Klon"</string>
diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml
index efde1506e7be..8dcf67195842 100644
--- a/core/res/res/values-ca/strings.xml
+++ b/core/res/res/values-ca/strings.xml
@@ -2274,18 +2274,12 @@
<string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Desplaça"</string>
<string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Posa en pausa"</string>
<string name="accessibility_autoclick_position" msgid="2933660969907663545">"Posició"</string>
- <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) -->
- <skip />
+ <string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"Desplaça\'t cap amunt"</string>
+ <string name="accessibility_autoclick_scroll_down" msgid="3733401063292018116">"Desplaça\'t cap avall"</string>
+ <string name="accessibility_autoclick_scroll_left" msgid="8564421367992824198">"Desplaça\'t cap a l\'esquerra"</string>
+ <string name="accessibility_autoclick_scroll_right" msgid="8932417330753984265">"Desplaça\'t cap a la dreta"</string>
+ <string name="accessibility_autoclick_scroll_exit" msgid="3788610039146769696">"Surt del mode de desplaçament"</string>
+ <string name="accessibility_autoclick_scroll_panel_title" msgid="7120598166296447036">"Tauler de desplaçament"</string>
<string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> s\'ha transferit al segment RESTRINGIT"</string>
<string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string>
<string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"ha enviat una imatge"</string>
@@ -2493,8 +2487,7 @@
<string name="profile_label_work_3" msgid="4834572253956798917">"Treball 3"</string>
<string name="profile_label_test" msgid="9168641926186071947">"Prova"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"Compartit"</string>
- <!-- no translation found for profile_label_supervising (5649312778545745371) -->
- <skip />
+ <string name="profile_label_supervising" msgid="5649312778545745371">"En supervisió"</string>
<string name="accessibility_label_managed_profile" msgid="3366526886209832641">"Perfil de treball"</string>
<string name="accessibility_label_private_profile" msgid="1436459319135548969">"Espai privat"</string>
<string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Clon"</string>
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index 59de93743290..61d9249e7774 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -2275,18 +2275,12 @@
<string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Posunutí"</string>
<string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Pozastavit"</string>
<string name="accessibility_autoclick_position" msgid="2933660969907663545">"Pozice"</string>
- <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) -->
- <skip />
+ <string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"Posunout nahoru"</string>
+ <string name="accessibility_autoclick_scroll_down" msgid="3733401063292018116">"Posunout dolů"</string>
+ <string name="accessibility_autoclick_scroll_left" msgid="8564421367992824198">"Posunout doleva"</string>
+ <string name="accessibility_autoclick_scroll_right" msgid="8932417330753984265">"Posunout doprava"</string>
+ <string name="accessibility_autoclick_scroll_exit" msgid="3788610039146769696">"Ukončit režim posouvání"</string>
+ <string name="accessibility_autoclick_scroll_panel_title" msgid="7120598166296447036">"Panel posouvání"</string>
<string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"Balíček <xliff:g id="PACKAGE_NAME">%1$s</xliff:g> byl vložen do sekce OMEZENO"</string>
<string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string>
<string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"posílá obrázek"</string>
@@ -2494,8 +2488,7 @@
<string name="profile_label_work_3" msgid="4834572253956798917">"Práce 3"</string>
<string name="profile_label_test" msgid="9168641926186071947">"Test"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"Komunální"</string>
- <!-- no translation found for profile_label_supervising (5649312778545745371) -->
- <skip />
+ <string name="profile_label_supervising" msgid="5649312778545745371">"Dohled"</string>
<string name="accessibility_label_managed_profile" msgid="3366526886209832641">"Pracovní profil"</string>
<string name="accessibility_label_private_profile" msgid="1436459319135548969">"Soukromý prostor"</string>
<string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Klon"</string>
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index 1038ff0bcf8f..8d8918fbf16e 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -2273,18 +2273,12 @@
<string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Rul"</string>
<string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Sæt på pause"</string>
<string name="accessibility_autoclick_position" msgid="2933660969907663545">"Placering"</string>
- <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) -->
- <skip />
+ <string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"Rul op"</string>
+ <string name="accessibility_autoclick_scroll_down" msgid="3733401063292018116">"Rul ned"</string>
+ <string name="accessibility_autoclick_scroll_left" msgid="8564421367992824198">"Rul til venstre"</string>
+ <string name="accessibility_autoclick_scroll_right" msgid="8932417330753984265">"Rul til højre"</string>
+ <string name="accessibility_autoclick_scroll_exit" msgid="3788610039146769696">"Afslut rulletilstand"</string>
+ <string name="accessibility_autoclick_scroll_panel_title" msgid="7120598166296447036">"Rullepanel"</string>
<string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> er blevet placeret i samlingen BEGRÆNSET"</string>
<string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string>
<string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"sendte et billede"</string>
@@ -2492,8 +2486,7 @@
<string name="profile_label_work_3" msgid="4834572253956798917">"Arbejde 3"</string>
<string name="profile_label_test" msgid="9168641926186071947">"Test"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"Fælles"</string>
- <!-- no translation found for profile_label_supervising (5649312778545745371) -->
- <skip />
+ <string name="profile_label_supervising" msgid="5649312778545745371">"Forældrestyring"</string>
<string name="accessibility_label_managed_profile" msgid="3366526886209832641">"Arbejdsprofil"</string>
<string name="accessibility_label_private_profile" msgid="1436459319135548969">"Privat område"</string>
<string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Klon"</string>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index c13dacad4dea..15edddee0dbe 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -2273,18 +2273,12 @@
<string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Scrollen"</string>
<string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Pausieren"</string>
<string name="accessibility_autoclick_position" msgid="2933660969907663545">"Position"</string>
- <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) -->
- <skip />
+ <string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"Nach oben scrollen"</string>
+ <string name="accessibility_autoclick_scroll_down" msgid="3733401063292018116">"Nach unten scrollen"</string>
+ <string name="accessibility_autoclick_scroll_left" msgid="8564421367992824198">"Nach links scrollen"</string>
+ <string name="accessibility_autoclick_scroll_right" msgid="8932417330753984265">"Nach rechts scrollen"</string>
+ <string name="accessibility_autoclick_scroll_exit" msgid="3788610039146769696">"Scrollmodus beenden"</string>
+ <string name="accessibility_autoclick_scroll_panel_title" msgid="7120598166296447036">"Bildlaufleiste"</string>
<string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> wurde in den BESCHRÄNKT-Bucket gelegt"</string>
<string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string>
<string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"hat ein Bild gesendet"</string>
@@ -2492,8 +2486,7 @@
<string name="profile_label_work_3" msgid="4834572253956798917">"Geschäftlich 3"</string>
<string name="profile_label_test" msgid="9168641926186071947">"Test"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"Gemeinsam genutzt"</string>
- <!-- no translation found for profile_label_supervising (5649312778545745371) -->
- <skip />
+ <string name="profile_label_supervising" msgid="5649312778545745371">"Elternaufsicht"</string>
<string name="accessibility_label_managed_profile" msgid="3366526886209832641">"Arbeitsprofil"</string>
<string name="accessibility_label_private_profile" msgid="1436459319135548969">"Vertrauliches Profil"</string>
<string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Klon"</string>
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index 50b06dfbc8b6..981c56d639ae 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -1451,12 +1451,12 @@
<string name="usb_power_notification_message" msgid="7284765627437897702">"Φόρτιση συνδεδεμένης συσκευής. Πατήστε για περισσότερες επιλογές."</string>
<string name="usb_unsupported_audio_accessory_title" msgid="2335775548086533065">"Εντοπίστηκε αναλογικό αξεσουάρ ήχου"</string>
<string name="usb_unsupported_audio_accessory_message" msgid="1300168007129796621">"Η συνδεδεμένη συσκευή δεν είναι συμβατή με αυτό το τηλέφωνο. Πατήστε για να μάθετε περισσότερα."</string>
- <string name="adb_active_notification_title" msgid="408390247354560331">"Eνεργός εντοπ. σφαλματών USB"</string>
- <string name="adb_active_notification_message" msgid="5617264033476778211">"Πατήστε για απενεργ. εντοπ. σφαλμ. USB"</string>
- <string name="adb_active_notification_message" product="tv" msgid="6624498401272780855">"Επιλογή για απενεργοποίηση του εντοπισμού σφαλμάτων USB."</string>
- <string name="adbwifi_active_notification_title" msgid="6147343659168302473">"Συνδέθηκε ο ασύρματος εντοπισμός σφαλμάτων"</string>
- <string name="adbwifi_active_notification_message" msgid="930987922852867972">"Πατήστε, για να απενεργοποιήσετε τον ασύρματο εντοπισμό σφαλμάτων"</string>
- <string name="adbwifi_active_notification_message" product="tv" msgid="8633421848366915478">"Επιλέξτε, για να απενεργοποιήσετε τον ασύρματο εντοπισμό σφαλμάτων."</string>
+ <string name="adb_active_notification_title" msgid="408390247354560331">"Eνεργή αποσφαλμάτωση USB"</string>
+ <string name="adb_active_notification_message" msgid="5617264033476778211">"Πατήστε για απενεργ. αποσφαλμάτ. USB"</string>
+ <string name="adb_active_notification_message" product="tv" msgid="6624498401272780855">"Επιλογή για απενεργοποίηση του αποσφαλμάτωσης USB."</string>
+ <string name="adbwifi_active_notification_title" msgid="6147343659168302473">"Συνδέθηκε η ασύρματη αποσφαλμάτωση"</string>
+ <string name="adbwifi_active_notification_message" msgid="930987922852867972">"Πατήστε, για να απενεργοποιήσετε την ασύρματη αποσφαλμάτωση"</string>
+ <string name="adbwifi_active_notification_message" product="tv" msgid="8633421848366915478">"Επιλέξτε, για να απενεργοποιήσετε την ασύρματη αποσφαλμάτωση."</string>
<string name="test_harness_mode_notification_title" msgid="2282785860014142511">"Η λειτουργία περιβάλλοντος δοκιμών ενεργοποιήθηκε"</string>
<string name="test_harness_mode_notification_message" msgid="3039123743127958420">"Εκτελέστε επαναφορά εργοστασιακών ρυθμίσεων για να απενεργοποιήσετε τη λειτουργία περιβάλλοντος δοκιμών."</string>
<string name="wrong_hsum_configuration_notification_title" msgid="7212758829332714385">"Λάθος διαμόρφωση κατασκευής HSUM"</string>
@@ -2127,7 +2127,7 @@
<string name="app_category_productivity" msgid="1844422703029557883">"Παραγωγικότητα"</string>
<string name="app_category_accessibility" msgid="6643521607848547683">"Προσβασιμότητα"</string>
<string name="device_storage_monitor_notification_channel" msgid="5164244565844470758">"Αποθηκευτικός χώρος συσκευής"</string>
- <string name="adb_debugging_notification_channel_tv" msgid="4764046459631031496">"Εντοπισμός σφαλμάτων USB"</string>
+ <string name="adb_debugging_notification_channel_tv" msgid="4764046459631031496">"Αποσφαλμάτωση USB"</string>
<string name="time_picker_hour_label" msgid="4208590187662336864">"ώρα"</string>
<string name="time_picker_minute_label" msgid="8307452311269824553">"λεπτό"</string>
<string name="time_picker_header_text" msgid="9073802285051516688">"Ορισμός ώρας"</string>
@@ -2273,18 +2273,12 @@
<string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Κύλιση"</string>
<string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Παύση"</string>
<string name="accessibility_autoclick_position" msgid="2933660969907663545">"Θέση"</string>
- <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) -->
- <skip />
+ <string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"Κύλιση προς τα επάνω"</string>
+ <string name="accessibility_autoclick_scroll_down" msgid="3733401063292018116">"Κύλιση προς τα κάτω"</string>
+ <string name="accessibility_autoclick_scroll_left" msgid="8564421367992824198">"Κύλιση προς τα αριστερά"</string>
+ <string name="accessibility_autoclick_scroll_right" msgid="8932417330753984265">"Κύλιση προς τα δεξιά"</string>
+ <string name="accessibility_autoclick_scroll_exit" msgid="3788610039146769696">"Έξοδος από τη λειτουργία κύλισης"</string>
+ <string name="accessibility_autoclick_scroll_panel_title" msgid="7120598166296447036">"Πλαίσιο κύλισης"</string>
<string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"Το πακέτο <xliff:g id="PACKAGE_NAME">%1$s</xliff:g> τοποθετήθηκε στον κάδο ΠΕΡΙΟΡΙΣΜΕΝΗΣ ΠΡΟΣΒΑΣΗΣ."</string>
<string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string>
<string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"έστειλε μια εικόνα"</string>
@@ -2492,8 +2486,7 @@
<string name="profile_label_work_3" msgid="4834572253956798917">"Εργασία 3"</string>
<string name="profile_label_test" msgid="9168641926186071947">"Δοκιμή"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"Κοινόχρηστο"</string>
- <!-- no translation found for profile_label_supervising (5649312778545745371) -->
- <skip />
+ <string name="profile_label_supervising" msgid="5649312778545745371">"Επίβλεψη"</string>
<string name="accessibility_label_managed_profile" msgid="3366526886209832641">"Προφίλ εργασίας"</string>
<string name="accessibility_label_private_profile" msgid="1436459319135548969">"Ιδιωτικός χώρος"</string>
<string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Κλώνος"</string>
diff --git a/core/res/res/values-en-rAU/strings.xml b/core/res/res/values-en-rAU/strings.xml
index 74462e996b5b..532fee688acd 100644
--- a/core/res/res/values-en-rAU/strings.xml
+++ b/core/res/res/values-en-rAU/strings.xml
@@ -2273,18 +2273,12 @@
<string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Scroll"</string>
<string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Pause"</string>
<string name="accessibility_autoclick_position" msgid="2933660969907663545">"Position"</string>
- <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) -->
- <skip />
+ <string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"Scroll up"</string>
+ <string name="accessibility_autoclick_scroll_down" msgid="3733401063292018116">"Scroll down"</string>
+ <string name="accessibility_autoclick_scroll_left" msgid="8564421367992824198">"Scroll left"</string>
+ <string name="accessibility_autoclick_scroll_right" msgid="8932417330753984265">"Scroll right"</string>
+ <string name="accessibility_autoclick_scroll_exit" msgid="3788610039146769696">"Exit scroll mode"</string>
+ <string name="accessibility_autoclick_scroll_panel_title" msgid="7120598166296447036">"Scroll panel"</string>
<string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> has been put into the RESTRICTED bucket"</string>
<string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string>
<string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"sent an image"</string>
@@ -2492,8 +2486,7 @@
<string name="profile_label_work_3" msgid="4834572253956798917">"Work 3"</string>
<string name="profile_label_test" msgid="9168641926186071947">"Test"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"Communal"</string>
- <!-- no translation found for profile_label_supervising (5649312778545745371) -->
- <skip />
+ <string name="profile_label_supervising" msgid="5649312778545745371">"Supervising"</string>
<string name="accessibility_label_managed_profile" msgid="3366526886209832641">"Work profile"</string>
<string name="accessibility_label_private_profile" msgid="1436459319135548969">"Private space"</string>
<string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Clone"</string>
diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml
index 9fa268338d4a..c079a4804057 100644
--- a/core/res/res/values-en-rGB/strings.xml
+++ b/core/res/res/values-en-rGB/strings.xml
@@ -2273,18 +2273,12 @@
<string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Scroll"</string>
<string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Pause"</string>
<string name="accessibility_autoclick_position" msgid="2933660969907663545">"Position"</string>
- <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) -->
- <skip />
+ <string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"Scroll up"</string>
+ <string name="accessibility_autoclick_scroll_down" msgid="3733401063292018116">"Scroll down"</string>
+ <string name="accessibility_autoclick_scroll_left" msgid="8564421367992824198">"Scroll left"</string>
+ <string name="accessibility_autoclick_scroll_right" msgid="8932417330753984265">"Scroll right"</string>
+ <string name="accessibility_autoclick_scroll_exit" msgid="3788610039146769696">"Exit scroll mode"</string>
+ <string name="accessibility_autoclick_scroll_panel_title" msgid="7120598166296447036">"Scroll panel"</string>
<string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> has been put into the RESTRICTED bucket"</string>
<string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string>
<string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"sent an image"</string>
@@ -2492,8 +2486,7 @@
<string name="profile_label_work_3" msgid="4834572253956798917">"Work 3"</string>
<string name="profile_label_test" msgid="9168641926186071947">"Test"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"Communal"</string>
- <!-- no translation found for profile_label_supervising (5649312778545745371) -->
- <skip />
+ <string name="profile_label_supervising" msgid="5649312778545745371">"Supervising"</string>
<string name="accessibility_label_managed_profile" msgid="3366526886209832641">"Work profile"</string>
<string name="accessibility_label_private_profile" msgid="1436459319135548969">"Private space"</string>
<string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Clone"</string>
diff --git a/core/res/res/values-en-rIN/strings.xml b/core/res/res/values-en-rIN/strings.xml
index e162a6094719..62daeffa9043 100644
--- a/core/res/res/values-en-rIN/strings.xml
+++ b/core/res/res/values-en-rIN/strings.xml
@@ -2273,18 +2273,12 @@
<string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Scroll"</string>
<string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Pause"</string>
<string name="accessibility_autoclick_position" msgid="2933660969907663545">"Position"</string>
- <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) -->
- <skip />
+ <string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"Scroll up"</string>
+ <string name="accessibility_autoclick_scroll_down" msgid="3733401063292018116">"Scroll down"</string>
+ <string name="accessibility_autoclick_scroll_left" msgid="8564421367992824198">"Scroll left"</string>
+ <string name="accessibility_autoclick_scroll_right" msgid="8932417330753984265">"Scroll right"</string>
+ <string name="accessibility_autoclick_scroll_exit" msgid="3788610039146769696">"Exit scroll mode"</string>
+ <string name="accessibility_autoclick_scroll_panel_title" msgid="7120598166296447036">"Scroll panel"</string>
<string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> has been put into the RESTRICTED bucket"</string>
<string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string>
<string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"sent an image"</string>
@@ -2492,8 +2486,7 @@
<string name="profile_label_work_3" msgid="4834572253956798917">"Work 3"</string>
<string name="profile_label_test" msgid="9168641926186071947">"Test"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"Communal"</string>
- <!-- no translation found for profile_label_supervising (5649312778545745371) -->
- <skip />
+ <string name="profile_label_supervising" msgid="5649312778545745371">"Supervising"</string>
<string name="accessibility_label_managed_profile" msgid="3366526886209832641">"Work profile"</string>
<string name="accessibility_label_private_profile" msgid="1436459319135548969">"Private space"</string>
<string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Clone"</string>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index 330039740ea1..b5a5e8f0deeb 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -2274,18 +2274,12 @@
<string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Desplazarse"</string>
<string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Pausar"</string>
<string name="accessibility_autoclick_position" msgid="2933660969907663545">"Posición"</string>
- <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) -->
- <skip />
+ <string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"Desplazarse hacia arriba"</string>
+ <string name="accessibility_autoclick_scroll_down" msgid="3733401063292018116">"Desplazarse hacia abajo"</string>
+ <string name="accessibility_autoclick_scroll_left" msgid="8564421367992824198">"Desplazarse a la izquierda"</string>
+ <string name="accessibility_autoclick_scroll_right" msgid="8932417330753984265">"Desplazarse a la derecha"</string>
+ <string name="accessibility_autoclick_scroll_exit" msgid="3788610039146769696">"Salir del modo de desplazamiento"</string>
+ <string name="accessibility_autoclick_scroll_panel_title" msgid="7120598166296447036">"Panel de desplazamiento"</string>
<string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> se ha incluido en el grupo de restringidos"</string>
<string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string>
<string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"ha enviado una imagen"</string>
@@ -2493,8 +2487,7 @@
<string name="profile_label_work_3" msgid="4834572253956798917">"Trabajo 3"</string>
<string name="profile_label_test" msgid="9168641926186071947">"Prueba"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"Común"</string>
- <!-- no translation found for profile_label_supervising (5649312778545745371) -->
- <skip />
+ <string name="profile_label_supervising" msgid="5649312778545745371">"Supervisando"</string>
<string name="accessibility_label_managed_profile" msgid="3366526886209832641">"Perfil de trabajo"</string>
<string name="accessibility_label_private_profile" msgid="1436459319135548969">"Espacio privado"</string>
<string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Clon"</string>
diff --git a/core/res/res/values-et/strings.xml b/core/res/res/values-et/strings.xml
index c172de8671c9..99f9791e6ff1 100644
--- a/core/res/res/values-et/strings.xml
+++ b/core/res/res/values-et/strings.xml
@@ -2273,18 +2273,12 @@
<string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Keri"</string>
<string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Peata"</string>
<string name="accessibility_autoclick_position" msgid="2933660969907663545">"Asukoht"</string>
- <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) -->
- <skip />
+ <string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"Keri üles"</string>
+ <string name="accessibility_autoclick_scroll_down" msgid="3733401063292018116">"Keri alla"</string>
+ <string name="accessibility_autoclick_scroll_left" msgid="8564421367992824198">"Keri vasakule"</string>
+ <string name="accessibility_autoclick_scroll_right" msgid="8932417330753984265">"Keri paremale"</string>
+ <string name="accessibility_autoclick_scroll_exit" msgid="3788610039146769696">"Välju kerimisrežiimist"</string>
+ <string name="accessibility_autoclick_scroll_panel_title" msgid="7120598166296447036">"Keri paneelil"</string>
<string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> on lisatud salve PIIRANGUTEGA"</string>
<string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string>
<string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"saatis kujutise"</string>
@@ -2492,8 +2486,7 @@
<string name="profile_label_work_3" msgid="4834572253956798917">"Töö 3"</string>
<string name="profile_label_test" msgid="9168641926186071947">"Test"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"Jagatud"</string>
- <!-- no translation found for profile_label_supervising (5649312778545745371) -->
- <skip />
+ <string name="profile_label_supervising" msgid="5649312778545745371">"Järelevalve"</string>
<string name="accessibility_label_managed_profile" msgid="3366526886209832641">"Tööprofiil"</string>
<string name="accessibility_label_private_profile" msgid="1436459319135548969">"Privaatne ruum"</string>
<string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Kloon"</string>
diff --git a/core/res/res/values-eu/strings.xml b/core/res/res/values-eu/strings.xml
index 24dbe4bb580c..bbf4c996cc4f 100644
--- a/core/res/res/values-eu/strings.xml
+++ b/core/res/res/values-eu/strings.xml
@@ -2273,18 +2273,12 @@
<string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Egin gora eta behera"</string>
<string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Pausatu"</string>
<string name="accessibility_autoclick_position" msgid="2933660969907663545">"Ezarri posizioan"</string>
- <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) -->
- <skip />
+ <string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"Egin gora"</string>
+ <string name="accessibility_autoclick_scroll_down" msgid="3733401063292018116">"Egin behera"</string>
+ <string name="accessibility_autoclick_scroll_left" msgid="8564421367992824198">"Egin ezkerrera"</string>
+ <string name="accessibility_autoclick_scroll_right" msgid="8932417330753984265">"Egin eskuinera"</string>
+ <string name="accessibility_autoclick_scroll_exit" msgid="3788610039146769696">"Irten gora/behera egiteko modutik"</string>
+ <string name="accessibility_autoclick_scroll_panel_title" msgid="7120598166296447036">"Gora/Behera egiteko panela"</string>
<string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"Murriztuen edukiontzian ezarri da <xliff:g id="PACKAGE_NAME">%1$s</xliff:g>"</string>
<string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string>
<string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"erabiltzaileak irudi bat bidali du"</string>
@@ -2492,8 +2486,7 @@
<string name="profile_label_work_3" msgid="4834572253956798917">"Lanekoa 3"</string>
<string name="profile_label_test" msgid="9168641926186071947">"Probakoa"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"Partekatua"</string>
- <!-- no translation found for profile_label_supervising (5649312778545745371) -->
- <skip />
+ <string name="profile_label_supervising" msgid="5649312778545745371">"Gainbegiratzea"</string>
<string name="accessibility_label_managed_profile" msgid="3366526886209832641">"Laneko profila"</string>
<string name="accessibility_label_private_profile" msgid="1436459319135548969">"Eremu pribatua"</string>
<string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Klona"</string>
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index f31bb1044c96..820a80c1f411 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -2486,8 +2486,7 @@
<string name="profile_label_work_3" msgid="4834572253956798917">"کار ۳"</string>
<string name="profile_label_test" msgid="9168641926186071947">"آزمایش"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"عمومی"</string>
- <!-- no translation found for profile_label_supervising (5649312778545745371) -->
- <skip />
+ <string name="profile_label_supervising" msgid="5649312778545745371">"نظارت"</string>
<string name="accessibility_label_managed_profile" msgid="3366526886209832641">"نمایه کاری"</string>
<string name="accessibility_label_private_profile" msgid="1436459319135548969">"فضای خصوصی"</string>
<string name="accessibility_label_clone_profile" msgid="7579118375042398784">"همسانه‌سازی"</string>
diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml
index f1a5fbcdbcc4..0083fd9a709b 100644
--- a/core/res/res/values-fi/strings.xml
+++ b/core/res/res/values-fi/strings.xml
@@ -1214,8 +1214,8 @@
<string name="Noon" msgid="6902418443846838189">"Keskipäivä"</string>
<string name="midnight" msgid="3646671134282785114">"keskiyö"</string>
<string name="Midnight" msgid="8176019203622191377">"Keskiyö"</string>
- <string name="elapsed_time_short_format_mm_ss" msgid="8689459651807876423">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string>
- <string name="elapsed_time_short_format_h_mm_ss" msgid="2302144714803345056">"<xliff:g id="HOURS">%1$d</xliff:g>:<xliff:g id="MINUTES">%2$02d</xliff:g>:<xliff:g id="SECONDS">%3$02d</xliff:g>"</string>
+ <string name="elapsed_time_short_format_mm_ss" msgid="8689459651807876423">"<xliff:g id="MINUTES">%1$02d</xliff:g>.<xliff:g id="SECONDS">%2$02d</xliff:g>"</string>
+ <string name="elapsed_time_short_format_h_mm_ss" msgid="2302144714803345056">"<xliff:g id="HOURS">%1$d</xliff:g>.<xliff:g id="MINUTES">%2$02d</xliff:g>.<xliff:g id="SECONDS">%3$02d</xliff:g>"</string>
<string name="selectAll" msgid="1532369154488982046">"Valitse kaikki"</string>
<string name="cut" msgid="2561199725874745819">"Leikkaa"</string>
<string name="copy" msgid="5472512047143665218">"Kopioi"</string>
@@ -2273,18 +2273,12 @@
<string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Vieritä"</string>
<string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Keskeytä"</string>
<string name="accessibility_autoclick_position" msgid="2933660969907663545">"Sijainti"</string>
- <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) -->
- <skip />
+ <string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"Vieritys ylös"</string>
+ <string name="accessibility_autoclick_scroll_down" msgid="3733401063292018116">"Vieritä alas"</string>
+ <string name="accessibility_autoclick_scroll_left" msgid="8564421367992824198">"Vieritä vasemmalle"</string>
+ <string name="accessibility_autoclick_scroll_right" msgid="8932417330753984265">"Vieritä oikealle"</string>
+ <string name="accessibility_autoclick_scroll_exit" msgid="3788610039146769696">"Poistu vieritystilasta"</string>
+ <string name="accessibility_autoclick_scroll_panel_title" msgid="7120598166296447036">"Vierityspaneeli"</string>
<string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> on nyt rajoitettujen ryhmässä"</string>
<string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string>
<string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"lähetti kuvan"</string>
@@ -2492,8 +2486,7 @@
<string name="profile_label_work_3" msgid="4834572253956798917">"Työ 3"</string>
<string name="profile_label_test" msgid="9168641926186071947">"Testi"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"Jaettu"</string>
- <!-- no translation found for profile_label_supervising (5649312778545745371) -->
- <skip />
+ <string name="profile_label_supervising" msgid="5649312778545745371">"Valvotaan"</string>
<string name="accessibility_label_managed_profile" msgid="3366526886209832641">"Työprofiili"</string>
<string name="accessibility_label_private_profile" msgid="1436459319135548969">"Yksityinen tila"</string>
<string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Klooni"</string>
diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml
index a7b12891d7b1..127e6fcc97f7 100644
--- a/core/res/res/values-fr-rCA/strings.xml
+++ b/core/res/res/values-fr-rCA/strings.xml
@@ -2487,8 +2487,7 @@
<string name="profile_label_work_3" msgid="4834572253956798917">"Professionnel 3"</string>
<string name="profile_label_test" msgid="9168641926186071947">"Test"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"Commun"</string>
- <!-- no translation found for profile_label_supervising (5649312778545745371) -->
- <skip />
+ <string name="profile_label_supervising" msgid="5649312778545745371">"Supervision"</string>
<string name="accessibility_label_managed_profile" msgid="3366526886209832641">"Profil professionnel"</string>
<string name="accessibility_label_private_profile" msgid="1436459319135548969">"Espace privé"</string>
<string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Clone"</string>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index 213c25696ec0..a30c414038af 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -2274,18 +2274,12 @@
<string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Faire défiler"</string>
<string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Pause"</string>
<string name="accessibility_autoclick_position" msgid="2933660969907663545">"Position"</string>
- <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) -->
- <skip />
+ <string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"Faire défiler vers le haut"</string>
+ <string name="accessibility_autoclick_scroll_down" msgid="3733401063292018116">"Faire défiler vers le bas"</string>
+ <string name="accessibility_autoclick_scroll_left" msgid="8564421367992824198">"Faire défiler vers la gauche"</string>
+ <string name="accessibility_autoclick_scroll_right" msgid="8932417330753984265">"Faire défiler vers la droite"</string>
+ <string name="accessibility_autoclick_scroll_exit" msgid="3788610039146769696">"Quitter le mode défilement"</string>
+ <string name="accessibility_autoclick_scroll_panel_title" msgid="7120598166296447036">"Panneau de défilement"</string>
<string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> a été placé dans le bucket RESTRICTED"</string>
<string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g> :"</string>
<string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"a envoyé une image"</string>
@@ -2493,8 +2487,7 @@
<string name="profile_label_work_3" msgid="4834572253956798917">"Professionnel 3"</string>
<string name="profile_label_test" msgid="9168641926186071947">"Test"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"Commun"</string>
- <!-- no translation found for profile_label_supervising (5649312778545745371) -->
- <skip />
+ <string name="profile_label_supervising" msgid="5649312778545745371">"Supervision"</string>
<string name="accessibility_label_managed_profile" msgid="3366526886209832641">"Profil professionnel"</string>
<string name="accessibility_label_private_profile" msgid="1436459319135548969">"Espace privé"</string>
<string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Clone"</string>
diff --git a/core/res/res/values-gl/strings.xml b/core/res/res/values-gl/strings.xml
index 8c265b02b93e..d79e3c63e084 100644
--- a/core/res/res/values-gl/strings.xml
+++ b/core/res/res/values-gl/strings.xml
@@ -2273,18 +2273,12 @@
<string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Desprazar"</string>
<string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Pausa"</string>
<string name="accessibility_autoclick_position" msgid="2933660969907663545">"Posición"</string>
- <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) -->
- <skip />
+ <string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"Desprazarse cara arriba"</string>
+ <string name="accessibility_autoclick_scroll_down" msgid="3733401063292018116">"Desprazarse cara abaixo"</string>
+ <string name="accessibility_autoclick_scroll_left" msgid="8564421367992824198">"Desprazar cara á esquerda"</string>
+ <string name="accessibility_autoclick_scroll_right" msgid="8932417330753984265">"Desprazar cara á dereita"</string>
+ <string name="accessibility_autoclick_scroll_exit" msgid="3788610039146769696">"Saír do modo de desprazamento"</string>
+ <string name="accessibility_autoclick_scroll_panel_title" msgid="7120598166296447036">"Panel de desprazamento"</string>
<string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> incluíuse no grupo RESTRINXIDO"</string>
<string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string>
<string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"enviouse unha imaxe"</string>
@@ -2492,8 +2486,7 @@
<string name="profile_label_work_3" msgid="4834572253956798917">"Traballo 3"</string>
<string name="profile_label_test" msgid="9168641926186071947">"Proba"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"Compartido"</string>
- <!-- no translation found for profile_label_supervising (5649312778545745371) -->
- <skip />
+ <string name="profile_label_supervising" msgid="5649312778545745371">"Supervisión"</string>
<string name="accessibility_label_managed_profile" msgid="3366526886209832641">"Perfil de traballo"</string>
<string name="accessibility_label_private_profile" msgid="1436459319135548969">"Espazo privado"</string>
<string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Clonado"</string>
diff --git a/core/res/res/values-gu/strings.xml b/core/res/res/values-gu/strings.xml
index c4da34989482..1d2f975da5e6 100644
--- a/core/res/res/values-gu/strings.xml
+++ b/core/res/res/values-gu/strings.xml
@@ -1803,8 +1803,7 @@
<string name="one_handed_mode_feature_name" msgid="2334330034828094891">"એક-હાથે વાપરો મોડ"</string>
<string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"એક્સ્ટ્રા ડિમ"</string>
<string name="hearing_aids_feature_name" msgid="1125892105105852542">"સાંભળવામાં સહાય કરતા ડિવાઇસ"</string>
- <!-- no translation found for autoclick_feature_name (8149248738736949630) -->
- <skip />
+ <string name="autoclick_feature_name" msgid="8149248738736949630">"ઑટોક્લિક"</string>
<string name="hearing_device_status_disconnected" msgid="497547752953543832">"ડિસ્કનેક્ટેડ છે"</string>
<string name="hearing_device_status_connected" msgid="2149385149669918764">"કનેક્ટેડ છે"</string>
<string name="hearing_device_status_active" msgid="4770378695482566032">"સક્રિય"</string>
@@ -2274,18 +2273,12 @@
<string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"સ્ક્રોલ કરો"</string>
<string name="accessibility_autoclick_pause" msgid="3272200156172573568">"થોભાવો"</string>
<string name="accessibility_autoclick_position" msgid="2933660969907663545">"સ્થિતિ"</string>
- <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) -->
- <skip />
+ <string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"ઉપર સ્ક્રોલ કરો"</string>
+ <string name="accessibility_autoclick_scroll_down" msgid="3733401063292018116">"નીચે સ્ક્રોલ કરો"</string>
+ <string name="accessibility_autoclick_scroll_left" msgid="8564421367992824198">"ડાબે સ્ક્રોલ કરો"</string>
+ <string name="accessibility_autoclick_scroll_right" msgid="8932417330753984265">"જમણે સ્ક્રોલ કરો"</string>
+ <string name="accessibility_autoclick_scroll_exit" msgid="3788610039146769696">"સ્ક્રોલ મોડમાંથી બહાર નીકળો"</string>
+ <string name="accessibility_autoclick_scroll_panel_title" msgid="7120598166296447036">"સ્ક્રોલ પૅનલ"</string>
<string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g>ને પ્રતિબંધિત સમૂહમાં મૂકવામાં આવ્યું છે"</string>
<string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string>
<string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"છબી મોકલી"</string>
@@ -2493,8 +2486,7 @@
<string name="profile_label_work_3" msgid="4834572253956798917">"ઑફિસ 3"</string>
<string name="profile_label_test" msgid="9168641926186071947">"પરીક્ષણ કરો"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"કૉમ્યુનલ"</string>
- <!-- no translation found for profile_label_supervising (5649312778545745371) -->
- <skip />
+ <string name="profile_label_supervising" msgid="5649312778545745371">"નિરીક્ષણ કરનાર"</string>
<string name="accessibility_label_managed_profile" msgid="3366526886209832641">"ઑફિસની પ્રોફાઇલ"</string>
<string name="accessibility_label_private_profile" msgid="1436459319135548969">"ખાનગી સ્પેસ"</string>
<string name="accessibility_label_clone_profile" msgid="7579118375042398784">"ક્લોન"</string>
diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml
index 01e5c7786cb7..b8be4057d480 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -2273,18 +2273,12 @@
<string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"स्क्रोल करें"</string>
<string name="accessibility_autoclick_pause" msgid="3272200156172573568">"रोकें"</string>
<string name="accessibility_autoclick_position" msgid="2933660969907663545">"पोज़िशन"</string>
- <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) -->
- <skip />
+ <string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"ऊपर की ओर स्क्रोल करें"</string>
+ <string name="accessibility_autoclick_scroll_down" msgid="3733401063292018116">"नीचे की ओर स्क्रोल करें"</string>
+ <string name="accessibility_autoclick_scroll_left" msgid="8564421367992824198">"बाईं ओर स्क्रोल करें"</string>
+ <string name="accessibility_autoclick_scroll_right" msgid="8932417330753984265">"दाईं ओर स्क्रोल करें"</string>
+ <string name="accessibility_autoclick_scroll_exit" msgid="3788610039146769696">"स्क्रोल मोड को बंद करें"</string>
+ <string name="accessibility_autoclick_scroll_panel_title" msgid="7120598166296447036">"पैनल को स्क्रोल करें"</string>
<string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> को प्रतिबंधित बकेट में रखा गया है"</string>
<string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string>
<string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"एक इमेज भेजी गई"</string>
@@ -2492,8 +2486,7 @@
<string name="profile_label_work_3" msgid="4834572253956798917">"ऑफ़िस 3"</string>
<string name="profile_label_test" msgid="9168641926186071947">"टेस्ट"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"कम्यूनिटी"</string>
- <!-- no translation found for profile_label_supervising (5649312778545745371) -->
- <skip />
+ <string name="profile_label_supervising" msgid="5649312778545745371">"निगरानी की जा रही है"</string>
<string name="accessibility_label_managed_profile" msgid="3366526886209832641">"वर्क प्रोफ़ाइल"</string>
<string name="accessibility_label_private_profile" msgid="1436459319135548969">"प्राइवेट स्पेस"</string>
<string name="accessibility_label_clone_profile" msgid="7579118375042398784">"क्लोन"</string>
diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml
index 826b860dfc2b..77c9a6049f0d 100644
--- a/core/res/res/values-hr/strings.xml
+++ b/core/res/res/values-hr/strings.xml
@@ -2274,18 +2274,12 @@
<string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Pomakni se"</string>
<string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Pauziraj"</string>
<string name="accessibility_autoclick_position" msgid="2933660969907663545">"Pozicija"</string>
- <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) -->
- <skip />
+ <string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"Pomakni prema gore"</string>
+ <string name="accessibility_autoclick_scroll_down" msgid="3733401063292018116">"Pomakni prema dolje"</string>
+ <string name="accessibility_autoclick_scroll_left" msgid="8564421367992824198">"Pomakni ulijevo"</string>
+ <string name="accessibility_autoclick_scroll_right" msgid="8932417330753984265">"Pomakni udesno"</string>
+ <string name="accessibility_autoclick_scroll_exit" msgid="3788610039146769696">"Izađi iz načina pomicanja"</string>
+ <string name="accessibility_autoclick_scroll_panel_title" msgid="7120598166296447036">"Ploča za pomicanje"</string>
<string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"Paket <xliff:g id="PACKAGE_NAME">%1$s</xliff:g> premješten je u spremnik OGRANIČENO"</string>
<string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string>
<string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"šalje sliku"</string>
@@ -2493,8 +2487,7 @@
<string name="profile_label_work_3" msgid="4834572253956798917">"Posao 3"</string>
<string name="profile_label_test" msgid="9168641926186071947">"Test"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"Zajedničko"</string>
- <!-- no translation found for profile_label_supervising (5649312778545745371) -->
- <skip />
+ <string name="profile_label_supervising" msgid="5649312778545745371">"Nadzire se"</string>
<string name="accessibility_label_managed_profile" msgid="3366526886209832641">"Radni profil"</string>
<string name="accessibility_label_private_profile" msgid="1436459319135548969">"Privatni prostor"</string>
<string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Klon"</string>
diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml
index 1519326cdb6b..24babe3305e5 100644
--- a/core/res/res/values-hu/strings.xml
+++ b/core/res/res/values-hu/strings.xml
@@ -2273,18 +2273,12 @@
<string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Görgetés"</string>
<string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Szüneteltetés"</string>
<string name="accessibility_autoclick_position" msgid="2933660969907663545">"Pozíció"</string>
- <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) -->
- <skip />
+ <string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"Görgetés felfelé"</string>
+ <string name="accessibility_autoclick_scroll_down" msgid="3733401063292018116">"Görgetés lefelé"</string>
+ <string name="accessibility_autoclick_scroll_left" msgid="8564421367992824198">"Görgetés balra"</string>
+ <string name="accessibility_autoclick_scroll_right" msgid="8932417330753984265">"Görgetés jobbra"</string>
+ <string name="accessibility_autoclick_scroll_exit" msgid="3788610039146769696">"Kilépés a görgetési módból"</string>
+ <string name="accessibility_autoclick_scroll_panel_title" msgid="7120598166296447036">"Görgetési panel"</string>
<string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"A következő csomag a KORLÁTOZOTT csoportba került: <xliff:g id="PACKAGE_NAME">%1$s</xliff:g>"</string>
<string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string>
<string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"képet küldött"</string>
@@ -2492,8 +2486,7 @@
<string name="profile_label_work_3" msgid="4834572253956798917">"3. munkahelyi"</string>
<string name="profile_label_test" msgid="9168641926186071947">"Teszt"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"Közös"</string>
- <!-- no translation found for profile_label_supervising (5649312778545745371) -->
- <skip />
+ <string name="profile_label_supervising" msgid="5649312778545745371">"Aktív felügyelet"</string>
<string name="accessibility_label_managed_profile" msgid="3366526886209832641">"Munkaprofil"</string>
<string name="accessibility_label_private_profile" msgid="1436459319135548969">"Privát terület"</string>
<string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Klón"</string>
diff --git a/core/res/res/values-hy/strings.xml b/core/res/res/values-hy/strings.xml
index 2fd68c11ba7b..7f78454e950c 100644
--- a/core/res/res/values-hy/strings.xml
+++ b/core/res/res/values-hy/strings.xml
@@ -2486,8 +2486,7 @@
<string name="profile_label_work_3" msgid="4834572253956798917">"Աշխատանքային 3"</string>
<string name="profile_label_test" msgid="9168641926186071947">"Փորձնական"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"Ընդհանուր"</string>
- <!-- no translation found for profile_label_supervising (5649312778545745371) -->
- <skip />
+ <string name="profile_label_supervising" msgid="5649312778545745371">"Վերահսկում"</string>
<string name="accessibility_label_managed_profile" msgid="3366526886209832641">"Աշխատանքային պրոֆիլ"</string>
<string name="accessibility_label_private_profile" msgid="1436459319135548969">"Մասնավոր տարածք"</string>
<string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Կլոն"</string>
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index 6e686b4263dd..fbbf2855565d 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -2273,18 +2273,12 @@
<string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Scroll"</string>
<string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Jeda"</string>
<string name="accessibility_autoclick_position" msgid="2933660969907663545">"Posisi"</string>
- <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) -->
- <skip />
+ <string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"Scroll ke Atas"</string>
+ <string name="accessibility_autoclick_scroll_down" msgid="3733401063292018116">"Scroll ke Bawah"</string>
+ <string name="accessibility_autoclick_scroll_left" msgid="8564421367992824198">"Scroll ke Kiri"</string>
+ <string name="accessibility_autoclick_scroll_right" msgid="8932417330753984265">"Scroll ke Kanan"</string>
+ <string name="accessibility_autoclick_scroll_exit" msgid="3788610039146769696">"Keluar dari Mode Scroll"</string>
+ <string name="accessibility_autoclick_scroll_panel_title" msgid="7120598166296447036">"Panel Scroll"</string>
<string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> telah dimasukkan ke dalam bucket DIBATASI"</string>
<string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string>
<string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"mengirim gambar"</string>
@@ -2492,8 +2486,7 @@
<string name="profile_label_work_3" msgid="4834572253956798917">"Kerja 3"</string>
<string name="profile_label_test" msgid="9168641926186071947">"Pengujian"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"Umum"</string>
- <!-- no translation found for profile_label_supervising (5649312778545745371) -->
- <skip />
+ <string name="profile_label_supervising" msgid="5649312778545745371">"Mengawasi"</string>
<string name="accessibility_label_managed_profile" msgid="3366526886209832641">"Profil kerja"</string>
<string name="accessibility_label_private_profile" msgid="1436459319135548969">"Ruang privasi"</string>
<string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Clone"</string>
diff --git a/core/res/res/values-is/strings.xml b/core/res/res/values-is/strings.xml
index 35b102e42933..d148f5d6de08 100644
--- a/core/res/res/values-is/strings.xml
+++ b/core/res/res/values-is/strings.xml
@@ -2273,18 +2273,12 @@
<string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Fletta"</string>
<string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Hlé"</string>
<string name="accessibility_autoclick_position" msgid="2933660969907663545">"Staðsetning"</string>
- <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) -->
- <skip />
+ <string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"Fletta upp"</string>
+ <string name="accessibility_autoclick_scroll_down" msgid="3733401063292018116">"Fletta niður"</string>
+ <string name="accessibility_autoclick_scroll_left" msgid="8564421367992824198">"Fletta til vinstri"</string>
+ <string name="accessibility_autoclick_scroll_right" msgid="8932417330753984265">"Fletta til hægri"</string>
+ <string name="accessibility_autoclick_scroll_exit" msgid="3788610039146769696">"Loka flettistillingu"</string>
+ <string name="accessibility_autoclick_scroll_panel_title" msgid="7120598166296447036">"Flettisvæði"</string>
<string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> var sett í flokkinn TAKMARKAÐ"</string>
<string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string>
<string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"sendi mynd"</string>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index 28aee0fbba0c..7fe1bdc6762d 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -2274,18 +2274,12 @@
<string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Scorri"</string>
<string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Metti in pausa"</string>
<string name="accessibility_autoclick_position" msgid="2933660969907663545">"Posizione"</string>
- <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) -->
- <skip />
+ <string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"Scorri verso l\'alto"</string>
+ <string name="accessibility_autoclick_scroll_down" msgid="3733401063292018116">"Scorri verso il basso"</string>
+ <string name="accessibility_autoclick_scroll_left" msgid="8564421367992824198">"Scorri verso sinistra"</string>
+ <string name="accessibility_autoclick_scroll_right" msgid="8932417330753984265">"Scorri verso destra"</string>
+ <string name="accessibility_autoclick_scroll_exit" msgid="3788610039146769696">"Disattiva la modalità di scorrimento"</string>
+ <string name="accessibility_autoclick_scroll_panel_title" msgid="7120598166296447036">"Riquadro di scorrimento"</string>
<string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> è stato inserito nel bucket RESTRICTED"</string>
<string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string>
<string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"ha inviato un\'immagine"</string>
diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml
index 5eadbd413f2c..c32bcdda3692 100644
--- a/core/res/res/values-iw/strings.xml
+++ b/core/res/res/values-iw/strings.xml
@@ -2274,18 +2274,12 @@
<string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"גלילה"</string>
<string name="accessibility_autoclick_pause" msgid="3272200156172573568">"השהיה"</string>
<string name="accessibility_autoclick_position" msgid="2933660969907663545">"מיקום"</string>
- <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) -->
- <skip />
+ <string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"גלילה למעלה"</string>
+ <string name="accessibility_autoclick_scroll_down" msgid="3733401063292018116">"גלילה למטה"</string>
+ <string name="accessibility_autoclick_scroll_left" msgid="8564421367992824198">"גלילה שמאלה"</string>
+ <string name="accessibility_autoclick_scroll_right" msgid="8932417330753984265">"גלילה ימינה"</string>
+ <string name="accessibility_autoclick_scroll_exit" msgid="3788610039146769696">"יציאה ממצב גלילה"</string>
+ <string name="accessibility_autoclick_scroll_panel_title" msgid="7120598166296447036">"גלילה בחלונית"</string>
<string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> התווספה לקטגוריה \'מוגבל\'"</string>
<string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string>
<string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"נשלחה תמונה"</string>
@@ -2493,8 +2487,7 @@
<string name="profile_label_work_3" msgid="4834572253956798917">"פרופיל עבודה 3"</string>
<string name="profile_label_test" msgid="9168641926186071947">"בדיקה"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"שיתופי"</string>
- <!-- no translation found for profile_label_supervising (5649312778545745371) -->
- <skip />
+ <string name="profile_label_supervising" msgid="5649312778545745371">"פרופיל מפקח"</string>
<string name="accessibility_label_managed_profile" msgid="3366526886209832641">"פרופיל העבודה"</string>
<string name="accessibility_label_private_profile" msgid="1436459319135548969">"המרחב הפרטי"</string>
<string name="accessibility_label_clone_profile" msgid="7579118375042398784">"שכפול"</string>
diff --git a/core/res/res/values-kk/strings.xml b/core/res/res/values-kk/strings.xml
index 24e45df6d1ac..97e550d9e4db 100644
--- a/core/res/res/values-kk/strings.xml
+++ b/core/res/res/values-kk/strings.xml
@@ -2273,18 +2273,12 @@
<string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Айналдыру"</string>
<string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Кідірту"</string>
<string name="accessibility_autoclick_position" msgid="2933660969907663545">"Орналастыру"</string>
- <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) -->
- <skip />
+ <string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"Жоғары айналдыру"</string>
+ <string name="accessibility_autoclick_scroll_down" msgid="3733401063292018116">"Төменге айналдыру"</string>
+ <string name="accessibility_autoclick_scroll_left" msgid="8564421367992824198">"Солға айналдыру"</string>
+ <string name="accessibility_autoclick_scroll_right" msgid="8932417330753984265">"Оңға айналдыру"</string>
+ <string name="accessibility_autoclick_scroll_exit" msgid="3788610039146769696">"Айналдыру режимінен шығу"</string>
+ <string name="accessibility_autoclick_scroll_panel_title" msgid="7120598166296447036">"Айналдыру панелі"</string>
<string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> ШЕКТЕЛГЕН себетке салынды."</string>
<string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string>
<string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"сурет жіберілді"</string>
@@ -2492,8 +2486,7 @@
<string name="profile_label_work_3" msgid="4834572253956798917">"Жұмыс 3"</string>
<string name="profile_label_test" msgid="9168641926186071947">"Сынақ"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"Жалпы"</string>
- <!-- no translation found for profile_label_supervising (5649312778545745371) -->
- <skip />
+ <string name="profile_label_supervising" msgid="5649312778545745371">"Қадағалау"</string>
<string name="accessibility_label_managed_profile" msgid="3366526886209832641">"Жұмыс профилі"</string>
<string name="accessibility_label_private_profile" msgid="1436459319135548969">"Құпия кеңістік"</string>
<string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Клон"</string>
diff --git a/core/res/res/values-kn/strings.xml b/core/res/res/values-kn/strings.xml
index c9a0f211a4e8..316955f0717b 100644
--- a/core/res/res/values-kn/strings.xml
+++ b/core/res/res/values-kn/strings.xml
@@ -2486,8 +2486,7 @@
<string name="profile_label_work_3" msgid="4834572253956798917">"ಕೆಲಸ 3"</string>
<string name="profile_label_test" msgid="9168641926186071947">"ಪರೀಕ್ಷೆ"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"ಸಮುದಾಯ"</string>
- <!-- no translation found for profile_label_supervising (5649312778545745371) -->
- <skip />
+ <string name="profile_label_supervising" msgid="5649312778545745371">"ಮೇಲ್ವಿಚಾರಣೆಯಾಗುತ್ತಿದೆ"</string>
<string name="accessibility_label_managed_profile" msgid="3366526886209832641">"ಉದ್ಯೋಗ ಪ್ರೊಫೈಲ್"</string>
<string name="accessibility_label_private_profile" msgid="1436459319135548969">"ಪ್ರೈವೆಟ್ ಸ್ಪೇಸ್"</string>
<string name="accessibility_label_clone_profile" msgid="7579118375042398784">"ಕ್ಲೋನ್"</string>
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index 852051f0d8e2..bd48cb3e3bf8 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -2273,18 +2273,12 @@
<string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"스크롤"</string>
<string name="accessibility_autoclick_pause" msgid="3272200156172573568">"일시중지"</string>
<string name="accessibility_autoclick_position" msgid="2933660969907663545">"위치"</string>
- <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) -->
- <skip />
+ <string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"위로 스크롤"</string>
+ <string name="accessibility_autoclick_scroll_down" msgid="3733401063292018116">"아래로 스크롤"</string>
+ <string name="accessibility_autoclick_scroll_left" msgid="8564421367992824198">"왼쪽으로 스크롤"</string>
+ <string name="accessibility_autoclick_scroll_right" msgid="8932417330753984265">"오른쪽으로 스크롤"</string>
+ <string name="accessibility_autoclick_scroll_exit" msgid="3788610039146769696">"스크롤 모드 종료"</string>
+ <string name="accessibility_autoclick_scroll_panel_title" msgid="7120598166296447036">"패널 스크롤"</string>
<string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> 항목이 RESTRICTED 버킷으로 이동함"</string>
<string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string>
<string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"이미지 보냄"</string>
@@ -2492,8 +2486,7 @@
<string name="profile_label_work_3" msgid="4834572253956798917">"직장 3"</string>
<string name="profile_label_test" msgid="9168641926186071947">"테스트"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"공동"</string>
- <!-- no translation found for profile_label_supervising (5649312778545745371) -->
- <skip />
+ <string name="profile_label_supervising" msgid="5649312778545745371">"감독 중"</string>
<string name="accessibility_label_managed_profile" msgid="3366526886209832641">"직장 프로필"</string>
<string name="accessibility_label_private_profile" msgid="1436459319135548969">"비공개 스페이스"</string>
<string name="accessibility_label_clone_profile" msgid="7579118375042398784">"클론"</string>
diff --git a/core/res/res/values-ky/strings.xml b/core/res/res/values-ky/strings.xml
index 8115a000a42a..906d4a3ed0fa 100644
--- a/core/res/res/values-ky/strings.xml
+++ b/core/res/res/values-ky/strings.xml
@@ -2273,18 +2273,12 @@
<string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Сыдыруу"</string>
<string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Тындыруу"</string>
<string name="accessibility_autoclick_position" msgid="2933660969907663545">"Орду"</string>
- <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) -->
- <skip />
+ <string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"Жогору сыдыруу"</string>
+ <string name="accessibility_autoclick_scroll_down" msgid="3733401063292018116">"Ылдый сыдыруу"</string>
+ <string name="accessibility_autoclick_scroll_left" msgid="8564421367992824198">"Солго сыдырып кароо"</string>
+ <string name="accessibility_autoclick_scroll_right" msgid="8932417330753984265">"Оңго сыдырып кароо"</string>
+ <string name="accessibility_autoclick_scroll_exit" msgid="3788610039146769696">"Сыдыруу режиминен чыгуу"</string>
+ <string name="accessibility_autoclick_scroll_panel_title" msgid="7120598166296447036">"Сыдыруу панели"</string>
<string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> ЧЕКТЕЛГЕН чакага коюлган"</string>
<string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string>
<string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"сүрөт жөнөттү"</string>
@@ -2492,8 +2486,7 @@
<string name="profile_label_work_3" msgid="4834572253956798917">"Жумуш 3"</string>
<string name="profile_label_test" msgid="9168641926186071947">"Сыноо"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"Жалпы"</string>
- <!-- no translation found for profile_label_supervising (5649312778545745371) -->
- <skip />
+ <string name="profile_label_supervising" msgid="5649312778545745371">"Көзөмөлдөнүүдө"</string>
<string name="accessibility_label_managed_profile" msgid="3366526886209832641">"Жумуш профили"</string>
<string name="accessibility_label_private_profile" msgid="1436459319135548969">"Жеке мейкиндик"</string>
<string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Клон"</string>
diff --git a/core/res/res/values-lo/strings.xml b/core/res/res/values-lo/strings.xml
index b9868d1f82f6..e6222da7af40 100644
--- a/core/res/res/values-lo/strings.xml
+++ b/core/res/res/values-lo/strings.xml
@@ -2486,8 +2486,7 @@
<string name="profile_label_work_3" msgid="4834572253956798917">"ວຽກ 3"</string>
<string name="profile_label_test" msgid="9168641926186071947">"ທົດສອບ"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"ສ່ວນກາງ"</string>
- <!-- no translation found for profile_label_supervising (5649312778545745371) -->
- <skip />
+ <string name="profile_label_supervising" msgid="5649312778545745371">"ການເບິ່ງແຍງກວດກາ"</string>
<string name="accessibility_label_managed_profile" msgid="3366526886209832641">"ໂປຣໄຟລ໌ບ່ອນເຮັດວຽກ"</string>
<string name="accessibility_label_private_profile" msgid="1436459319135548969">"ພື້ນທີ່ສ່ວນບຸກຄົນ"</string>
<string name="accessibility_label_clone_profile" msgid="7579118375042398784">"ໂຄລນ"</string>
diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml
index c71c2b8e612e..3a2836833ea4 100644
--- a/core/res/res/values-lt/strings.xml
+++ b/core/res/res/values-lt/strings.xml
@@ -2488,8 +2488,7 @@
<string name="profile_label_work_3" msgid="4834572253956798917">"Darbas (3)"</string>
<string name="profile_label_test" msgid="9168641926186071947">"Bandymas"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"Bendruomenės"</string>
- <!-- no translation found for profile_label_supervising (5649312778545745371) -->
- <skip />
+ <string name="profile_label_supervising" msgid="5649312778545745371">"Prižiūrima"</string>
<string name="accessibility_label_managed_profile" msgid="3366526886209832641">"Darbo profilis"</string>
<string name="accessibility_label_private_profile" msgid="1436459319135548969">"Privati erdvė"</string>
<string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Klonuoti"</string>
diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml
index 252343724908..b0f6313c214d 100644
--- a/core/res/res/values-lv/strings.xml
+++ b/core/res/res/values-lv/strings.xml
@@ -2274,18 +2274,12 @@
<string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Ritināt"</string>
<string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Pārtraukt"</string>
<string name="accessibility_autoclick_position" msgid="2933660969907663545">"Pozīcija"</string>
- <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) -->
- <skip />
+ <string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"Ritināt augšup"</string>
+ <string name="accessibility_autoclick_scroll_down" msgid="3733401063292018116">"Ritināt lejup"</string>
+ <string name="accessibility_autoclick_scroll_left" msgid="8564421367992824198">"Ritināt pa kreisi"</string>
+ <string name="accessibility_autoclick_scroll_right" msgid="8932417330753984265">"Ritināt pa labi"</string>
+ <string name="accessibility_autoclick_scroll_exit" msgid="3788610039146769696">"Izslēgt ritināšanas režīmu"</string>
+ <string name="accessibility_autoclick_scroll_panel_title" msgid="7120598166296447036">"Ritināšanas panelis"</string>
<string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"Pakotne “<xliff:g id="PACKAGE_NAME">%1$s</xliff:g>” ir ievietota ierobežotā kopā."</string>
<string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string>
<string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"nosūtīts attēls"</string>
@@ -2493,8 +2487,7 @@
<string name="profile_label_work_3" msgid="4834572253956798917">"Darbam (3.)"</string>
<string name="profile_label_test" msgid="9168641926186071947">"Testēšanai"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"Kopīgs"</string>
- <!-- no translation found for profile_label_supervising (5649312778545745371) -->
- <skip />
+ <string name="profile_label_supervising" msgid="5649312778545745371">"Uzraudzība"</string>
<string name="accessibility_label_managed_profile" msgid="3366526886209832641">"Darba profils"</string>
<string name="accessibility_label_private_profile" msgid="1436459319135548969">"Privātā telpa"</string>
<string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Klons"</string>
diff --git a/core/res/res/values-mk/strings.xml b/core/res/res/values-mk/strings.xml
index c6cb1faa850b..8b389a0e5a1b 100644
--- a/core/res/res/values-mk/strings.xml
+++ b/core/res/res/values-mk/strings.xml
@@ -2273,18 +2273,12 @@
<string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Лизгање"</string>
<string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Паузирај"</string>
<string name="accessibility_autoclick_position" msgid="2933660969907663545">"Позиционирај"</string>
- <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) -->
- <skip />
+ <string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"Лизгај нагоре"</string>
+ <string name="accessibility_autoclick_scroll_down" msgid="3733401063292018116">"Лизгај надолу"</string>
+ <string name="accessibility_autoclick_scroll_left" msgid="8564421367992824198">"Лизгај налево"</string>
+ <string name="accessibility_autoclick_scroll_right" msgid="8932417330753984265">"Лизгај надесно"</string>
+ <string name="accessibility_autoclick_scroll_exit" msgid="3788610039146769696">"Излези од „Режим на лизгање“"</string>
+ <string name="accessibility_autoclick_scroll_panel_title" msgid="7120598166296447036">"Табла за лизгање"</string>
<string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> е ставен во корпата ОГРАНИЧЕНИ"</string>
<string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string>
<string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"испрати слика"</string>
@@ -2492,8 +2486,7 @@
<string name="profile_label_work_3" msgid="4834572253956798917">"Работен профил 3"</string>
<string name="profile_label_test" msgid="9168641926186071947">"Профил за тестирање"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"Профил на заедницата"</string>
- <!-- no translation found for profile_label_supervising (5649312778545745371) -->
- <skip />
+ <string name="profile_label_supervising" msgid="5649312778545745371">"Вршење надзор"</string>
<string name="accessibility_label_managed_profile" msgid="3366526886209832641">"Работен профил"</string>
<string name="accessibility_label_private_profile" msgid="1436459319135548969">"Приватен простор"</string>
<string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Клониран профил"</string>
diff --git a/core/res/res/values-ml/strings.xml b/core/res/res/values-ml/strings.xml
index fca58a113c7b..825da1894522 100644
--- a/core/res/res/values-ml/strings.xml
+++ b/core/res/res/values-ml/strings.xml
@@ -2273,18 +2273,12 @@
<string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"സ്‌ക്രോൾ ചെയ്യുക"</string>
<string name="accessibility_autoclick_pause" msgid="3272200156172573568">"താൽക്കാലികമായി നിർത്തുക"</string>
<string name="accessibility_autoclick_position" msgid="2933660969907663545">"സ്ഥാനം"</string>
- <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) -->
- <skip />
+ <string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"മുകളിലേക്ക് സ്‌ക്രോൾ ചെയ്യുക"</string>
+ <string name="accessibility_autoclick_scroll_down" msgid="3733401063292018116">"താഴേക്ക് സ്‌ക്രോൾ ചെയ്യുക"</string>
+ <string name="accessibility_autoclick_scroll_left" msgid="8564421367992824198">"ഇടത്തേക്ക് സ്‌ക്രോൾ ചെയ്യുക"</string>
+ <string name="accessibility_autoclick_scroll_right" msgid="8932417330753984265">"വലത്തേക്ക് സ്‌ക്രോൾ ചെയ്യുക"</string>
+ <string name="accessibility_autoclick_scroll_exit" msgid="3788610039146769696">"സ്ക്രോൾ മോഡിൽ നിന്ന് പുറത്തുകടക്കുക"</string>
+ <string name="accessibility_autoclick_scroll_panel_title" msgid="7120598166296447036">"സ്‌ക്രോൾ പാനൽ"</string>
<string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> നിയന്ത്രിത ബക്കറ്റിലേക്ക് നീക്കി"</string>
<string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string>
<string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"ചിത്രം അയച്ചു"</string>
@@ -2492,8 +2486,7 @@
<string name="profile_label_work_3" msgid="4834572253956798917">"ഔദ്യോഗികം 3"</string>
<string name="profile_label_test" msgid="9168641926186071947">"ടെസ്‌റ്റ്"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"കമ്മ്യൂണൽ"</string>
- <!-- no translation found for profile_label_supervising (5649312778545745371) -->
- <skip />
+ <string name="profile_label_supervising" msgid="5649312778545745371">"മേൽനോട്ടമുണ്ട്"</string>
<string name="accessibility_label_managed_profile" msgid="3366526886209832641">"ഔദ്യോഗിക പ്രൊഫൈൽ"</string>
<string name="accessibility_label_private_profile" msgid="1436459319135548969">"സ്വകാര്യ സ്പേസ്"</string>
<string name="accessibility_label_clone_profile" msgid="7579118375042398784">"ക്ലോൺ ചെയ്യുക"</string>
diff --git a/core/res/res/values-mn/strings.xml b/core/res/res/values-mn/strings.xml
index 740c78386c3a..4222ce1df348 100644
--- a/core/res/res/values-mn/strings.xml
+++ b/core/res/res/values-mn/strings.xml
@@ -2273,18 +2273,12 @@
<string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Гүйлгэх"</string>
<string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Түр зогсоох"</string>
<string name="accessibility_autoclick_position" msgid="2933660969907663545">"Байрлал"</string>
- <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) -->
- <skip />
+ <string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"Дээш гүйлгэх"</string>
+ <string name="accessibility_autoclick_scroll_down" msgid="3733401063292018116">"Доош гүйлгэх"</string>
+ <string name="accessibility_autoclick_scroll_left" msgid="8564421367992824198">"Зүүн тийш гүйлгэх"</string>
+ <string name="accessibility_autoclick_scroll_right" msgid="8932417330753984265">"Баруун тийш гүйлгэх"</string>
+ <string name="accessibility_autoclick_scroll_exit" msgid="3788610039146769696">"Гүйлгэх горимоос гарах"</string>
+ <string name="accessibility_autoclick_scroll_panel_title" msgid="7120598166296447036">"Гүйлгэх үйлдлийн түр зуурын самбар"</string>
<string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g>-г ХЯЗГААРЛАСАН сагс руу орууллаа"</string>
<string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string>
<string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"зураг илгээсэн"</string>
@@ -2492,8 +2486,7 @@
<string name="profile_label_work_3" msgid="4834572253956798917">"Ажил 3"</string>
<string name="profile_label_test" msgid="9168641926186071947">"Туршилт"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"Нийтийн"</string>
- <!-- no translation found for profile_label_supervising (5649312778545745371) -->
- <skip />
+ <string name="profile_label_supervising" msgid="5649312778545745371">"Хянаж байна"</string>
<string name="accessibility_label_managed_profile" msgid="3366526886209832641">"Ажлын профайл"</string>
<string name="accessibility_label_private_profile" msgid="1436459319135548969">"Хаалттай орон зай"</string>
<string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Клон"</string>
diff --git a/core/res/res/values-mr/strings.xml b/core/res/res/values-mr/strings.xml
index 6f741d7b77b9..73fc1b657c97 100644
--- a/core/res/res/values-mr/strings.xml
+++ b/core/res/res/values-mr/strings.xml
@@ -2273,18 +2273,12 @@
<string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"स्क्रोल करा"</string>
<string name="accessibility_autoclick_pause" msgid="3272200156172573568">"थांबवा"</string>
<string name="accessibility_autoclick_position" msgid="2933660969907663545">"स्थिती"</string>
- <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) -->
- <skip />
+ <string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"वर स्क्रोल करा"</string>
+ <string name="accessibility_autoclick_scroll_down" msgid="3733401063292018116">"खाली स्क्रोल करा"</string>
+ <string name="accessibility_autoclick_scroll_left" msgid="8564421367992824198">"डावीकडे स्क्रोल करा"</string>
+ <string name="accessibility_autoclick_scroll_right" msgid="8932417330753984265">"उजवीकडे स्क्रोल करा"</string>
+ <string name="accessibility_autoclick_scroll_exit" msgid="3788610039146769696">"स्क्रोल करा मोड मधून बाहेर पडा"</string>
+ <string name="accessibility_autoclick_scroll_panel_title" msgid="7120598166296447036">"स्क्रोल करा पॅनल"</string>
<string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> हे प्रतिबंधित बादलीमध्ये ठेवण्यात आले आहे"</string>
<string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string>
<string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"इमेज पाठवली आहे"</string>
@@ -2492,8 +2486,7 @@
<string name="profile_label_work_3" msgid="4834572253956798917">"ऑफिस ३"</string>
<string name="profile_label_test" msgid="9168641926186071947">"चाचणी"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"सामुदायिक"</string>
- <!-- no translation found for profile_label_supervising (5649312778545745371) -->
- <skip />
+ <string name="profile_label_supervising" msgid="5649312778545745371">"पर्यवेक्षण करत आहे"</string>
<string name="accessibility_label_managed_profile" msgid="3366526886209832641">"कार्य प्रोफाइल"</string>
<string name="accessibility_label_private_profile" msgid="1436459319135548969">"खाजगी स्पेस"</string>
<string name="accessibility_label_clone_profile" msgid="7579118375042398784">"क्लोन"</string>
diff --git a/core/res/res/values-my/strings.xml b/core/res/res/values-my/strings.xml
index 52421557f296..f3160d85e97b 100644
--- a/core/res/res/values-my/strings.xml
+++ b/core/res/res/values-my/strings.xml
@@ -2486,8 +2486,7 @@
<string name="profile_label_work_3" msgid="4834572253956798917">"အလုပ် ၃"</string>
<string name="profile_label_test" msgid="9168641926186071947">"စမ်းသပ်မှု"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"အများသုံး"</string>
- <!-- no translation found for profile_label_supervising (5649312778545745371) -->
- <skip />
+ <string name="profile_label_supervising" msgid="5649312778545745371">"ကြီးကြပ်နေသည်"</string>
<string name="accessibility_label_managed_profile" msgid="3366526886209832641">"အလုပ်ပရိုဖိုင်"</string>
<string name="accessibility_label_private_profile" msgid="1436459319135548969">"သီးသန့်နေရာ"</string>
<string name="accessibility_label_clone_profile" msgid="7579118375042398784">"ပုံတူပွားရန်"</string>
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index 3dca9b927c10..a4026db705ce 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -2273,18 +2273,12 @@
<string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Rull"</string>
<string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Sett på pause"</string>
<string name="accessibility_autoclick_position" msgid="2933660969907663545">"Plassér"</string>
- <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) -->
- <skip />
+ <string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"Rull opp"</string>
+ <string name="accessibility_autoclick_scroll_down" msgid="3733401063292018116">"Rull ned"</string>
+ <string name="accessibility_autoclick_scroll_left" msgid="8564421367992824198">"Rull til venstre"</string>
+ <string name="accessibility_autoclick_scroll_right" msgid="8932417330753984265">"Rull til høyre"</string>
+ <string name="accessibility_autoclick_scroll_exit" msgid="3788610039146769696">"Avslutt rullemodus"</string>
+ <string name="accessibility_autoclick_scroll_panel_title" msgid="7120598166296447036">"Rullepanel"</string>
<string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> er blitt plassert i TILGANGSBEGRENSET-toppmappen"</string>
<string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string>
<string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"har sendt et bilde"</string>
@@ -2492,8 +2486,7 @@
<string name="profile_label_work_3" msgid="4834572253956798917">"Jobb 3"</string>
<string name="profile_label_test" msgid="9168641926186071947">"Test"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"Felles"</string>
- <!-- no translation found for profile_label_supervising (5649312778545745371) -->
- <skip />
+ <string name="profile_label_supervising" msgid="5649312778545745371">"Med tilsyn"</string>
<string name="accessibility_label_managed_profile" msgid="3366526886209832641">"Jobbprofil"</string>
<string name="accessibility_label_private_profile" msgid="1436459319135548969">"Privat område"</string>
<string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Klon"</string>
diff --git a/core/res/res/values-ne/strings.xml b/core/res/res/values-ne/strings.xml
index 13c98551b8ca..b3ce1c8eb9b6 100644
--- a/core/res/res/values-ne/strings.xml
+++ b/core/res/res/values-ne/strings.xml
@@ -2273,18 +2273,12 @@
<string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"स्क्रोल गर्नुहोस्"</string>
<string name="accessibility_autoclick_pause" msgid="3272200156172573568">"पज गर्नुहोस्"</string>
<string name="accessibility_autoclick_position" msgid="2933660969907663545">"स्थिति"</string>
- <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) -->
- <skip />
+ <string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"माथितिर स्क्रोल गर्नुहोस्"</string>
+ <string name="accessibility_autoclick_scroll_down" msgid="3733401063292018116">"तलतिर स्क्रोल गर्नुहोस्"</string>
+ <string name="accessibility_autoclick_scroll_left" msgid="8564421367992824198">"बायाँतिर स्क्रोल गर्नुहोस्"</string>
+ <string name="accessibility_autoclick_scroll_right" msgid="8932417330753984265">"दायाँतिर स्क्रोल गर्नुहोस्"</string>
+ <string name="accessibility_autoclick_scroll_exit" msgid="3788610039146769696">"स्क्रोल गर्नुहोस् मोडबाट बाहिरिनुहोस्"</string>
+ <string name="accessibility_autoclick_scroll_panel_title" msgid="7120598166296447036">"प्यानल स्क्रोल गर्नुहोस्"</string>
<string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> लाई प्रतिबन्धित बाल्टीमा राखियो"</string>
<string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string>
<string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"फोटो पठाइयो"</string>
@@ -2492,8 +2486,7 @@
<string name="profile_label_work_3" msgid="4834572253956798917">"कार्य प्रोफाइल ३"</string>
<string name="profile_label_test" msgid="9168641926186071947">"परीक्षण"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"सामुदायिक"</string>
- <!-- no translation found for profile_label_supervising (5649312778545745371) -->
- <skip />
+ <string name="profile_label_supervising" msgid="5649312778545745371">"सुपरिवेक्षण गरिँदै छ"</string>
<string name="accessibility_label_managed_profile" msgid="3366526886209832641">"कार्य प्रोफाइल"</string>
<string name="accessibility_label_private_profile" msgid="1436459319135548969">"निजी स्पेस"</string>
<string name="accessibility_label_clone_profile" msgid="7579118375042398784">"क्लोन"</string>
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index f5e3831a6032..ebdd4c65db20 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -2273,18 +2273,12 @@
<string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Scrollen"</string>
<string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Pauzeren"</string>
<string name="accessibility_autoclick_position" msgid="2933660969907663545">"Positie"</string>
- <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) -->
- <skip />
+ <string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"Omhoog scrollen"</string>
+ <string name="accessibility_autoclick_scroll_down" msgid="3733401063292018116">"Omlaag scrollen"</string>
+ <string name="accessibility_autoclick_scroll_left" msgid="8564421367992824198">"Naar links scrollen"</string>
+ <string name="accessibility_autoclick_scroll_right" msgid="8932417330753984265">"Naar rechts scrollen"</string>
+ <string name="accessibility_autoclick_scroll_exit" msgid="3788610039146769696">"Scrollmodus sluiten"</string>
+ <string name="accessibility_autoclick_scroll_panel_title" msgid="7120598166296447036">"Deelvenster scrollen"</string>
<string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> is in de bucket RESTRICTED geplaatst"</string>
<string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string>
<string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"heeft een afbeelding gestuurd"</string>
@@ -2492,8 +2486,7 @@
<string name="profile_label_work_3" msgid="4834572253956798917">"Werk 3"</string>
<string name="profile_label_test" msgid="9168641926186071947">"Test"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"Gemeenschappelijk"</string>
- <!-- no translation found for profile_label_supervising (5649312778545745371) -->
- <skip />
+ <string name="profile_label_supervising" msgid="5649312778545745371">"Toezicht"</string>
<string name="accessibility_label_managed_profile" msgid="3366526886209832641">"Werkprofiel"</string>
<string name="accessibility_label_private_profile" msgid="1436459319135548969">"Privégedeelte"</string>
<string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Kloon"</string>
diff --git a/core/res/res/values-or/strings.xml b/core/res/res/values-or/strings.xml
index eee243f3f47a..2b91cff7f216 100644
--- a/core/res/res/values-or/strings.xml
+++ b/core/res/res/values-or/strings.xml
@@ -2273,18 +2273,12 @@
<string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"ସ୍କ୍ରୋଲ କରନ୍ତୁ"</string>
<string name="accessibility_autoclick_pause" msgid="3272200156172573568">"ବିରତ କରନ୍ତୁ"</string>
<string name="accessibility_autoclick_position" msgid="2933660969907663545">"ସ୍ଥିତି"</string>
- <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) -->
- <skip />
+ <string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"ଉପରକୁ ସ୍କ୍ରୋଲ କରନ୍ତୁ"</string>
+ <string name="accessibility_autoclick_scroll_down" msgid="3733401063292018116">"ତଳକୁ ସ୍କ୍ରୋଲ କରନ୍ତୁ"</string>
+ <string name="accessibility_autoclick_scroll_left" msgid="8564421367992824198">"ବାମକୁ ସ୍କ୍ରୋଲ କରନ୍ତୁ"</string>
+ <string name="accessibility_autoclick_scroll_right" msgid="8932417330753984265">"ଡାହାଣକୁ ସ୍କ୍ରୋଲ କରନ୍ତୁ"</string>
+ <string name="accessibility_autoclick_scroll_exit" msgid="3788610039146769696">"ସ୍କ୍ରୋଲ ମୋଡରୁ ବାହାରି ଯାଆନ୍ତୁ"</string>
+ <string name="accessibility_autoclick_scroll_panel_title" msgid="7120598166296447036">"ସ୍କ୍ରୋଲ ପେନେଲ"</string>
<string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g>କୁ ପ୍ରତିବନ୍ଧିତ ବକେଟରେ ରଖାଯାଇଛି"</string>
<string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string>
<string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"ଏକ ଛବି ପଠାଯାଇଛି"</string>
@@ -2492,8 +2486,7 @@
<string name="profile_label_work_3" msgid="4834572253956798917">"ୱାର୍କ 3"</string>
<string name="profile_label_test" msgid="9168641926186071947">"ଟେଷ୍ଟ"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"କମ୍ୟୁନାଲ"</string>
- <!-- no translation found for profile_label_supervising (5649312778545745371) -->
- <skip />
+ <string name="profile_label_supervising" msgid="5649312778545745371">"ନିରୀକ୍ଷଣ କରାଯାଉଛି"</string>
<string name="accessibility_label_managed_profile" msgid="3366526886209832641">"ୱାର୍କ ପ୍ରୋଫାଇଲ"</string>
<string name="accessibility_label_private_profile" msgid="1436459319135548969">"ପ୍ରାଇଭେଟ ସ୍ପେସ"</string>
<string name="accessibility_label_clone_profile" msgid="7579118375042398784">"କ୍ଲୋନ"</string>
diff --git a/core/res/res/values-pa/strings.xml b/core/res/res/values-pa/strings.xml
index 6632fb0f1354..5a9f227f6772 100644
--- a/core/res/res/values-pa/strings.xml
+++ b/core/res/res/values-pa/strings.xml
@@ -2273,18 +2273,12 @@
<string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"ਸਕ੍ਰੋਲ ਕਰੋ"</string>
<string name="accessibility_autoclick_pause" msgid="3272200156172573568">"ਰੋਕੋ"</string>
<string name="accessibility_autoclick_position" msgid="2933660969907663545">"ਸਥਿਤੀ"</string>
- <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) -->
- <skip />
+ <string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"ਉੱਪਰ ਵੱਲ ਸਕ੍ਰੋਲ ਕਰੋ"</string>
+ <string name="accessibility_autoclick_scroll_down" msgid="3733401063292018116">"ਹੇਠਾਂ ਵੱਲ ਸਕ੍ਰੋਲ ਕਰੋ"</string>
+ <string name="accessibility_autoclick_scroll_left" msgid="8564421367992824198">"ਖੱਬੇ ਪਾਸੇ ਵੱਲ ਸਕ੍ਰੋਲ ਕਰੋ"</string>
+ <string name="accessibility_autoclick_scroll_right" msgid="8932417330753984265">"ਸੱਜੇ ਪਾਸੇ ਵੱਲ ਸਕ੍ਰੋਲ ਕਰੋ"</string>
+ <string name="accessibility_autoclick_scroll_exit" msgid="3788610039146769696">"ਸਕ੍ਰੋਲ ਮੋਡ ਤੋਂ ਬਾਹਰ ਜਾਓ"</string>
+ <string name="accessibility_autoclick_scroll_panel_title" msgid="7120598166296447036">"ਸਕ੍ਰੋਲ ਪੈਨਲ"</string>
<string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> ਨੂੰ ਪ੍ਰਤਿਬੰਧਿਤ ਖਾਨੇ ਵਿੱਚ ਪਾਇਆ ਗਿਆ ਹੈ"</string>
<string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string>
<string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"ਚਿੱਤਰ ਭੇਜਿਆ ਗਿਆ"</string>
@@ -2492,8 +2486,7 @@
<string name="profile_label_work_3" msgid="4834572253956798917">"ਕੰਮ ਸੰਬੰਧੀ 3"</string>
<string name="profile_label_test" msgid="9168641926186071947">"ਜਾਂਚ"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"ਭਾਈਚਾਰਕ"</string>
- <!-- no translation found for profile_label_supervising (5649312778545745371) -->
- <skip />
+ <string name="profile_label_supervising" msgid="5649312778545745371">"ਨਿਗਰਾਨੀ ਕੀਤੀ ਜਾ ਰਹੀ ਹੈ"</string>
<string name="accessibility_label_managed_profile" msgid="3366526886209832641">"ਕਾਰਜ ਪ੍ਰੋਫਾਈਲ"</string>
<string name="accessibility_label_private_profile" msgid="1436459319135548969">"ਪ੍ਰਾਈਵੇਟ ਸਪੇਸ"</string>
<string name="accessibility_label_clone_profile" msgid="7579118375042398784">"ਕਲੋਨ"</string>
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index 2cdac419ab0d..5f02c1fcc532 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -2275,18 +2275,12 @@
<string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Przewijanie"</string>
<string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Wstrzymaj"</string>
<string name="accessibility_autoclick_position" msgid="2933660969907663545">"Pozycja"</string>
- <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) -->
- <skip />
+ <string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"Przewiń w górę"</string>
+ <string name="accessibility_autoclick_scroll_down" msgid="3733401063292018116">"Przewiń w dół"</string>
+ <string name="accessibility_autoclick_scroll_left" msgid="8564421367992824198">"Przewiń w lewo"</string>
+ <string name="accessibility_autoclick_scroll_right" msgid="8932417330753984265">"Przewiń w prawo"</string>
+ <string name="accessibility_autoclick_scroll_exit" msgid="3788610039146769696">"Wyłącz tryb przewijania"</string>
+ <string name="accessibility_autoclick_scroll_panel_title" msgid="7120598166296447036">"Przewiń panel"</string>
<string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"Umieszczono pakiet <xliff:g id="PACKAGE_NAME">%1$s</xliff:g> w zasobniku danych RESTRICTED"</string>
<string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string>
<string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"wysłano obraz"</string>
@@ -2494,8 +2488,7 @@
<string name="profile_label_work_3" msgid="4834572253956798917">"Służbowy 3"</string>
<string name="profile_label_test" msgid="9168641926186071947">"Testowy"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"Wspólny"</string>
- <!-- no translation found for profile_label_supervising (5649312778545745371) -->
- <skip />
+ <string name="profile_label_supervising" msgid="5649312778545745371">"Nadzorujesz"</string>
<string name="accessibility_label_managed_profile" msgid="3366526886209832641">"Profil służbowy"</string>
<string name="accessibility_label_private_profile" msgid="1436459319135548969">"Przestrzeń prywatna"</string>
<string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Klon"</string>
diff --git a/core/res/res/values-pt-rBR/strings.xml b/core/res/res/values-pt-rBR/strings.xml
index 52dc72c2c280..32d883ab7c29 100644
--- a/core/res/res/values-pt-rBR/strings.xml
+++ b/core/res/res/values-pt-rBR/strings.xml
@@ -2274,18 +2274,12 @@
<string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Rolar"</string>
<string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Pausar"</string>
<string name="accessibility_autoclick_position" msgid="2933660969907663545">"Posição"</string>
- <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) -->
- <skip />
+ <string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"Rolar para cima"</string>
+ <string name="accessibility_autoclick_scroll_down" msgid="3733401063292018116">"Rolar para baixo"</string>
+ <string name="accessibility_autoclick_scroll_left" msgid="8564421367992824198">"Rolar para a esquerda"</string>
+ <string name="accessibility_autoclick_scroll_right" msgid="8932417330753984265">"Rolar para a direita"</string>
+ <string name="accessibility_autoclick_scroll_exit" msgid="3788610039146769696">"Sair do modo de rolagem"</string>
+ <string name="accessibility_autoclick_scroll_panel_title" msgid="7120598166296447036">"Painel de rolagem"</string>
<string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> foi colocado no intervalo \"RESTRITO\""</string>
<string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string>
<string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"enviou uma imagem"</string>
@@ -2493,8 +2487,7 @@
<string name="profile_label_work_3" msgid="4834572253956798917">"Trabalho 3"</string>
<string name="profile_label_test" msgid="9168641926186071947">"Teste"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"Público"</string>
- <!-- no translation found for profile_label_supervising (5649312778545745371) -->
- <skip />
+ <string name="profile_label_supervising" msgid="5649312778545745371">"Supervisionando"</string>
<string name="accessibility_label_managed_profile" msgid="3366526886209832641">"Perfil de trabalho"</string>
<string name="accessibility_label_private_profile" msgid="1436459319135548969">"Espaço privado"</string>
<string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Clone"</string>
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index a8400e2837a4..57507df2370a 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -2487,8 +2487,7 @@
<string name="profile_label_work_3" msgid="4834572253956798917">"Trabalho 3"</string>
<string name="profile_label_test" msgid="9168641926186071947">"Teste"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"Comum"</string>
- <!-- no translation found for profile_label_supervising (5649312778545745371) -->
- <skip />
+ <string name="profile_label_supervising" msgid="5649312778545745371">"A supervisionar"</string>
<string name="accessibility_label_managed_profile" msgid="3366526886209832641">"Perfil de trabalho"</string>
<string name="accessibility_label_private_profile" msgid="1436459319135548969">"Espaço privado"</string>
<string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Clone"</string>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index 52dc72c2c280..32d883ab7c29 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -2274,18 +2274,12 @@
<string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Rolar"</string>
<string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Pausar"</string>
<string name="accessibility_autoclick_position" msgid="2933660969907663545">"Posição"</string>
- <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) -->
- <skip />
+ <string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"Rolar para cima"</string>
+ <string name="accessibility_autoclick_scroll_down" msgid="3733401063292018116">"Rolar para baixo"</string>
+ <string name="accessibility_autoclick_scroll_left" msgid="8564421367992824198">"Rolar para a esquerda"</string>
+ <string name="accessibility_autoclick_scroll_right" msgid="8932417330753984265">"Rolar para a direita"</string>
+ <string name="accessibility_autoclick_scroll_exit" msgid="3788610039146769696">"Sair do modo de rolagem"</string>
+ <string name="accessibility_autoclick_scroll_panel_title" msgid="7120598166296447036">"Painel de rolagem"</string>
<string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> foi colocado no intervalo \"RESTRITO\""</string>
<string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string>
<string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"enviou uma imagem"</string>
@@ -2493,8 +2487,7 @@
<string name="profile_label_work_3" msgid="4834572253956798917">"Trabalho 3"</string>
<string name="profile_label_test" msgid="9168641926186071947">"Teste"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"Público"</string>
- <!-- no translation found for profile_label_supervising (5649312778545745371) -->
- <skip />
+ <string name="profile_label_supervising" msgid="5649312778545745371">"Supervisionando"</string>
<string name="accessibility_label_managed_profile" msgid="3366526886209832641">"Perfil de trabalho"</string>
<string name="accessibility_label_private_profile" msgid="1436459319135548969">"Espaço privado"</string>
<string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Clone"</string>
diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml
index 8ad6d02e6113..2e6f58dca1df 100644
--- a/core/res/res/values-ro/strings.xml
+++ b/core/res/res/values-ro/strings.xml
@@ -2274,18 +2274,12 @@
<string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Derulează"</string>
<string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Întrerupe"</string>
<string name="accessibility_autoclick_position" msgid="2933660969907663545">"Poziție"</string>
- <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) -->
- <skip />
+ <string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"Derulează în sus"</string>
+ <string name="accessibility_autoclick_scroll_down" msgid="3733401063292018116">"Derulează în jos"</string>
+ <string name="accessibility_autoclick_scroll_left" msgid="8564421367992824198">"Derulează la stânga"</string>
+ <string name="accessibility_autoclick_scroll_right" msgid="8932417330753984265">"Derulează la dreapta"</string>
+ <string name="accessibility_autoclick_scroll_exit" msgid="3788610039146769696">"Ieși din modul de derulare"</string>
+ <string name="accessibility_autoclick_scroll_panel_title" msgid="7120598166296447036">"Panou de derulare"</string>
<string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> a fost adăugat la grupul RESTRICȚIONATE"</string>
<string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string>
<string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"a trimis o imagine"</string>
@@ -2493,8 +2487,7 @@
<string name="profile_label_work_3" msgid="4834572253956798917">"Serviciu 3"</string>
<string name="profile_label_test" msgid="9168641926186071947">"Test"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"Comun"</string>
- <!-- no translation found for profile_label_supervising (5649312778545745371) -->
- <skip />
+ <string name="profile_label_supervising" msgid="5649312778545745371">"Supraveghere activă"</string>
<string name="accessibility_label_managed_profile" msgid="3366526886209832641">"Profil de serviciu"</string>
<string name="accessibility_label_private_profile" msgid="1436459319135548969">"Spațiu privat"</string>
<string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Clonă"</string>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index 0ee25426bbf6..2beebd7569fc 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -2275,18 +2275,12 @@
<string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Прокрутить"</string>
<string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Приостановить"</string>
<string name="accessibility_autoclick_position" msgid="2933660969907663545">"Положение"</string>
- <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) -->
- <skip />
+ <string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"Прокрутка вверх"</string>
+ <string name="accessibility_autoclick_scroll_down" msgid="3733401063292018116">"Прокрутка вниз"</string>
+ <string name="accessibility_autoclick_scroll_left" msgid="8564421367992824198">"Прокрутка влево"</string>
+ <string name="accessibility_autoclick_scroll_right" msgid="8932417330753984265">"Прокрутка вправо"</string>
+ <string name="accessibility_autoclick_scroll_exit" msgid="3788610039146769696">"Отключить режим прокрутки"</string>
+ <string name="accessibility_autoclick_scroll_panel_title" msgid="7120598166296447036">"Панель прокрутки"</string>
<string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"Приложение \"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g>\" помещено в категорию с ограниченным доступом."</string>
<string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string>
<string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"Отправлено изображение"</string>
@@ -2494,8 +2488,7 @@
<string name="profile_label_work_3" msgid="4834572253956798917">"Рабочий 3"</string>
<string name="profile_label_test" msgid="9168641926186071947">"Тестовый"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"Совместный"</string>
- <!-- no translation found for profile_label_supervising (5649312778545745371) -->
- <skip />
+ <string name="profile_label_supervising" msgid="5649312778545745371">"Управляющий профиль"</string>
<string name="accessibility_label_managed_profile" msgid="3366526886209832641">"Рабочий профиль"</string>
<string name="accessibility_label_private_profile" msgid="1436459319135548969">"Частное пространство"</string>
<string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Клонированный"</string>
diff --git a/core/res/res/values-si/strings.xml b/core/res/res/values-si/strings.xml
index 0d3cd7e4979a..a59187cf62c6 100644
--- a/core/res/res/values-si/strings.xml
+++ b/core/res/res/values-si/strings.xml
@@ -2273,18 +2273,12 @@
<string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"අනුචලනය කරන්න"</string>
<string name="accessibility_autoclick_pause" msgid="3272200156172573568">"විරාම කරන්න"</string>
<string name="accessibility_autoclick_position" msgid="2933660969907663545">"ස්ථානය"</string>
- <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) -->
- <skip />
+ <string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"ඉහළට අනුචලනය කරන්න"</string>
+ <string name="accessibility_autoclick_scroll_down" msgid="3733401063292018116">"පහළට අනුචලනය කරන්න"</string>
+ <string name="accessibility_autoclick_scroll_left" msgid="8564421367992824198">"වමට අනුචලනය කරන්න"</string>
+ <string name="accessibility_autoclick_scroll_right" msgid="8932417330753984265">"දකුණට අනුචලනය කරන්න"</string>
+ <string name="accessibility_autoclick_scroll_exit" msgid="3788610039146769696">"අනුචලන ප්‍රකාරයෙන් පිටවන්න"</string>
+ <string name="accessibility_autoclick_scroll_panel_title" msgid="7120598166296447036">"අනුචලන පැනලය"</string>
<string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> අවහිර කළ බාල්දියට දමා ඇත"</string>
<string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string>
<string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"රූපයක් එව්වා"</string>
@@ -2492,8 +2486,7 @@
<string name="profile_label_work_3" msgid="4834572253956798917">"කාර්යාලය 3"</string>
<string name="profile_label_test" msgid="9168641926186071947">"පරීක්ෂණය"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"වාර්ගික"</string>
- <!-- no translation found for profile_label_supervising (5649312778545745371) -->
- <skip />
+ <string name="profile_label_supervising" msgid="5649312778545745371">"අධීක්ෂණය කිරීම"</string>
<string name="accessibility_label_managed_profile" msgid="3366526886209832641">"කාර්යාල පැතිකඩ"</string>
<string name="accessibility_label_private_profile" msgid="1436459319135548969">"රහසිගත අවකාශය"</string>
<string name="accessibility_label_clone_profile" msgid="7579118375042398784">"ක්ලෝන කරන්න"</string>
diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml
index e3855d1a6790..999000e6e490 100644
--- a/core/res/res/values-sk/strings.xml
+++ b/core/res/res/values-sk/strings.xml
@@ -2275,18 +2275,12 @@
<string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Posúvať"</string>
<string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Pozastaviť"</string>
<string name="accessibility_autoclick_position" msgid="2933660969907663545">"Pozícia"</string>
- <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) -->
- <skip />
+ <string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"Posunúť nahor"</string>
+ <string name="accessibility_autoclick_scroll_down" msgid="3733401063292018116">"Posunúť nadol"</string>
+ <string name="accessibility_autoclick_scroll_left" msgid="8564421367992824198">"Posunúť doľava"</string>
+ <string name="accessibility_autoclick_scroll_right" msgid="8932417330753984265">"Posunúť doprava"</string>
+ <string name="accessibility_autoclick_scroll_exit" msgid="3788610039146769696">"Ukončiť režim posúvania"</string>
+ <string name="accessibility_autoclick_scroll_panel_title" msgid="7120598166296447036">"Posúvateľný panel"</string>
<string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"Balík <xliff:g id="PACKAGE_NAME">%1$s</xliff:g> bol vložený do kontajnera OBMEDZENÉ"</string>
<string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string>
<string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"odoslal(a) obrázok"</string>
@@ -2494,8 +2488,7 @@
<string name="profile_label_work_3" msgid="4834572253956798917">"3. pracovný"</string>
<string name="profile_label_test" msgid="9168641926186071947">"Test"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"Spoločné"</string>
- <!-- no translation found for profile_label_supervising (5649312778545745371) -->
- <skip />
+ <string name="profile_label_supervising" msgid="5649312778545745371">"Dohľad"</string>
<string name="accessibility_label_managed_profile" msgid="3366526886209832641">"Pracovný profil"</string>
<string name="accessibility_label_private_profile" msgid="1436459319135548969">"Súkromný priestor"</string>
<string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Klon"</string>
diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml
index b0ebb5f073b7..4751cb9cfb87 100644
--- a/core/res/res/values-sl/strings.xml
+++ b/core/res/res/values-sl/strings.xml
@@ -2488,8 +2488,7 @@
<string name="profile_label_work_3" msgid="4834572253956798917">"Delo 3"</string>
<string name="profile_label_test" msgid="9168641926186071947">"Preizkus"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"Skupno"</string>
- <!-- no translation found for profile_label_supervising (5649312778545745371) -->
- <skip />
+ <string name="profile_label_supervising" msgid="5649312778545745371">"Nadzor"</string>
<string name="accessibility_label_managed_profile" msgid="3366526886209832641">"Delovni profil"</string>
<string name="accessibility_label_private_profile" msgid="1436459319135548969">"Zasebni prostor"</string>
<string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Klon"</string>
diff --git a/core/res/res/values-sq/strings.xml b/core/res/res/values-sq/strings.xml
index 0f99307da7e3..5c9becec9550 100644
--- a/core/res/res/values-sq/strings.xml
+++ b/core/res/res/values-sq/strings.xml
@@ -2273,18 +2273,12 @@
<string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Lëviz"</string>
<string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Vendos në pauzë"</string>
<string name="accessibility_autoclick_position" msgid="2933660969907663545">"Pozicioni"</string>
- <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) -->
- <skip />
+ <string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"Lëviz lart"</string>
+ <string name="accessibility_autoclick_scroll_down" msgid="3733401063292018116">"Lëviz poshtë"</string>
+ <string name="accessibility_autoclick_scroll_left" msgid="8564421367992824198">"Lëviz majtas"</string>
+ <string name="accessibility_autoclick_scroll_right" msgid="8932417330753984265">"Lëviz djathtas"</string>
+ <string name="accessibility_autoclick_scroll_exit" msgid="3788610039146769696">"Dil nga modaliteti i lëvizjes"</string>
+ <string name="accessibility_autoclick_scroll_panel_title" msgid="7120598166296447036">"Paneli i lëvizjes"</string>
<string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> është vendosur në grupin E KUFIZUAR"</string>
<string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string>
<string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"dërgoi një imazh"</string>
@@ -2492,8 +2486,7 @@
<string name="profile_label_work_3" msgid="4834572253956798917">"Puna 3"</string>
<string name="profile_label_test" msgid="9168641926186071947">"Test"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"I përbashkët"</string>
- <!-- no translation found for profile_label_supervising (5649312778545745371) -->
- <skip />
+ <string name="profile_label_supervising" msgid="5649312778545745371">"Në mbikëqyrje"</string>
<string name="accessibility_label_managed_profile" msgid="3366526886209832641">"Profili i punës"</string>
<string name="accessibility_label_private_profile" msgid="1436459319135548969">"Hapësira private"</string>
<string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Klon"</string>
diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml
index 4827c0cc3265..112173ac13ad 100644
--- a/core/res/res/values-sr/strings.xml
+++ b/core/res/res/values-sr/strings.xml
@@ -2274,18 +2274,12 @@
<string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Скролујте"</string>
<string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Паузирај"</string>
<string name="accessibility_autoclick_position" msgid="2933660969907663545">"Позиција"</string>
- <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) -->
- <skip />
+ <string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"Скролуј нагоре"</string>
+ <string name="accessibility_autoclick_scroll_down" msgid="3733401063292018116">"Скролуј надоле"</string>
+ <string name="accessibility_autoclick_scroll_left" msgid="8564421367992824198">"Скролуј улево"</string>
+ <string name="accessibility_autoclick_scroll_right" msgid="8932417330753984265">"Скролуј удесно"</string>
+ <string name="accessibility_autoclick_scroll_exit" msgid="3788610039146769696">"Изађи из режима скроловања"</string>
+ <string name="accessibility_autoclick_scroll_panel_title" msgid="7120598166296447036">"Окно за скроловање"</string>
<string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"Пакет <xliff:g id="PACKAGE_NAME">%1$s</xliff:g> је додат у сегмент ОГРАНИЧЕНО"</string>
<string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string>
<string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"је послао/ла слику"</string>
@@ -2493,8 +2487,7 @@
<string name="profile_label_work_3" msgid="4834572253956798917">"Посао 3"</string>
<string name="profile_label_test" msgid="9168641926186071947">"Тест"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"Заједничко"</string>
- <!-- no translation found for profile_label_supervising (5649312778545745371) -->
- <skip />
+ <string name="profile_label_supervising" msgid="5649312778545745371">"Надзире се"</string>
<string name="accessibility_label_managed_profile" msgid="3366526886209832641">"Пословни профил"</string>
<string name="accessibility_label_private_profile" msgid="1436459319135548969">"Приватан простор"</string>
<string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Клонирано"</string>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index c471f065b377..5cb528b5c555 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -2273,18 +2273,12 @@
<string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Scrolla"</string>
<string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Pausa"</string>
<string name="accessibility_autoclick_position" msgid="2933660969907663545">"Position"</string>
- <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) -->
- <skip />
+ <string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"Scrolla uppåt"</string>
+ <string name="accessibility_autoclick_scroll_down" msgid="3733401063292018116">"Scrolla nedåt"</string>
+ <string name="accessibility_autoclick_scroll_left" msgid="8564421367992824198">"Scrolla åt vänster"</string>
+ <string name="accessibility_autoclick_scroll_right" msgid="8932417330753984265">"Scrolla åt höger"</string>
+ <string name="accessibility_autoclick_scroll_exit" msgid="3788610039146769696">"Avsluta scrollningsläget"</string>
+ <string name="accessibility_autoclick_scroll_panel_title" msgid="7120598166296447036">"Scrollningspanel"</string>
<string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> har placerats i hinken RESTRICTED"</string>
<string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string>
<string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"har skickat en bild"</string>
@@ -2492,8 +2486,7 @@
<string name="profile_label_work_3" msgid="4834572253956798917">"Arbete 3"</string>
<string name="profile_label_test" msgid="9168641926186071947">"Test"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"Allmän"</string>
- <!-- no translation found for profile_label_supervising (5649312778545745371) -->
- <skip />
+ <string name="profile_label_supervising" msgid="5649312778545745371">"Kontrollerar"</string>
<string name="accessibility_label_managed_profile" msgid="3366526886209832641">"Jobbprofil"</string>
<string name="accessibility_label_private_profile" msgid="1436459319135548969">"Privat utrymme"</string>
<string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Klona"</string>
diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml
index 31f5592ecaad..5207e032fc30 100644
--- a/core/res/res/values-sw/strings.xml
+++ b/core/res/res/values-sw/strings.xml
@@ -2273,18 +2273,12 @@
<string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Sogeza"</string>
<string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Sitisha"</string>
<string name="accessibility_autoclick_position" msgid="2933660969907663545">"Nafasi"</string>
- <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) -->
- <skip />
+ <string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"Sogeza Juu"</string>
+ <string name="accessibility_autoclick_scroll_down" msgid="3733401063292018116">"Sogeza Chini"</string>
+ <string name="accessibility_autoclick_scroll_left" msgid="8564421367992824198">"Sogeza Kushoto"</string>
+ <string name="accessibility_autoclick_scroll_right" msgid="8932417330753984265">"Sogeza Kulia"</string>
+ <string name="accessibility_autoclick_scroll_exit" msgid="3788610039146769696">"Funga Hali ya Kusogeza"</string>
+ <string name="accessibility_autoclick_scroll_panel_title" msgid="7120598166296447036">"Sogeza Kidirisha"</string>
<string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> kimewekwa katika kikundi KILICHODHIBITIWA"</string>
<string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string>
<string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"alituma picha"</string>
@@ -2492,8 +2486,7 @@
<string name="profile_label_work_3" msgid="4834572253956798917">"Wa 3 wa Kazini"</string>
<string name="profile_label_test" msgid="9168641926186071947">"Jaribio"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"Unaoshirikiwa"</string>
- <!-- no translation found for profile_label_supervising (5649312778545745371) -->
- <skip />
+ <string name="profile_label_supervising" msgid="5649312778545745371">"Unasimamia"</string>
<string name="accessibility_label_managed_profile" msgid="3366526886209832641">"Wasifu wa kazini"</string>
<string name="accessibility_label_private_profile" msgid="1436459319135548969">"Sehemu ya faragha"</string>
<string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Nakala"</string>
diff --git a/core/res/res/values-ta/strings.xml b/core/res/res/values-ta/strings.xml
index 36b82d99aded..015ce084355b 100644
--- a/core/res/res/values-ta/strings.xml
+++ b/core/res/res/values-ta/strings.xml
@@ -2273,18 +2273,12 @@
<string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"நகர்த்தும்"</string>
<string name="accessibility_autoclick_pause" msgid="3272200156172573568">"இடைநிறுத்து"</string>
<string name="accessibility_autoclick_position" msgid="2933660969907663545">"நிலை"</string>
- <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) -->
- <skip />
+ <string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"மேலே நகர்த்து"</string>
+ <string name="accessibility_autoclick_scroll_down" msgid="3733401063292018116">"கீழே நகர்த்து"</string>
+ <string name="accessibility_autoclick_scroll_left" msgid="8564421367992824198">"இடப்புறம் நகர்த்து"</string>
+ <string name="accessibility_autoclick_scroll_right" msgid="8932417330753984265">"வலப்புறம் நகர்த்து"</string>
+ <string name="accessibility_autoclick_scroll_exit" msgid="3788610039146769696">"நகர்த்துதல் பயன்முறையில் இருந்து வெளியேறு"</string>
+ <string name="accessibility_autoclick_scroll_panel_title" msgid="7120598166296447036">"பேனலை நகர்த்து"</string>
<string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> என்பதை வரம்பிடப்பட்ட பக்கெட்திற்குள் சேர்க்கப்பட்டது"</string>
<string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string>
<string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"படம் அனுப்பப்பட்டது"</string>
@@ -2492,8 +2486,7 @@
<string name="profile_label_work_3" msgid="4834572253956798917">"பணி 3"</string>
<string name="profile_label_test" msgid="9168641926186071947">"பரிசோதனை"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"பொது"</string>
- <!-- no translation found for profile_label_supervising (5649312778545745371) -->
- <skip />
+ <string name="profile_label_supervising" msgid="5649312778545745371">"கண்காணிக்கப்படுகிறது"</string>
<string name="accessibility_label_managed_profile" msgid="3366526886209832641">"பணிக் கணக்கு"</string>
<string name="accessibility_label_private_profile" msgid="1436459319135548969">"ரகசிய இடம்"</string>
<string name="accessibility_label_clone_profile" msgid="7579118375042398784">"குளோன்"</string>
diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml
index b67f3828ab72..224ac78c5f92 100644
--- a/core/res/res/values-th/strings.xml
+++ b/core/res/res/values-th/strings.xml
@@ -2273,18 +2273,12 @@
<string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"เลื่อน"</string>
<string name="accessibility_autoclick_pause" msgid="3272200156172573568">"หยุดชั่วคราว"</string>
<string name="accessibility_autoclick_position" msgid="2933660969907663545">"วางตำแหน่ง"</string>
- <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) -->
- <skip />
+ <string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"เลื่อนขึ้น"</string>
+ <string name="accessibility_autoclick_scroll_down" msgid="3733401063292018116">"เลื่อนลง"</string>
+ <string name="accessibility_autoclick_scroll_left" msgid="8564421367992824198">"เลื่อนไปทางซ้าย"</string>
+ <string name="accessibility_autoclick_scroll_right" msgid="8932417330753984265">"เลื่อนไปทางขวา"</string>
+ <string name="accessibility_autoclick_scroll_exit" msgid="3788610039146769696">"ออกจากโหมดเลื่อน"</string>
+ <string name="accessibility_autoclick_scroll_panel_title" msgid="7120598166296447036">"เลื่อนแผง"</string>
<string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"ใส่ <xliff:g id="PACKAGE_NAME">%1$s</xliff:g> ในที่เก็บข้อมูลที่ถูกจำกัดแล้ว"</string>
<string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string>
<string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"ส่งรูปภาพ"</string>
@@ -2492,8 +2486,7 @@
<string name="profile_label_work_3" msgid="4834572253956798917">"งาน 3"</string>
<string name="profile_label_test" msgid="9168641926186071947">"ทดสอบ"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"ส่วนกลาง"</string>
- <!-- no translation found for profile_label_supervising (5649312778545745371) -->
- <skip />
+ <string name="profile_label_supervising" msgid="5649312778545745371">"ผู้ควบคุมดูแล"</string>
<string name="accessibility_label_managed_profile" msgid="3366526886209832641">"โปรไฟล์งาน"</string>
<string name="accessibility_label_private_profile" msgid="1436459319135548969">"พื้นที่ส่วนตัว"</string>
<string name="accessibility_label_clone_profile" msgid="7579118375042398784">"โคลน"</string>
diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml
index 3c380884ceea..9b6ecd30b233 100644
--- a/core/res/res/values-tl/strings.xml
+++ b/core/res/res/values-tl/strings.xml
@@ -2273,18 +2273,12 @@
<string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Mag-scroll"</string>
<string name="accessibility_autoclick_pause" msgid="3272200156172573568">"I-pause"</string>
<string name="accessibility_autoclick_position" msgid="2933660969907663545">"Posisyon"</string>
- <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) -->
- <skip />
+ <string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"Mag-scroll Pataas"</string>
+ <string name="accessibility_autoclick_scroll_down" msgid="3733401063292018116">"Mag-scroll Pababa"</string>
+ <string name="accessibility_autoclick_scroll_left" msgid="8564421367992824198">"Mag-scroll Pakaliwa"</string>
+ <string name="accessibility_autoclick_scroll_right" msgid="8932417330753984265">"Mag-scroll Pakanan"</string>
+ <string name="accessibility_autoclick_scroll_exit" msgid="3788610039146769696">"Umalis sa Scroll Mode"</string>
+ <string name="accessibility_autoclick_scroll_panel_title" msgid="7120598166296447036">"Scroll Panel"</string>
<string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"Inilagay ang <xliff:g id="PACKAGE_NAME">%1$s</xliff:g> sa PINAGHIHIGPITANG bucket"</string>
<string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string>
<string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"nagpadala ng larawan"</string>
@@ -2492,8 +2486,7 @@
<string name="profile_label_work_3" msgid="4834572253956798917">"Trabaho 3"</string>
<string name="profile_label_test" msgid="9168641926186071947">"Test"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"Communal"</string>
- <!-- no translation found for profile_label_supervising (5649312778545745371) -->
- <skip />
+ <string name="profile_label_supervising" msgid="5649312778545745371">"Namamahala"</string>
<string name="accessibility_label_managed_profile" msgid="3366526886209832641">"Profile sa trabaho"</string>
<string name="accessibility_label_private_profile" msgid="1436459319135548969">"Pribadong space"</string>
<string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Clone"</string>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index 8119d7d2bb2a..ea4cfa644bf3 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -2273,18 +2273,12 @@
<string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Kaydırma"</string>
<string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Duraklatma"</string>
<string name="accessibility_autoclick_position" msgid="2933660969907663545">"Konum"</string>
- <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) -->
- <skip />
+ <string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"Yukarı Kaydır"</string>
+ <string name="accessibility_autoclick_scroll_down" msgid="3733401063292018116">"Aşağı Kaydır"</string>
+ <string name="accessibility_autoclick_scroll_left" msgid="8564421367992824198">"Sola Kaydır"</string>
+ <string name="accessibility_autoclick_scroll_right" msgid="8932417330753984265">"Sağa Kaydır"</string>
+ <string name="accessibility_autoclick_scroll_exit" msgid="3788610039146769696">"Kaydırma Modundan Çık"</string>
+ <string name="accessibility_autoclick_scroll_panel_title" msgid="7120598166296447036">"Paneli Kaydır"</string>
<string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> KISITLANMIŞ gruba yerleştirildi"</string>
<string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string>
<string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"bir resim gönderildi"</string>
@@ -2492,8 +2486,7 @@
<string name="profile_label_work_3" msgid="4834572253956798917">"İş 3"</string>
<string name="profile_label_test" msgid="9168641926186071947">"Test"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"Paylaşılan"</string>
- <!-- no translation found for profile_label_supervising (5649312778545745371) -->
- <skip />
+ <string name="profile_label_supervising" msgid="5649312778545745371">"Gözetim"</string>
<string name="accessibility_label_managed_profile" msgid="3366526886209832641">"İş profili"</string>
<string name="accessibility_label_private_profile" msgid="1436459319135548969">"Özel alan"</string>
<string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Klon"</string>
diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml
index cda2af43ef12..693b16e88a4c 100644
--- a/core/res/res/values-uk/strings.xml
+++ b/core/res/res/values-uk/strings.xml
@@ -2275,18 +2275,12 @@
<string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Прокрутити"</string>
<string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Призупинити"</string>
<string name="accessibility_autoclick_position" msgid="2933660969907663545">"Змінити позицію"</string>
- <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) -->
- <skip />
+ <string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"Прокрутити вгору"</string>
+ <string name="accessibility_autoclick_scroll_down" msgid="3733401063292018116">"Прокрутити вниз"</string>
+ <string name="accessibility_autoclick_scroll_left" msgid="8564421367992824198">"Прокрутити ліворуч"</string>
+ <string name="accessibility_autoclick_scroll_right" msgid="8932417330753984265">"Прокрутити праворуч"</string>
+ <string name="accessibility_autoclick_scroll_exit" msgid="3788610039146769696">"Вимкнути режим прокручування"</string>
+ <string name="accessibility_autoclick_scroll_panel_title" msgid="7120598166296447036">"Панель прокручування"</string>
<string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"Пакет \"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g>\" додано в сегмент з обмеженнями"</string>
<string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string>
<string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"надіслано зображення"</string>
@@ -2494,8 +2488,7 @@
<string name="profile_label_work_3" msgid="4834572253956798917">"Робочий профіль 3"</string>
<string name="profile_label_test" msgid="9168641926186071947">"Тестовий профіль"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"Спільний профіль"</string>
- <!-- no translation found for profile_label_supervising (5649312778545745371) -->
- <skip />
+ <string name="profile_label_supervising" msgid="5649312778545745371">"Батьківськ. контроль"</string>
<string name="accessibility_label_managed_profile" msgid="3366526886209832641">"Робочий профіль"</string>
<string name="accessibility_label_private_profile" msgid="1436459319135548969">"Приватний простір"</string>
<string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Копія профілю"</string>
diff --git a/core/res/res/values-uz/strings.xml b/core/res/res/values-uz/strings.xml
index fc2bd4b605d1..f7b10bff74eb 100644
--- a/core/res/res/values-uz/strings.xml
+++ b/core/res/res/values-uz/strings.xml
@@ -2486,8 +2486,7 @@
<string name="profile_label_work_3" msgid="4834572253956798917">"Ish 3"</string>
<string name="profile_label_test" msgid="9168641926186071947">"Test"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"Umumiy"</string>
- <!-- no translation found for profile_label_supervising (5649312778545745371) -->
- <skip />
+ <string name="profile_label_supervising" msgid="5649312778545745371">"Nazorat ostida"</string>
<string name="accessibility_label_managed_profile" msgid="3366526886209832641">"Ish profili"</string>
<string name="accessibility_label_private_profile" msgid="1436459319135548969">"Maxfiy makon"</string>
<string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Nusxalash"</string>
diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml
index 4141af07dc8b..4ee534c6ecfe 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -2273,18 +2273,12 @@
<string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Cuộn"</string>
<string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Tạm dừng"</string>
<string name="accessibility_autoclick_position" msgid="2933660969907663545">"Vị trí"</string>
- <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) -->
- <skip />
+ <string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"Cuộn lên"</string>
+ <string name="accessibility_autoclick_scroll_down" msgid="3733401063292018116">"Cuộn xuống"</string>
+ <string name="accessibility_autoclick_scroll_left" msgid="8564421367992824198">"Cuộn sang trái"</string>
+ <string name="accessibility_autoclick_scroll_right" msgid="8932417330753984265">"Cuộn sang phải"</string>
+ <string name="accessibility_autoclick_scroll_exit" msgid="3788610039146769696">"Thoát khỏi chế độ cuộn"</string>
+ <string name="accessibility_autoclick_scroll_panel_title" msgid="7120598166296447036">"Cuộn bảng điều khiển"</string>
<string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"Đã đưa <xliff:g id="PACKAGE_NAME">%1$s</xliff:g> vào bộ chứa BỊ HẠN CHẾ"</string>
<string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string>
<string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"đã gửi hình ảnh"</string>
@@ -2492,8 +2486,7 @@
<string name="profile_label_work_3" msgid="4834572253956798917">"Công việc 3"</string>
<string name="profile_label_test" msgid="9168641926186071947">"Kiểm thử"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"Dùng chung"</string>
- <!-- no translation found for profile_label_supervising (5649312778545745371) -->
- <skip />
+ <string name="profile_label_supervising" msgid="5649312778545745371">"Giám sát"</string>
<string name="accessibility_label_managed_profile" msgid="3366526886209832641">"Hồ sơ công việc"</string>
<string name="accessibility_label_private_profile" msgid="1436459319135548969">"Không gian riêng tư"</string>
<string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Nhân bản"</string>
diff --git a/core/res/res/values-w192dp/dimens_watch.xml b/core/res/res/values-w192dp/dimens_watch.xml
new file mode 100644
index 000000000000..a3730aaf7961
--- /dev/null
+++ b/core/res/res/values-w192dp/dimens_watch.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2025 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.
+ -->
+<resources>
+ <!-- watch's indeterminate progress bar dimens based on the current screen size -->
+ <dimen name="loader_horizontal_min_width_watch">67dp</dimen>
+ <dimen name="loader_horizontal_min_height_watch">15dp</dimen>
+</resources>
diff --git a/core/res/res/values-w204dp/dimens_watch.xml b/core/res/res/values-w204dp/dimens_watch.xml
new file mode 100644
index 000000000000..3509474c5c2e
--- /dev/null
+++ b/core/res/res/values-w204dp/dimens_watch.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2025 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.
+ -->
+<resources>
+ <!-- watch's indeterminate progress bar dimens based on the current screen size -->
+ <dimen name="loader_horizontal_min_width_watch">70dp</dimen>
+ <dimen name="loader_horizontal_min_height_watch">15dp</dimen>
+</resources>
diff --git a/core/res/res/values-w216dp/dimens_watch.xml b/core/res/res/values-w216dp/dimens_watch.xml
new file mode 100644
index 000000000000..96d80abb5988
--- /dev/null
+++ b/core/res/res/values-w216dp/dimens_watch.xml
@@ -0,0 +1,21 @@
+<!--
+ ~ Copyright (C) 2025 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.
+ -->
+
+<resources>
+ <!-- watch's indeterminate progress bar dimens based on the current screen size -->
+ <dimen name="loader_horizontal_min_width_watch">74dp</dimen>
+ <dimen name="loader_horizontal_min_height_watch">16dp</dimen>
+</resources> \ No newline at end of file
diff --git a/core/res/res/values-w228dp/dimens_watch.xml b/core/res/res/values-w228dp/dimens_watch.xml
new file mode 100644
index 000000000000..960e3228ee0b
--- /dev/null
+++ b/core/res/res/values-w228dp/dimens_watch.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2025 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.
+ -->
+<resources>
+ <!-- watch's indeterminate progress bar dimens based on the current screen size -->
+ <dimen name="loader_horizontal_min_width_watch">78dp</dimen>
+ <dimen name="loader_horizontal_min_height_watch">16dp</dimen>
+</resources>
diff --git a/core/res/res/values-w240dp/dimens_watch.xml b/core/res/res/values-w240dp/dimens_watch.xml
new file mode 100644
index 000000000000..d8478a872ae1
--- /dev/null
+++ b/core/res/res/values-w240dp/dimens_watch.xml
@@ -0,0 +1,21 @@
+<!--
+ ~ Copyright (C) 2025 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.
+ -->
+
+<resources>
+ <!-- watch's indeterminate progress bar dimens based on the current screen size -->
+ <dimen name="loader_horizontal_min_width_watch">82dp</dimen>
+ <dimen name="loader_horizontal_min_height_watch">17dp</dimen>
+</resources>
diff --git a/core/res/res/values-watch/styles_device_defaults.xml b/core/res/res/values-watch/styles_device_defaults.xml
index fb7dbb0660c5..eeb66e7cf6a8 100644
--- a/core/res/res/values-watch/styles_device_defaults.xml
+++ b/core/res/res/values-watch/styles_device_defaults.xml
@@ -42,5 +42,8 @@
<item name="indeterminateOnly">false</item>
<!-- Use Wear Material3 ring shape as default determinate drawable -->
<item name="progressDrawable">@drawable/progress_ring_watch</item>
+ <item name="indeterminateDrawable">@drawable/loader_horizontal_watch</item>
+ <item name="android:minWidth">@dimen/loader_horizontal_min_width_watch</item>
+ <item name="android:minHeight">@dimen/loader_horizontal_min_height_watch</item>
</style>
</resources>
diff --git a/core/res/res/values-watch/themes_device_defaults.xml b/core/res/res/values-watch/themes_device_defaults.xml
index 60aec5342b4f..4738d20cdede 100644
--- a/core/res/res/values-watch/themes_device_defaults.xml
+++ b/core/res/res/values-watch/themes_device_defaults.xml
@@ -48,6 +48,9 @@ a similar way.
<item name="disabledAlpha">@dimen/disabled_alpha_device_default</item>
<item name="primaryContentAlpha">@dimen/primary_content_alpha_device_default</item>
<item name="secondaryContentAlpha">@dimen/secondary_content_alpha_device_default</item>
+
+ <!-- Material3 default delay before scroll bar fading animation. -->
+ <item name="scrollbarDefaultDelayBeforeFade">1000</item>
</style>
<!-- Variant of {@link #Theme_DeviceDefault} with no action bar -->
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index 95dd35fcb0dd..98069c65d235 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -2273,18 +2273,12 @@
<string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"滚动"</string>
<string name="accessibility_autoclick_pause" msgid="3272200156172573568">"暂停"</string>
<string name="accessibility_autoclick_position" msgid="2933660969907663545">"位置"</string>
- <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) -->
- <skip />
+ <string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"向上滚动"</string>
+ <string name="accessibility_autoclick_scroll_down" msgid="3733401063292018116">"向下滚动"</string>
+ <string name="accessibility_autoclick_scroll_left" msgid="8564421367992824198">"向左滚动"</string>
+ <string name="accessibility_autoclick_scroll_right" msgid="8932417330753984265">"向右滚动"</string>
+ <string name="accessibility_autoclick_scroll_exit" msgid="3788610039146769696">"退出滚动模式"</string>
+ <string name="accessibility_autoclick_scroll_panel_title" msgid="7120598166296447036">"滚动面板"</string>
<string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> 已被放入受限存储分区"</string>
<string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string>
<string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"发送了一张图片"</string>
@@ -2492,8 +2486,7 @@
<string name="profile_label_work_3" msgid="4834572253956798917">"工作 3"</string>
<string name="profile_label_test" msgid="9168641926186071947">"测试"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"共用"</string>
- <!-- no translation found for profile_label_supervising (5649312778545745371) -->
- <skip />
+ <string name="profile_label_supervising" msgid="5649312778545745371">"监管"</string>
<string name="accessibility_label_managed_profile" msgid="3366526886209832641">"工作资料"</string>
<string name="accessibility_label_private_profile" msgid="1436459319135548969">"私密空间"</string>
<string name="accessibility_label_clone_profile" msgid="7579118375042398784">"克隆"</string>
diff --git a/core/res/res/values-zh-rHK/strings.xml b/core/res/res/values-zh-rHK/strings.xml
index b956da1e856e..038973d760dd 100644
--- a/core/res/res/values-zh-rHK/strings.xml
+++ b/core/res/res/values-zh-rHK/strings.xml
@@ -2273,18 +2273,12 @@
<string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"捲動"</string>
<string name="accessibility_autoclick_pause" msgid="3272200156172573568">"暫停"</string>
<string name="accessibility_autoclick_position" msgid="2933660969907663545">"位置"</string>
- <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) -->
- <skip />
+ <string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"向上捲動"</string>
+ <string name="accessibility_autoclick_scroll_down" msgid="3733401063292018116">"向下捲動"</string>
+ <string name="accessibility_autoclick_scroll_left" msgid="8564421367992824198">"向左捲動"</string>
+ <string name="accessibility_autoclick_scroll_right" msgid="8932417330753984265">"向右捲動"</string>
+ <string name="accessibility_autoclick_scroll_exit" msgid="3788610039146769696">"退出捲動模式"</string>
+ <string name="accessibility_autoclick_scroll_panel_title" msgid="7120598166296447036">"捲動面板"</string>
<string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> 已納入受限制的儲存區"</string>
<string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string>
<string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"已傳送圖片"</string>
@@ -2492,8 +2486,7 @@
<string name="profile_label_work_3" msgid="4834572253956798917">"工作 3"</string>
<string name="profile_label_test" msgid="9168641926186071947">"測試"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"共用"</string>
- <!-- no translation found for profile_label_supervising (5649312778545745371) -->
- <skip />
+ <string name="profile_label_supervising" msgid="5649312778545745371">"監管"</string>
<string name="accessibility_label_managed_profile" msgid="3366526886209832641">"工作設定檔"</string>
<string name="accessibility_label_private_profile" msgid="1436459319135548969">"私人空間"</string>
<string name="accessibility_label_clone_profile" msgid="7579118375042398784">"複製"</string>
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index f9abe0e69a46..8ff587bf1e55 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -2273,18 +2273,12 @@
<string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"捲動"</string>
<string name="accessibility_autoclick_pause" msgid="3272200156172573568">"暫停"</string>
<string name="accessibility_autoclick_position" msgid="2933660969907663545">"位置"</string>
- <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) -->
- <skip />
+ <string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"向上捲動"</string>
+ <string name="accessibility_autoclick_scroll_down" msgid="3733401063292018116">"向下捲動"</string>
+ <string name="accessibility_autoclick_scroll_left" msgid="8564421367992824198">"向左捲動"</string>
+ <string name="accessibility_autoclick_scroll_right" msgid="8932417330753984265">"向右捲動"</string>
+ <string name="accessibility_autoclick_scroll_exit" msgid="3788610039146769696">"退出捲動模式"</string>
+ <string name="accessibility_autoclick_scroll_panel_title" msgid="7120598166296447036">"捲動面板"</string>
<string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"已將「<xliff:g id="PACKAGE_NAME">%1$s</xliff:g>」移入受限制的值區"</string>
<string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string>
<string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"傳送了一張圖片"</string>
@@ -2492,8 +2486,7 @@
<string name="profile_label_work_3" msgid="4834572253956798917">"工作 3"</string>
<string name="profile_label_test" msgid="9168641926186071947">"測試"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"通用"</string>
- <!-- no translation found for profile_label_supervising (5649312778545745371) -->
- <skip />
+ <string name="profile_label_supervising" msgid="5649312778545745371">"監督中"</string>
<string name="accessibility_label_managed_profile" msgid="3366526886209832641">"工作資料夾"</string>
<string name="accessibility_label_private_profile" msgid="1436459319135548969">"私人空間"</string>
<string name="accessibility_label_clone_profile" msgid="7579118375042398784">"複製"</string>
diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml
index 4292eab29a16..3fc910cca43a 100644
--- a/core/res/res/values-zu/strings.xml
+++ b/core/res/res/values-zu/strings.xml
@@ -2273,18 +2273,12 @@
<string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Skrola"</string>
<string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Misa"</string>
<string name="accessibility_autoclick_position" msgid="2933660969907663545">"Indawo"</string>
- <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) -->
- <skip />
- <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) -->
- <skip />
+ <string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"Skrolela Phezulu"</string>
+ <string name="accessibility_autoclick_scroll_down" msgid="3733401063292018116">"Skrolela Phansi"</string>
+ <string name="accessibility_autoclick_scroll_left" msgid="8564421367992824198">"Skrolela Ngakwesokunxele"</string>
+ <string name="accessibility_autoclick_scroll_right" msgid="8932417330753984265">"Skrolela Ngakwesokudla"</string>
+ <string name="accessibility_autoclick_scroll_exit" msgid="3788610039146769696">"Phuma Kumodi Yokuskrola"</string>
+ <string name="accessibility_autoclick_scroll_panel_title" msgid="7120598166296447036">"Iphaneli Yokuskrola"</string>
<string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"I-<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> ifakwe kubhakede LOKUKHAWULELWE"</string>
<string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string>
<string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"uthumele isithombe"</string>
@@ -2492,8 +2486,7 @@
<string name="profile_label_work_3" msgid="4834572253956798917">"Umsebenzi 3"</string>
<string name="profile_label_test" msgid="9168641926186071947">"Hlola"</string>
<string name="profile_label_communal" msgid="8743921499944800427">"Okomphakathi"</string>
- <!-- no translation found for profile_label_supervising (5649312778545745371) -->
- <skip />
+ <string name="profile_label_supervising" msgid="5649312778545745371">"Ukugada"</string>
<string name="accessibility_label_managed_profile" msgid="3366526886209832641">"Iphrofayela yomsebenzi"</string>
<string name="accessibility_label_private_profile" msgid="1436459319135548969">"Indawo engasese"</string>
<string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Yenza i-Clone"</string>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index d2c993aecb0d..647e3dc2d268 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -5573,6 +5573,14 @@
<!-- @hide internal use only -->
<declare-styleable name="NotificationProgressBar">
+ <!-- Minimum required drawing width for segments. The drawing width refers to the width
+ after the original segments have been adjusted for the neighboring Points and gaps.
+ This is enforced by stretching the segments that are too short. -->
+ <attr name="segMinWidth" format="dimension" />
+ <!-- The gap between two segments. -->
+ <attr name="segSegGap" format="dimension" />
+ <!-- The gap between a segment and a point. -->
+ <attr name="segPointGap" format="dimension" />
<!-- Draws the tracker on a NotificationProgressBar. -->
<attr name="tracker" format="reference" />
<!-- Height of the tracker. -->
@@ -7580,25 +7588,9 @@
<!-- NotificationProgressDrawable class -->
<!-- ================================== -->
- <!-- Drawable used to render a notification progress bar, with segments and points. -->
- <!-- @hide internal use only -->
- <declare-styleable name="NotificationProgressDrawable">
- <!-- The gap between two segments. -->
- <attr name="segSegGap" format="dimension" />
- <!-- The gap between a segment and a point. -->
- <attr name="segPointGap" format="dimension" />
- </declare-styleable>
-
<!-- Used to config the segments of a NotificationProgressDrawable. -->
<!-- @hide internal use only -->
<declare-styleable name="NotificationProgressDrawableSegments">
- <!-- TODO: b/390196782 - maybe move this to NotificationProgressBar, because that's the only
- place this is used actually. Same for NotificationProgressDrawable.segSegGap/segPointGap
- above. -->
- <!-- Minimum required drawing width. The drawing width refers to the width after
- the original segments have been adjusted for the neighboring Points and gaps. This is
- enforced by stretching the segments that are too short. -->
- <attr name="minWidth" />
<!-- Height of the solid segments. -->
<attr name="height" />
<!-- Height of the faded segments. -->
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index abf180da60c3..828461c66a1f 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -1219,6 +1219,8 @@
6 - Lock if keyguard enabled or go to sleep (doze)
7 - Dream if possible or go to sleep (doze)
8 - Go to glanceable hub or dream if possible, or sleep if neither is available (doze)
+ 9 - Go to dream if device is not dreaming, stop dream if device is dreaming, or sleep if
+ neither is available (doze)
-->
<integer name="config_shortPressOnPowerBehavior">1</integer>
@@ -3220,6 +3222,10 @@
Note that HSUM devices without this enabled will not automatically have a main user. -->
<bool name="config_isMainUserPermanentAdmin">true</bool>
+ <!-- Whether all secondary users (and the system user) are eligible to have profiles.
+ If false, only the MainUser is eligible to have profiles. -->
+ <bool name="config_supportProfilesOnNonMainUser">false</bool>
+
<!-- Whether switch to headless system user is allowed. If allowed,
headless system user can run in the foreground even though it is not a full user. -->
<bool name="config_canSwitchToHeadlessSystemUser">false</bool>
diff --git a/core/res/res/values/config_telephony.xml b/core/res/res/values/config_telephony.xml
index ef6b9188532e..1d40110dc7ca 100644
--- a/core/res/res/values/config_telephony.xml
+++ b/core/res/res/values/config_telephony.xml
@@ -86,7 +86,7 @@
CarrierConfigManager#KEY_AUTO_DATA_SWITCH_RAT_SIGNAL_SCORE_STRING_ARRAY.
If 0, the device always switch to the higher score SIM.
If < 0, the network type and signal strength based auto switch is disabled. -->
- <integer name="auto_data_switch_score_tolerance">4000</integer>
+ <integer name="auto_data_switch_score_tolerance">7000</integer>
<java-symbol type="integer" name="auto_data_switch_score_tolerance" />
<!-- Boolean indicating whether the Iwlan data service supports persistence of iwlan ipsec
@@ -261,7 +261,24 @@
to identify providers that should be ignored if the carrier config
carrier_supported_satellite_services_per_provider_bundle does not support them.
-->
- <string-array name="config_satellite_providers" translatable="false"></string-array>
+ <string-array name="config_satellite_providers" translatable="false">
+ <!-- T-Mobile - USA -->
+ <item>"310830"</item>
+ <!-- Entel Chile - Chile -->
+ <item>"73029"</item>
+ <!-- KDDI - Japan -->
+ <item>"44055"</item>
+ <!-- Kyivstar - Ukraine -->
+ <item>"255707"</item>
+ <!-- One NZ - New Zealand -->
+ <item>"53013"</item>
+ <!-- Optus - Australia -->
+ <item>"50559"</item>
+ <!-- Rogers - Canada -->
+ <item>"302723"</item>
+ <!-- Telstra - Australia -->
+ <item>"50511"</item>
+ </string-array>
<java-symbol type="array" name="config_satellite_providers" />
<!-- The identifier of the satellite's SIM profile. The identifier is composed of MCC and MNC
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index 534a3a01285a..c0d2779f3a31 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -338,9 +338,9 @@
<!-- The spacing between the content and the header text above it, scaling with text size.
This value is chosen so that, taking into account the text spacing for both the text in the
- top line and the text in the content, the distance between them is 4dp with the default
+ top line and the text in the content, the distance between them is ~2dp with the default
screen configuration (and will grow accordingly for larger font sizes) -->
- <dimen name="notification_2025_content_margin_top">10sp</dimen>
+ <dimen name="notification_2025_content_margin_top">8sp</dimen>
<!-- height of the content margin that is applied at the end of the notification content -->
<dimen name="notification_content_margin">20dp</dimen>
@@ -523,6 +523,9 @@
<!-- The spacing between messages in Notification.MessagingStyle -->
<dimen name="notification_messaging_spacing">6dp</dimen>
+ <!-- The spacing between messages in Notification.MessagingStyle (2025 redesign version) -->
+ <dimen name="notification_2025_messaging_spacing">14dp</dimen>
+
<!-- The spacing between messages in Notification.MessagingStyle -->
<dimen name="notification_messaging_spacing_conversation_group">24dp</dimen>
diff --git a/core/res/res/values/dimens_watch.xml b/core/res/res/values/dimens_watch.xml
index 7462b733b0ae..a8ed666d13fe 100644
--- a/core/res/res/values/dimens_watch.xml
+++ b/core/res/res/values/dimens_watch.xml
@@ -61,4 +61,8 @@
<dimen name="disabled_alpha_wear_material3">0.12</dimen>
<!-- Alpha transparency applied to elements which are considered primary (e.g. primary text) -->
<dimen name="primary_content_alpha_wear_material3">0.38</dimen>
+
+ <!-- watch's indeterminate progress bar dimens -->
+ <dimen name="loader_horizontal_min_width_watch">76dp</dimen>
+ <dimen name="loader_horizontal_min_height_watch">14dp</dimen>
</resources>
diff --git a/core/res/res/values/styles_material.xml b/core/res/res/values/styles_material.xml
index 73681d26f297..8f13ee1ccb49 100644
--- a/core/res/res/values/styles_material.xml
+++ b/core/res/res/values/styles_material.xml
@@ -503,6 +503,9 @@ please see styles_device_defaults.xml.
<style name="Widget.Material.Notification.ProgressBar" parent="Widget.Material.Light.ProgressBar.Horizontal" />
<style name="Widget.Material.Notification.NotificationProgressBar" parent="Widget.Material.Light.ProgressBar.Horizontal">
+ <item name="segMinWidth">@dimen/notification_progress_segments_min_width</item>
+ <item name="segSegGap">@dimen/notification_progress_segSeg_gap</item>
+ <item name="segPointGap">@dimen/notification_progress_segPoint_gap</item>
<item name="progressDrawable">@drawable/notification_progress</item>
<item name="trackerHeight">@dimen/notification_progress_tracker_height</item>
</style>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 1bcb55499898..219ac3f89997 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -363,6 +363,7 @@
<java-symbol type="bool" name="config_speed_up_audio_on_mt_calls" />
<java-symbol type="bool" name="config_useFixedVolume" />
<java-symbol type="bool" name="config_isMainUserPermanentAdmin"/>
+ <java-symbol type="bool" name="config_supportProfilesOnNonMainUser"/>
<java-symbol type="bool" name="config_canSwitchToHeadlessSystemUser"/>
<java-symbol type="bool" name="config_enableMultiUserUI"/>
<java-symbol type="bool" name="config_enableMultipleAdmins"/>
diff --git a/core/res/res/xml/sms_short_codes.xml b/core/res/res/xml/sms_short_codes.xml
index 9b3a6cba5f23..36564cd90d05 100644
--- a/core/res/res/xml/sms_short_codes.xml
+++ b/core/res/res/xml/sms_short_codes.xml
@@ -358,7 +358,7 @@
<!-- USA: 5-6 digits (premium codes from https://www.premiumsmsrefunds.com/ShortCodes.htm),
visual voicemail code for T-Mobile: 122 -->
- <shortcode country="us" pattern="\\d{5,6}" premium="20433|21(?:344|472)|22715|23(?:333|847)|24(?:15|28)0|25209|27(?:449|606|663)|28498|305(?:00|83)|32(?:340|941)|33(?:166|786|849)|34746|35(?:182|564)|37975|38(?:135|146|254)|41(?:366|463)|42335|43(?:355|500)|44(?:578|711|811)|45814|46(?:157|173|327)|46666|47553|48(?:221|277|669)|50(?:844|920)|51(?:062|368)|52944|54(?:723|892)|55928|56483|57370|59(?:182|187|252|342)|60339|61(?:266|982)|62478|64(?:219|898)|65(?:108|500)|69(?:208|388)|70877|71851|72(?:078|087|465)|73(?:288|588|882|909|997)|74(?:034|332|815)|76426|79213|81946|83177|84(?:103|685)|85797|86(?:234|236|666)|89616|90(?:715|842|938)|91(?:362|958)|94719|95297|96(?:040|666|835|969)|97(?:142|294|688)|99(?:689|796|807)" standard="44567|244444" free="122|87902|21696|24614|28003|30356|33669|40196|41064|41270|43753|44034|46645|52413|56139|57969|61785|66975|75136|76227|81398|83952|85140|86566|86799|95737|96684|99245|611611|96831|10907" />
+ <shortcode country="us" pattern="\\d{5,6}" free="122|\\d{5,6}" />
<!--Uruguay : 1-6 digits (standard system default, not country specific) -->
<shortcode country="uy" pattern="\\d{1,6}" free="55002|191289" />
diff --git a/core/tests/FileSystemUtilsTest/OWNERS b/core/tests/FileSystemUtilsTest/OWNERS
new file mode 100644
index 000000000000..74eeacfeb973
--- /dev/null
+++ b/core/tests/FileSystemUtilsTest/OWNERS
@@ -0,0 +1,2 @@
+waghpawan@google.com
+kaleshsingh@google.com
diff --git a/core/tests/FileSystemUtilsTest/src/com/android/internal/content/FileSystemUtilsTest.java b/core/tests/FileSystemUtilsTest/src/com/android/internal/content/FileSystemUtilsTest.java
index 208d74e49afe..dbfd3e8ccdaa 100644
--- a/core/tests/FileSystemUtilsTest/src/com/android/internal/content/FileSystemUtilsTest.java
+++ b/core/tests/FileSystemUtilsTest/src/com/android/internal/content/FileSystemUtilsTest.java
@@ -38,6 +38,8 @@ public class FileSystemUtilsTest extends BaseHostJUnit4Test {
private static final String PAGE_SIZE_COMPAT_ENABLED_BY_PLATFORM =
"app_with_4kb_elf_no_override.apk";
+ private static final int DEVICE_WAIT_TIMEOUT = 120000;
+
@Test
@AppModeFull
public void runPunchedApp_embeddedNativeLibs() throws DeviceNotAvailableException {
@@ -98,8 +100,20 @@ public class FileSystemUtilsTest extends BaseHostJUnit4Test {
@AppModeFull
public void runAppWith4KbLib_compatByAlignmentChecks()
throws DeviceNotAvailableException, TargetSetupError {
+ // make sure that device is available for UI test
+ prepareDevice();
// This test is expected to fail since compat is disabled in manifest
runPageSizeCompatTest(PAGE_SIZE_COMPAT_ENABLED_BY_PLATFORM,
"testPageSizeCompat_compatByAlignmentChecks");
}
+
+ private void prepareDevice() throws DeviceNotAvailableException {
+ // Verify that device is online before running test and enable root
+ getDevice().waitForDeviceAvailable(DEVICE_WAIT_TIMEOUT);
+ getDevice().enableAdbRoot();
+ getDevice().waitForDeviceAvailable(DEVICE_WAIT_TIMEOUT);
+
+ getDevice().executeShellCommand("input keyevent KEYCODE_WAKEUP");
+ getDevice().executeShellCommand("wm dismiss-keyguard");
+ }
}
diff --git a/core/tests/coretests/src/android/animation/AnimatorSetCallsTest.java b/core/tests/coretests/src/android/animation/AnimatorSetCallsTest.java
index 6d86bd209a3d..746ba9659c83 100644
--- a/core/tests/coretests/src/android/animation/AnimatorSetCallsTest.java
+++ b/core/tests/coretests/src/android/animation/AnimatorSetCallsTest.java
@@ -38,6 +38,7 @@ import java.util.ArrayList;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.Consumer;
@MediumTest
@@ -490,9 +491,24 @@ public class AnimatorSetCallsTest {
@Test
public void testCancelOnPendingEndListener() throws Throwable {
+ testPendingEndListener(AnimatorSet::cancel);
+ }
+
+ @Test
+ public void testEndOnPendingEndListener() throws Throwable {
+ testPendingEndListener(animatorSet -> {
+ // This verifies that isRunning() and isStarted() are true at last frame.
+ // Then the end() should invoke the end callback immediately.
+ if (animatorSet.isRunning() && animatorSet.isStarted()) {
+ animatorSet.end();
+ }
+ });
+ }
+
+ private void testPendingEndListener(Consumer<AnimatorSet> finishOnLastFrame) throws Throwable {
final CountDownLatch endLatch = new CountDownLatch(1);
final Handler handler = new Handler(Looper.getMainLooper());
- final boolean[] endCalledRightAfterCancel = new boolean[2];
+ final boolean[] endCalledImmediately = new boolean[2];
final AnimatorSet set = new AnimatorSet();
final ValueAnimatorTests.MyListener asListener = new ValueAnimatorTests.MyListener();
final ValueAnimatorTests.MyListener vaListener = new ValueAnimatorTests.MyListener();
@@ -502,9 +518,9 @@ public class AnimatorSetCallsTest {
va.addUpdateListener(animation -> {
if (animation.getAnimatedFraction() == 1f) {
handler.post(() -> {
- set.cancel();
- endCalledRightAfterCancel[0] = vaListener.endCalled;
- endCalledRightAfterCancel[1] = asListener.endCalled;
+ finishOnLastFrame.accept(set);
+ endCalledImmediately[0] = vaListener.endCalled;
+ endCalledImmediately[1] = asListener.endCalled;
endLatch.countDown();
});
}
@@ -517,8 +533,8 @@ public class AnimatorSetCallsTest {
try {
handler.post(set::start);
assertTrue(endLatch.await(1, TimeUnit.SECONDS));
- assertTrue(endCalledRightAfterCancel[0]);
- assertTrue(endCalledRightAfterCancel[1]);
+ assertTrue(endCalledImmediately[0]);
+ assertTrue(endCalledImmediately[1]);
} finally {
ValueAnimator.setPostNotifyEndListenerEnabled(false);
}
diff --git a/core/tests/coretests/src/android/animation/ValueAnimatorTests.java b/core/tests/coretests/src/android/animation/ValueAnimatorTests.java
index a55909f0c193..89664fff979b 100644
--- a/core/tests/coretests/src/android/animation/ValueAnimatorTests.java
+++ b/core/tests/coretests/src/android/animation/ValueAnimatorTests.java
@@ -44,6 +44,7 @@ import org.junit.runner.RunWith;
import java.util.ArrayList;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
+import java.util.function.Consumer;
@RunWith(AndroidJUnit4.class)
@MediumTest
@@ -923,9 +924,25 @@ public class ValueAnimatorTests {
@Test
public void testCancelOnPendingEndListener() throws Throwable {
+ testPendingEndListener(ValueAnimator::cancel);
+ }
+
+ @Test
+ public void testEndOnPendingEndListener() throws Throwable {
+ testPendingEndListener(animator -> {
+ // This verifies that isRunning() and isStarted() are true at last frame.
+ // Then the end() should invoke the end callback immediately.
+ if (animator.isRunning() && animator.isStarted()) {
+ animator.end();
+ }
+ });
+ }
+
+ private void testPendingEndListener(Consumer<ValueAnimator> finishOnLastFrame)
+ throws Throwable {
+ final boolean[] endCalledImmediately = new boolean[1];
final CountDownLatch endLatch = new CountDownLatch(1);
final Handler handler = new Handler(Looper.getMainLooper());
- final boolean[] endCalledRightAfterCancel = new boolean[1];
final MyListener listener = new MyListener();
final ValueAnimator va = new ValueAnimator();
va.setFloatValues(0f, 1f);
@@ -933,8 +950,8 @@ public class ValueAnimatorTests {
va.addUpdateListener(animation -> {
if (animation.getAnimatedFraction() == 1f) {
handler.post(() -> {
- va.cancel();
- endCalledRightAfterCancel[0] = listener.endCalled;
+ finishOnLastFrame.accept(va);
+ endCalledImmediately[0] = listener.endCalled;
endLatch.countDown();
});
}
@@ -945,7 +962,7 @@ public class ValueAnimatorTests {
try {
handler.post(va::start);
assertThat(endLatch.await(1, TimeUnit.SECONDS)).isTrue();
- assertThat(endCalledRightAfterCancel[0]).isTrue();
+ assertThat(endCalledImmediately[0]).isTrue();
} finally {
ValueAnimator.setPostNotifyEndListenerEnabled(false);
}
diff --git a/core/tests/coretests/src/android/app/ApplicationPackageManagerTest.java b/core/tests/coretests/src/android/app/ApplicationPackageManagerTest.java
index 62d89f6dc846..146b386e5a4b 100644
--- a/core/tests/coretests/src/android/app/ApplicationPackageManagerTest.java
+++ b/core/tests/coretests/src/android/app/ApplicationPackageManagerTest.java
@@ -16,19 +16,37 @@
package android.app;
+import static android.content.Intent.ACTION_MAIN;
+import static android.content.Intent.CATEGORY_INFO;
+import static android.content.Intent.CATEGORY_LAUNCHER;
+import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
+import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE;
+import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
import static android.os.storage.VolumeInfo.STATE_MOUNTED;
import static android.os.storage.VolumeInfo.STATE_UNMOUNTED;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.argThat;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.content.ComponentName;
import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageManager;
import android.content.pm.PackageInfo;
import android.content.pm.PackageItemInfo;
+import android.content.pm.PackageManager.ResolveInfoFlags;
+import android.content.pm.ResolveInfo;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.content.res.XmlResourceParser;
@@ -44,6 +62,7 @@ import com.android.internal.annotations.VisibleForTesting;
import junit.framework.TestCase;
+import org.mockito.ArgumentMatcher;
import org.mockito.Mockito;
import org.xmlpull.v1.XmlPullParser;
@@ -102,14 +121,14 @@ public class ApplicationPackageManagerTest extends TestCase {
sVolumes.add(sPrivateUnmountedVol);
}
- private static final class MockedApplicationPackageManager extends ApplicationPackageManager {
+ public static class MockedApplicationPackageManager extends ApplicationPackageManager {
private boolean mForceAllowOnExternal = false;
private boolean mAllow3rdPartyOnInternal = true;
private HashMap<ApplicationInfo, Resources> mResourcesMap;
public MockedApplicationPackageManager() {
super(null, null);
- mResourcesMap = new HashMap<ApplicationInfo, Resources>();
+ mResourcesMap = new HashMap<>();
}
public void setForceAllowOnExternal(boolean forceAllowOnExternal) {
@@ -153,7 +172,7 @@ public class ApplicationPackageManagerTest extends TestCase {
}
private StorageManager getMockedStorageManager() {
- StorageManager storageManager = Mockito.mock(StorageManager.class);
+ StorageManager storageManager = mock(StorageManager.class);
Mockito.when(storageManager.getVolumes()).thenReturn(sVolumes);
Mockito.when(storageManager.findVolumeById(VolumeInfo.ID_PRIVATE_INTERNAL))
.thenReturn(sInternalVol);
@@ -190,7 +209,7 @@ public class ApplicationPackageManagerTest extends TestCase {
sysAppInfo.flags = ApplicationInfo.FLAG_SYSTEM;
StorageManager storageManager = getMockedStorageManager();
- IPackageManager pm = Mockito.mock(IPackageManager.class);
+ IPackageManager pm = mock(IPackageManager.class);
MockedApplicationPackageManager appPkgMgr = new MockedApplicationPackageManager();
@@ -220,7 +239,7 @@ public class ApplicationPackageManagerTest extends TestCase {
ApplicationInfo appInfo = new ApplicationInfo();
StorageManager storageManager = getMockedStorageManager();
- IPackageManager pm = Mockito.mock(IPackageManager.class);
+ IPackageManager pm = mock(IPackageManager.class);
Mockito.when(pm.isPackageDeviceAdminOnAnyUser(Mockito.anyString())).thenReturn(false);
MockedApplicationPackageManager appPkgMgr = new MockedApplicationPackageManager();
@@ -249,7 +268,7 @@ public class ApplicationPackageManagerTest extends TestCase {
ApplicationInfo appInfo = new ApplicationInfo();
StorageManager storageManager = getMockedStorageManager();
- IPackageManager pm = Mockito.mock(IPackageManager.class);
+ IPackageManager pm = mock(IPackageManager.class);
MockedApplicationPackageManager appPkgMgr = new MockedApplicationPackageManager();
appPkgMgr.setForceAllowOnExternal(true);
@@ -291,15 +310,15 @@ public class ApplicationPackageManagerTest extends TestCase {
public void testExtractPackageItemInfoAttributes_noMetaData() {
final MockedApplicationPackageManager appPkgMgr = new MockedApplicationPackageManager();
- final PackageItemInfo packageItemInfo = Mockito.mock(PackageItemInfo.class);
+ final PackageItemInfo packageItemInfo = mock(PackageItemInfo.class);
assertThat(appPkgMgr.extractPackageItemInfoAttributes(packageItemInfo, null, null,
new int[]{})).isNull();
}
public void testExtractPackageItemInfoAttributes_noParser() {
final MockedApplicationPackageManager appPkgMgr = new MockedApplicationPackageManager();
- final PackageItemInfo packageItemInfo = Mockito.mock(PackageItemInfo.class);
- final ApplicationInfo applicationInfo = Mockito.mock(ApplicationInfo.class);
+ final PackageItemInfo packageItemInfo = mock(PackageItemInfo.class);
+ final ApplicationInfo applicationInfo = mock(ApplicationInfo.class);
when(packageItemInfo.getApplicationInfo()).thenReturn(applicationInfo);
assertThat(appPkgMgr.extractPackageItemInfoAttributes(packageItemInfo, null, null,
new int[]{})).isNull();
@@ -307,8 +326,8 @@ public class ApplicationPackageManagerTest extends TestCase {
public void testExtractPackageItemInfoAttributes_noMetaDataXml() {
final MockedApplicationPackageManager appPkgMgr = new MockedApplicationPackageManager();
- final PackageItemInfo packageItemInfo = Mockito.mock(PackageItemInfo.class);
- final ApplicationInfo applicationInfo = Mockito.mock(ApplicationInfo.class);
+ final PackageItemInfo packageItemInfo = mock(PackageItemInfo.class);
+ final ApplicationInfo applicationInfo = mock(ApplicationInfo.class);
when(packageItemInfo.getApplicationInfo()).thenReturn(applicationInfo);
when(packageItemInfo.loadXmlMetaData(any(), any())).thenReturn(null);
assertThat(appPkgMgr.extractPackageItemInfoAttributes(packageItemInfo, null, null,
@@ -318,9 +337,9 @@ public class ApplicationPackageManagerTest extends TestCase {
public void testExtractPackageItemInfoAttributes_nonMatchingRootTag() throws Exception {
final String rootTag = "rootTag";
final MockedApplicationPackageManager appPkgMgr = new MockedApplicationPackageManager();
- final PackageItemInfo packageItemInfo = Mockito.mock(PackageItemInfo.class);
- final ApplicationInfo applicationInfo = Mockito.mock(ApplicationInfo.class);
- final XmlResourceParser parser = Mockito.mock(XmlResourceParser.class);
+ final PackageItemInfo packageItemInfo = mock(PackageItemInfo.class);
+ final ApplicationInfo applicationInfo = mock(ApplicationInfo.class);
+ final XmlResourceParser parser = mock(XmlResourceParser.class);
when(packageItemInfo.getApplicationInfo()).thenReturn(applicationInfo);
packageItemInfo.metaData = new Bundle();
@@ -334,11 +353,11 @@ public class ApplicationPackageManagerTest extends TestCase {
public void testExtractPackageItemInfoAttributes_successfulExtraction() throws Exception {
final String rootTag = "rootTag";
final MockedApplicationPackageManager appPkgMgr = new MockedApplicationPackageManager();
- final PackageItemInfo packageItemInfo = Mockito.mock(PackageItemInfo.class);
- final ApplicationInfo applicationInfo = Mockito.mock(ApplicationInfo.class);
- final XmlResourceParser parser = Mockito.mock(XmlResourceParser.class);
- final Resources resources = Mockito.mock(Resources.class);
- final TypedArray attributes = Mockito.mock(TypedArray.class);
+ final PackageItemInfo packageItemInfo = mock(PackageItemInfo.class);
+ final ApplicationInfo applicationInfo = mock(ApplicationInfo.class);
+ final XmlResourceParser parser = mock(XmlResourceParser.class);
+ final Resources resources = mock(Resources.class);
+ final TypedArray attributes = mock(TypedArray.class);
when(packageItemInfo.getApplicationInfo()).thenReturn(applicationInfo);
packageItemInfo.metaData = new Bundle();
@@ -351,4 +370,123 @@ public class ApplicationPackageManagerTest extends TestCase {
assertThat(appPkgMgr.extractPackageItemInfoAttributes(packageItemInfo, null, rootTag,
new int[]{})).isEqualTo(attributes);
}
+
+ public void testGetLaunchIntentForPackage_categoryInfoActivity_returnsIt() throws Exception {
+ String pkg = "com.some.package";
+ int userId = 42;
+ ResolveInfo categoryInfoResolveInfo = new ResolveInfo();
+ categoryInfoResolveInfo.activityInfo = new ActivityInfo();
+ categoryInfoResolveInfo.activityInfo.packageName = pkg;
+ categoryInfoResolveInfo.activityInfo.name = "activity";
+ Intent baseIntent = new Intent(ACTION_MAIN).setPackage(pkg);
+
+ final MockedApplicationPackageManager pm = spy(new MockedApplicationPackageManager());
+ doReturn(userId).when(pm).getUserId();
+ doReturn(List.of(categoryInfoResolveInfo))
+ .when(pm).queryIntentActivitiesAsUser(
+ eqIntent(new Intent(baseIntent).addCategory(CATEGORY_INFO)),
+ any(ResolveInfoFlags.class),
+ anyInt());
+ doReturn(
+ List.of())
+ .when(pm).queryIntentActivitiesAsUser(
+ eqIntent(new Intent(baseIntent).addCategory(CATEGORY_LAUNCHER)),
+ any(ResolveInfoFlags.class),
+ anyInt());
+
+ Intent intent = pm.getLaunchIntentForPackage(pkg, true);
+
+ assertThat(intent).isNotNull();
+ assertThat(intent.getComponent()).isEqualTo(new ComponentName(pkg, "activity"));
+ assertThat(intent.getCategories()).containsExactly(CATEGORY_INFO);
+ assertThat(intent.getFlags()).isEqualTo(FLAG_ACTIVITY_NEW_TASK);
+ verify(pm).queryIntentActivitiesAsUser(
+ eqIntent(new Intent(ACTION_MAIN).addCategory(CATEGORY_INFO).setPackage(pkg)),
+ eqResolveInfoFlags(MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE),
+ eq(userId));
+ }
+
+ public void testGetLaunchIntentForPackage_categoryLauncherActivity_returnsIt() {
+ String pkg = "com.some.package";
+ int userId = 42;
+ ResolveInfo categoryLauncherResolveInfo1 = new ResolveInfo();
+ categoryLauncherResolveInfo1.activityInfo = new ActivityInfo();
+ categoryLauncherResolveInfo1.activityInfo.packageName = pkg;
+ categoryLauncherResolveInfo1.activityInfo.name = "activity1";
+ ResolveInfo categoryLauncherResolveInfo2 = new ResolveInfo();
+ categoryLauncherResolveInfo2.activityInfo = new ActivityInfo();
+ categoryLauncherResolveInfo2.activityInfo.packageName = pkg;
+ categoryLauncherResolveInfo2.activityInfo.name = "activity2";
+ Intent baseIntent = new Intent(ACTION_MAIN).setPackage(pkg);
+
+ final MockedApplicationPackageManager pm = spy(new MockedApplicationPackageManager());
+ doReturn(userId).when(pm).getUserId();
+ doReturn(List.of())
+ .when(pm).queryIntentActivitiesAsUser(
+ eqIntent(new Intent(baseIntent).addCategory(CATEGORY_INFO)),
+ any(ResolveInfoFlags.class),
+ anyInt());
+ doReturn(
+ List.of(categoryLauncherResolveInfo1, categoryLauncherResolveInfo2))
+ .when(pm).queryIntentActivitiesAsUser(
+ eqIntent(new Intent(baseIntent).addCategory(CATEGORY_LAUNCHER)),
+ any(ResolveInfoFlags.class),
+ anyInt());
+
+ Intent intent = pm.getLaunchIntentForPackage(pkg, true);
+
+ assertThat(intent).isNotNull();
+ assertThat(intent.getComponent()).isEqualTo(new ComponentName(pkg, "activity1"));
+ assertThat(intent.getCategories()).containsExactly(CATEGORY_LAUNCHER);
+ assertThat(intent.getFlags()).isEqualTo(FLAG_ACTIVITY_NEW_TASK);
+ }
+
+ public void testGetLaunchIntentForPackage_noSuitableActivity_returnsNull() throws Exception {
+ String pkg = "com.some.package";
+ int userId = 42;
+
+ final MockedApplicationPackageManager pm = spy(new MockedApplicationPackageManager());
+ doReturn(userId).when(pm).getUserId();
+ doReturn(List.of())
+ .when(pm).queryIntentActivitiesAsUser(
+ any(),
+ any(ResolveInfoFlags.class),
+ anyInt());
+
+ Intent intent = pm.getLaunchIntentForPackage(pkg, true);
+
+ assertThat(intent).isNull();
+ }
+
+ /** Equality check for intents -- ignoring extras */
+ private static Intent eqIntent(Intent wanted) {
+ return argThat(
+ new ArgumentMatcher<>() {
+ @Override
+ public boolean matches(Intent argument) {
+ return wanted.filterEquals(argument)
+ && wanted.getFlags() == argument.getFlags();
+ }
+
+ @Override
+ public String toString() {
+ return wanted.toString();
+ }
+ });
+ }
+
+ private static ResolveInfoFlags eqResolveInfoFlags(long flagsWanted) {
+ return argThat(
+ new ArgumentMatcher<>() {
+ @Override
+ public boolean matches(ResolveInfoFlags argument) {
+ return argument.getValue() == flagsWanted;
+ }
+
+ @Override
+ public String toString() {
+ return String.valueOf(flagsWanted);
+ }
+ });
+ }
}
diff --git a/core/tests/coretests/src/android/content/IntentTest.java b/core/tests/coretests/src/android/content/IntentTest.java
index fa1948d9786c..1dbe7f5f245b 100644
--- a/core/tests/coretests/src/android/content/IntentTest.java
+++ b/core/tests/coretests/src/android/content/IntentTest.java
@@ -22,6 +22,7 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
+import android.net.Uri;
import android.os.Binder;
import android.os.Bundle;
import android.os.IBinder;
@@ -32,14 +33,21 @@ import android.platform.test.flag.junit.CheckFlagsRule;
import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.security.Flags;
import android.util.ArraySet;
+import android.util.Xml;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
+import com.android.internal.util.XmlUtils;
+import com.android.modules.utils.TypedXmlPullParser;
+import com.android.modules.utils.TypedXmlSerializer;
+
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@@ -277,4 +285,40 @@ public class IntentTest {
assertThat(b2.getBundle("bundle").getClassLoader()).isEqualTo(cl);
}
+ @Test
+ @RequiresFlagsEnabled(android.content.flags.Flags.FLAG_INTENT_SAVE_TO_XML_PACKAGE)
+ public void testSaveToXmlAndRestore() throws Exception {
+ // Create an intent and set fields.
+ Intent original = new Intent();
+ original.setAction(Intent.ACTION_MAIN);
+ original.setComponent(ComponentName.createRelative("com.intent.test", "IntentTest"));
+ original.setData(Uri.parse("content://path/to/file.txt"));
+ original.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
+ original.setIdentifier("unique_identifier");
+ original.setPackage("com.intent.test");
+ original.addCategory(Intent.CATEGORY_LAUNCHER);
+ original.putExtra("Name", "Some really important data");
+
+ String tag = "intent";
+
+ // Write to xml.
+ ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
+ TypedXmlSerializer serializer = Xml.resolveSerializer(byteArrayOutputStream);
+ serializer.startDocument(null, true);
+ serializer.startTag(null, tag);
+ original.saveToXml(serializer);
+ serializer.endTag(null, tag);
+ serializer.endDocument();
+
+ // Restore from xml.
+ ByteArrayInputStream byteArrayInputStream =
+ new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
+ TypedXmlPullParser parser = Xml.resolvePullParser(byteArrayInputStream);
+ XmlUtils.beginDocument(parser, tag);
+ Intent restored = Intent.restoreFromXml(parser);
+
+ // Verify that the restored intent passed filterEquals on the original.
+ assertTrue(original.filterEquals(restored));
+ }
+
}
diff --git a/core/tests/coretests/src/android/content/pm/RegisteredServicesCacheUnitTest.java b/core/tests/coretests/src/android/content/pm/RegisteredServicesCacheUnitTest.java
index 8349659517c5..b63fcdc8362f 100644
--- a/core/tests/coretests/src/android/content/pm/RegisteredServicesCacheUnitTest.java
+++ b/core/tests/coretests/src/android/content/pm/RegisteredServicesCacheUnitTest.java
@@ -68,6 +68,7 @@ import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
/**
* Unit tests for {@link android.content.pm.RegisteredServicesCache}
@@ -84,8 +85,8 @@ public class RegisteredServicesCacheUnitTest {
@Rule
public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
- private final ResolveInfo mResolveInfo1 = new ResolveInfo();
- private final ResolveInfo mResolveInfo2 = new ResolveInfo();
+ private final TestResolveInfo mResolveInfo1 = new TestResolveInfo();
+ private final TestResolveInfo mResolveInfo2 = new TestResolveInfo();
private final TestServiceType mTestServiceType1 = new TestServiceType("t1", "value1");
private final TestServiceType mTestServiceType2 = new TestServiceType("t2", "value2");
@Mock
@@ -195,13 +196,13 @@ public class RegisteredServicesCacheUnitTest {
reset(testServicesCache);
- testServicesCache.clearServicesForQuerying();
int u1uid = UserHandle.getUid(U1, UID1);
assertThat(u1uid).isNotEqualTo(UID1);
final RegisteredServicesCache.ServiceInfo<TestServiceType> serviceInfo2 = newServiceInfo(
mTestServiceType1, u1uid, mResolveInfo1.serviceInfo.getComponentName(),
1000L /* lastUpdateTime */);
+ mResolveInfo1.setResolveInfoId(U1);
testServicesCache.addServiceForQuerying(U1, mResolveInfo1, serviceInfo2);
testServicesCache.getAllServices(U1);
@@ -286,7 +287,7 @@ public class RegisteredServicesCacheUnitTest {
}
@Test
- public void testClearServiceInfoCachesAfterTimeout() throws Exception {
+ public void testClearServiceInfoCachesForSingleUserAfterTimeout() throws Exception {
PackageInfo packageInfo1 = createPackageInfo(1000L /* lastUpdateTime */);
when(mMockPackageManager.getPackageInfoAsUser(eq(mResolveInfo1.serviceInfo.packageName),
anyInt(), eq(U0))).thenReturn(packageInfo1);
@@ -316,6 +317,58 @@ public class RegisteredServicesCacheUnitTest {
verify(testServicesCache, times(1)).parseServiceInfo(eq(mResolveInfo1), eq(1000L));
}
+ @Test
+ public void testClearServiceInfoCachesForMultiUserAfterTimeout() throws Exception {
+ PackageInfo packageInfo1 = createPackageInfo(1000L /* lastUpdateTime */);
+ when(mMockPackageManager.getPackageInfoAsUser(eq(mResolveInfo1.serviceInfo.packageName),
+ anyInt(), eq(U0))).thenReturn(packageInfo1);
+ PackageInfo packageInfo2 = createPackageInfo(2000L /* lastUpdateTime */);
+ when(mMockPackageManager.getPackageInfoAsUser(eq(mResolveInfo2.serviceInfo.packageName),
+ anyInt(), eq(U1))).thenReturn(packageInfo2);
+
+ TestRegisteredServicesCache testServicesCache = spy(
+ new TestRegisteredServicesCache(mMockInjector, null /* serializerAndParser */));
+ final RegisteredServicesCache.ServiceInfo<TestServiceType> serviceInfo1 = newServiceInfo(
+ mTestServiceType1, UID1, mResolveInfo1.serviceInfo.getComponentName(),
+ 1000L /* lastUpdateTime */);
+ testServicesCache.addServiceForQuerying(U0, mResolveInfo1, serviceInfo1);
+
+ int u1uid = UserHandle.getUid(U1, UID1);
+ final RegisteredServicesCache.ServiceInfo<TestServiceType> serviceInfo2 = newServiceInfo(
+ mTestServiceType2, u1uid, mResolveInfo2.serviceInfo.getComponentName(),
+ 2000L /* lastUpdateTime */);
+ testServicesCache.addServiceForQuerying(U1, mResolveInfo2, serviceInfo2);
+
+ // Don't invoke run on the Runnable for U0 user, and it will not clear the service info of
+ // U0 user. Invoke run on the Runnable for U1 user, and it will just clear the service info
+ // of U1 user.
+ doAnswer(invocation -> {
+ Message message = invocation.getArgument(0);
+ if (!message.obj.equals(Integer.valueOf(U0))) {
+ message.getCallback().run();
+ }
+ return true;
+ }).when(mMockBackgroundHandler).sendMessageAtTime(any(Message.class), anyLong());
+
+ // It will generate the service info of U0 user into cache.
+ testServicesCache.getAllServices(U0);
+ verify(testServicesCache, times(1)).parseServiceInfo(eq(mResolveInfo1), eq(1000L));
+ // It will generate the service info of U1 user into cache.
+ testServicesCache.getAllServices(U1);
+ verify(testServicesCache, times(1)).parseServiceInfo(eq(mResolveInfo2), eq(2000L));
+ verify(mMockBackgroundHandler, times(2)).sendMessageAtTime(any(Message.class), anyLong());
+
+ reset(testServicesCache);
+
+ testServicesCache.invalidateCache(U0);
+ testServicesCache.getAllServices(U0);
+ verify(testServicesCache, never()).parseServiceInfo(eq(mResolveInfo1), eq(1000L));
+
+ testServicesCache.invalidateCache(U1);
+ testServicesCache.getAllServices(U1);
+ verify(testServicesCache, times(1)).parseServiceInfo(eq(mResolveInfo2), eq(2000L));
+ }
+
private static RegisteredServicesCache.ServiceInfo<TestServiceType> newServiceInfo(
TestServiceType type, int uid, ComponentName componentName, long lastUpdateTime) {
final ComponentInfo info = new ComponentInfo();
@@ -324,7 +377,7 @@ public class RegisteredServicesCacheUnitTest {
return new RegisteredServicesCache.ServiceInfo<>(type, info, componentName, lastUpdateTime);
}
- private void addServiceInfoIntoResolveInfo(ResolveInfo resolveInfo, String packageName,
+ private void addServiceInfoIntoResolveInfo(TestResolveInfo resolveInfo, String packageName,
String serviceName) {
final ServiceInfo serviceInfo = new ServiceInfo();
serviceInfo.packageName = packageName;
@@ -345,7 +398,7 @@ public class RegisteredServicesCacheUnitTest {
static final String SERVICE_INTERFACE = "RegisteredServicesCacheUnitTest";
static final String SERVICE_META_DATA = "RegisteredServicesCacheUnitTest";
static final String ATTRIBUTES_NAME = "test";
- private SparseArray<Map<ResolveInfo, ServiceInfo<TestServiceType>>> mServices =
+ private SparseArray<Map<TestResolveInfo, ServiceInfo<TestServiceType>>> mServices =
new SparseArray<>();
public TestRegisteredServicesCache(Injector<TestServiceType> injector,
@@ -362,14 +415,14 @@ public class RegisteredServicesCacheUnitTest {
@Override
protected List<ResolveInfo> queryIntentServices(int userId) {
- Map<ResolveInfo, ServiceInfo<TestServiceType>> map = mServices.get(userId,
- new HashMap<ResolveInfo, ServiceInfo<TestServiceType>>());
+ Map<TestResolveInfo, ServiceInfo<TestServiceType>> map = mServices.get(userId,
+ new HashMap<TestResolveInfo, ServiceInfo<TestServiceType>>());
return new ArrayList<>(map.keySet());
}
- void addServiceForQuerying(int userId, ResolveInfo resolveInfo,
+ void addServiceForQuerying(int userId, TestResolveInfo resolveInfo,
ServiceInfo<TestServiceType> serviceInfo) {
- Map<ResolveInfo, ServiceInfo<TestServiceType>> map = mServices.get(userId);
+ Map<TestResolveInfo, ServiceInfo<TestServiceType>> map = mServices.get(userId);
if (map == null) {
map = new HashMap<>();
mServices.put(userId, map);
@@ -377,16 +430,12 @@ public class RegisteredServicesCacheUnitTest {
map.put(resolveInfo, serviceInfo);
}
- void clearServicesForQuerying() {
- mServices.clear();
- }
-
@Override
protected ServiceInfo<TestServiceType> parseServiceInfo(ResolveInfo resolveInfo,
long lastUpdateTime) throws XmlPullParserException, IOException {
int size = mServices.size();
for (int i = 0; i < size; i++) {
- Map<ResolveInfo, ServiceInfo<TestServiceType>> map = mServices.valueAt(i);
+ Map<TestResolveInfo, ServiceInfo<TestServiceType>> map = mServices.valueAt(i);
ServiceInfo<TestServiceType> serviceInfo = map.get(resolveInfo);
if (serviceInfo != null) {
return serviceInfo;
@@ -400,4 +449,20 @@ public class RegisteredServicesCacheUnitTest {
super.onUserRemoved(userId);
}
}
+
+ /**
+ * Create different hash code with the same {@link android.content.pm.ResolveInfo} for testing.
+ */
+ public static class TestResolveInfo extends ResolveInfo {
+ int mResolveInfoId = 0;
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mResolveInfoId, serviceInfo);
+ }
+
+ public void setResolveInfoId(int resolveInfoId) {
+ mResolveInfoId = resolveInfoId;
+ }
+ }
}
diff --git a/core/tests/coretests/src/android/content/pm/UserInfoTest.java b/core/tests/coretests/src/android/content/pm/UserInfoTest.java
index edeea6d85ca6..c84c21557ea4 100644
--- a/core/tests/coretests/src/android/content/pm/UserInfoTest.java
+++ b/core/tests/coretests/src/android/content/pm/UserInfoTest.java
@@ -16,19 +16,44 @@
package android.content.pm;
+import static android.content.pm.UserInfo.FLAG_DEMO;
+import static android.content.pm.UserInfo.FLAG_FULL;
+import static android.content.pm.UserInfo.FLAG_GUEST;
+import static android.content.pm.UserInfo.FLAG_MAIN;
+import static android.content.pm.UserInfo.FLAG_PROFILE;
+import static android.content.pm.UserInfo.FLAG_SYSTEM;
+import static android.os.UserManager.USER_TYPE_FULL_RESTRICTED;
+import static android.os.UserManager.USER_TYPE_FULL_SYSTEM;
+import static android.os.UserManager.USER_TYPE_SYSTEM_HEADLESS;
+
import static com.google.common.truth.Truth.assertThat;
+import android.content.pm.UserInfo.UserInfoFlag;
import android.os.UserHandle;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
+import android.platform.test.flag.junit.SetFlagsRule;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
+import com.google.common.truth.Expect;
+
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
+
@RunWith(AndroidJUnit4.class)
@SmallTest
-public class UserInfoTest {
+public final class UserInfoTest {
+
+ @Rule
+ public final SetFlagsRule flags =
+ new SetFlagsRule(SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT);
+
+ @Rule public final Expect expect = Expect.create();
+
@Test
public void testSimple() throws Exception {
final UserInfo ui = new UserInfo(10, "Test", UserInfo.FLAG_GUEST);
@@ -56,9 +81,6 @@ public class UserInfoTest {
assertThat(ui.isInitialized()).isEqualTo(false);
assertThat(ui.isFull()).isEqualTo(false);
assertThat(ui.isMain()).isEqualTo(false);
-
- // Derived dynamically
- assertThat(ui.canHaveProfile()).isEqualTo(false);
}
@Test
@@ -68,4 +90,64 @@ public class UserInfoTest {
assertThat(ui.toString()).isNotEmpty();
assertThat(ui.toFullString()).isNotEmpty();
}
+
+ @Test
+ @DisableFlags(android.multiuser.Flags.FLAG_PROFILES_FOR_ALL)
+ public void testCanHaveProfile_flagProfilesForAllDisabled() {
+ expectCannotHaveProfile("non-full user", createTestUserInfo(/* flags= */ 0));
+ expectCannotHaveProfile("guest user", createTestUserInfo(FLAG_FULL | FLAG_GUEST));
+ expectCanHaveProfile("main user", createTestUserInfo(FLAG_FULL | FLAG_MAIN));
+ expectCannotHaveProfile("non-main user", createTestUserInfo(FLAG_FULL));
+ expectCannotHaveProfile("demo user", createTestUserInfo(FLAG_FULL | FLAG_DEMO));
+ expectCannotHaveProfile("restricted user",
+ createTestUserInfo(USER_TYPE_FULL_RESTRICTED, FLAG_FULL));
+ expectCannotHaveProfile("profile user", createTestUserInfo(FLAG_PROFILE));
+ expectCanHaveProfile("(full) system user that's also main user",
+ createTestUserInfo(USER_TYPE_FULL_SYSTEM, FLAG_FULL | FLAG_SYSTEM | FLAG_MAIN));
+ expectCannotHaveProfile("headless system user that's not main user",
+ createTestUserInfo(USER_TYPE_SYSTEM_HEADLESS, FLAG_SYSTEM));
+ }
+
+ @Test
+ @EnableFlags(android.multiuser.Flags.FLAG_PROFILES_FOR_ALL)
+ public void testCanHaveProfile_flagProfilesForAllEnabled() {
+ expectCannotHaveProfile("non-full user", createTestUserInfo(/* flags= */ 0));
+ expectCannotHaveProfile("guest user", createTestUserInfo(FLAG_FULL | FLAG_GUEST));
+ expectCanHaveProfile("main user", createTestUserInfo(FLAG_FULL | FLAG_MAIN));
+ expectCanHaveProfile("non-main user", createTestUserInfo(FLAG_FULL));
+ expectCannotHaveProfile("demo user", createTestUserInfo(FLAG_FULL | FLAG_DEMO));
+ expectCannotHaveProfile("restricted user",
+ createTestUserInfo(USER_TYPE_FULL_RESTRICTED, FLAG_FULL));
+ expectCannotHaveProfile("profile user", createTestUserInfo(FLAG_PROFILE));
+ expectCanHaveProfile("(full) system user that's also main user",
+ createTestUserInfo(USER_TYPE_FULL_SYSTEM, FLAG_FULL | FLAG_SYSTEM | FLAG_MAIN));
+ expectCannotHaveProfile("headless system user that's not main user",
+ createTestUserInfo(USER_TYPE_SYSTEM_HEADLESS, FLAG_SYSTEM));
+ }
+
+ /**
+ * Creates a new {@link UserInfo} with id {@code 10}, name {@code Test}, and the given
+ * {@code flags}.
+ */
+ private UserInfo createTestUserInfo(@UserInfoFlag int flags) {
+ return new UserInfo(10, "Test", flags);
+ }
+
+ /**
+ * Creates a new {@link UserInfo} with id {@code 10}, name {@code Test}, and the given
+ * {@code userType} and {@code flags}.
+ */
+ private UserInfo createTestUserInfo(String userType, @UserInfoFlag int flags) {
+ return new UserInfo(10, "Test", /* iconPath= */ null, flags, userType);
+ }
+
+ private void expectCanHaveProfile(String description, UserInfo user) {
+ expect.withMessage("canHaveProfile() on %s (%s)", description, user)
+ .that(user.canHaveProfile()).isTrue();
+ }
+
+ private void expectCannotHaveProfile(String description, UserInfo user) {
+ expect.withMessage("canHaveProfile() on %s (%s)", description, user)
+ .that(user.canHaveProfile()).isFalse();
+ }
}
diff --git a/core/tests/coretests/src/android/os/ParcelTest.java b/core/tests/coretests/src/android/os/ParcelTest.java
index bb059108d4b6..3e6520106ab0 100644
--- a/core/tests/coretests/src/android/os/ParcelTest.java
+++ b/core/tests/coretests/src/android/os/ParcelTest.java
@@ -29,8 +29,6 @@ import android.util.Log;
import androidx.test.ext.junit.runners.AndroidJUnit4;
-import java.nio.BufferOverflowException;
-import java.nio.ByteBuffer;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -418,63 +416,4 @@ public class ParcelTest {
int binderEndPos = pA.dataPosition();
assertTrue(pA.hasBinders(binderStartPos, binderEndPos - binderStartPos));
}
-
- private static final byte[] TEST_DATA = new byte[] {4, 8, 15, 16, 23, 42};
-
- // Allow for some Parcel overhead
- private static final int TEST_DATA_LENGTH = TEST_DATA.length + 100;
-
- @Test
- public void testMarshall_ByteBuffer_wrapped() {
- ByteBuffer bb = ByteBuffer.allocate(TEST_DATA_LENGTH);
- testMarshall_ByteBuffer(bb);
- }
-
- @Test
- public void testMarshall_DirectByteBuffer() {
- ByteBuffer bb = ByteBuffer.allocateDirect(TEST_DATA_LENGTH);
- testMarshall_ByteBuffer(bb);
- }
-
- private void testMarshall_ByteBuffer(ByteBuffer bb) {
- // Ensure that Parcel respects the starting offset by not starting at 0
- bb.position(1);
- bb.mark();
-
- // Parcel test data, then marshall into the ByteBuffer
- Parcel p1 = Parcel.obtain();
- p1.writeByteArray(TEST_DATA);
- p1.marshall(bb);
- p1.recycle();
-
- assertTrue(bb.position() > 1);
- bb.reset();
-
- // Unmarshall test data into a new Parcel
- Parcel p2 = Parcel.obtain();
- bb.reset();
- p2.unmarshall(bb);
- assertTrue(bb.position() > 1);
- p2.setDataPosition(0);
- byte[] marshalled = p2.marshall();
-
- bb.reset();
- for (int i = 0; i < TEST_DATA.length; i++) {
- assertEquals(bb.get(), marshalled[i]);
- }
-
- byte[] testDataCopy = new byte[TEST_DATA.length];
- p2.setDataPosition(0);
- p2.readByteArray(testDataCopy);
- for (int i = 0; i < TEST_DATA.length; i++) {
- assertEquals(TEST_DATA[i], testDataCopy[i]);
- }
-
- // Test that overflowing the buffer throws an exception
- bb.reset();
- // Leave certainly not enough room for the test data
- bb.limit(bb.position() + TEST_DATA.length - 1);
- assertThrows(BufferOverflowException.class, () -> p2.marshall(bb));
- p2.recycle();
- }
}
diff --git a/core/tests/coretests/src/android/security/advancedprotection/AdvancedProtectionManagerTest.java b/core/tests/coretests/src/android/security/advancedprotection/AdvancedProtectionManagerTest.java
index 45864b01c795..e06ff98b0384 100644
--- a/core/tests/coretests/src/android/security/advancedprotection/AdvancedProtectionManagerTest.java
+++ b/core/tests/coretests/src/android/security/advancedprotection/AdvancedProtectionManagerTest.java
@@ -16,10 +16,14 @@
package android.security.advancedprotection;
+import static android.os.UserManager.DISALLOW_CELLULAR_2G;
+import static android.os.UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES_GLOBALLY;
import static android.security.advancedprotection.AdvancedProtectionManager.ACTION_SHOW_ADVANCED_PROTECTION_SUPPORT_DIALOG;
import static android.security.advancedprotection.AdvancedProtectionManager.EXTRA_SUPPORT_DIALOG_FEATURE;
import static android.security.advancedprotection.AdvancedProtectionManager.EXTRA_SUPPORT_DIALOG_TYPE;
import static android.security.advancedprotection.AdvancedProtectionManager.FEATURE_ID_DISALLOW_CELLULAR_2G;
+import static android.security.advancedprotection.AdvancedProtectionManager.FEATURE_ID_DISALLOW_INSTALL_UNKNOWN_SOURCES;
+import static android.security.advancedprotection.AdvancedProtectionManager.FEATURE_ID_ENABLE_MTE;
import static android.security.advancedprotection.AdvancedProtectionManager.SUPPORT_DIALOG_TYPE_BLOCKED_INTERACTION;
import static android.security.advancedprotection.AdvancedProtectionManager.SUPPORT_DIALOG_TYPE_DISABLED_SETTING;
import static android.security.advancedprotection.AdvancedProtectionManager.SUPPORT_DIALOG_TYPE_UNKNOWN;
@@ -37,6 +41,9 @@ import org.junit.runners.JUnit4;
public class AdvancedProtectionManagerTest {
private static final int FEATURE_ID_INVALID = -1;
private static final int SUPPORT_DIALOG_TYPE_INVALID = -1;
+ //TODO(b/378931989): Switch to android.app.admin.DevicePolicyIdentifiers.MEMORY_TAGGING_POLICY
+ //when the appropriate flag is launched.
+ private static final String MEMORY_TAGGING_POLICY = "memoryTagging";
@Test
public void testCreateSupportIntent_validFeature_validTypeUnknown_createsIntent() {
@@ -94,4 +101,44 @@ public class AdvancedProtectionManagerTest {
AdvancedProtectionManager.createSupportIntent(FEATURE_ID_INVALID,
SUPPORT_DIALOG_TYPE_INVALID));
}
+
+ @Test
+ public void testCreateSupportIntentForPolicy_2g_typeUnknown_createsIntentForDisabledSetting() {
+ Intent intent = AdvancedProtectionManager
+ .createSupportIntentForPolicyIdentifierOrRestriction(
+ DISALLOW_CELLULAR_2G, SUPPORT_DIALOG_TYPE_UNKNOWN);
+
+ assertEquals(ACTION_SHOW_ADVANCED_PROTECTION_SUPPORT_DIALOG, intent.getAction());
+ assertEquals(FEATURE_ID_DISALLOW_CELLULAR_2G, intent.getIntExtra(
+ EXTRA_SUPPORT_DIALOG_FEATURE, FEATURE_ID_INVALID));
+ assertEquals(SUPPORT_DIALOG_TYPE_DISABLED_SETTING, intent.getIntExtra(
+ EXTRA_SUPPORT_DIALOG_TYPE, SUPPORT_DIALOG_TYPE_INVALID));
+ }
+
+ @Test
+ public void testCreateSupportIntentForPolicy_mte_typeUnknown_createsIntentForDisabledSetting() {
+ Intent intent = AdvancedProtectionManager
+ .createSupportIntentForPolicyIdentifierOrRestriction(
+ MEMORY_TAGGING_POLICY, SUPPORT_DIALOG_TYPE_UNKNOWN);
+
+ assertEquals(ACTION_SHOW_ADVANCED_PROTECTION_SUPPORT_DIALOG, intent.getAction());
+ assertEquals(FEATURE_ID_ENABLE_MTE, intent.getIntExtra(
+ EXTRA_SUPPORT_DIALOG_FEATURE, FEATURE_ID_INVALID));
+ assertEquals(SUPPORT_DIALOG_TYPE_DISABLED_SETTING, intent.getIntExtra(
+ EXTRA_SUPPORT_DIALOG_TYPE, SUPPORT_DIALOG_TYPE_INVALID));
+ }
+
+ @Test
+ public void
+ testCreateSupportIntentForPolicy_unknownSources_typeUnknown_createsIntentForUnknown() {
+ Intent intent = AdvancedProtectionManager
+ .createSupportIntentForPolicyIdentifierOrRestriction(
+ DISALLOW_INSTALL_UNKNOWN_SOURCES_GLOBALLY, SUPPORT_DIALOG_TYPE_UNKNOWN);
+
+ assertEquals(ACTION_SHOW_ADVANCED_PROTECTION_SUPPORT_DIALOG, intent.getAction());
+ assertEquals(FEATURE_ID_DISALLOW_INSTALL_UNKNOWN_SOURCES, intent.getIntExtra(
+ EXTRA_SUPPORT_DIALOG_FEATURE, FEATURE_ID_INVALID));
+ assertEquals(SUPPORT_DIALOG_TYPE_UNKNOWN, intent.getIntExtra(
+ EXTRA_SUPPORT_DIALOG_TYPE, SUPPORT_DIALOG_TYPE_INVALID));
+ }
}
diff --git a/core/tests/coretests/src/android/window/BackTouchTrackerTest.kt b/core/tests/coretests/src/android/window/BackTouchTrackerTest.kt
index ad68e385459e..381b566018c7 100644
--- a/core/tests/coretests/src/android/window/BackTouchTrackerTest.kt
+++ b/core/tests/coretests/src/android/window/BackTouchTrackerTest.kt
@@ -37,7 +37,7 @@ class BackTouchTrackerTest {
fun generatesProgress_onStart() {
val linearTracker = linearTouchTracker()
linearTracker.setGestureStartLocation(INITIAL_X_LEFT_EDGE, 0f, BackEvent.EDGE_LEFT)
- val event = linearTracker.createStartEvent()
+ val event = linearTracker.createStartEvent(null)
assertEquals(0f, event.progress, 0f)
}
diff --git a/core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java b/core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java
index 66524d1c1d2a..215c1623a530 100644
--- a/core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java
+++ b/core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java
@@ -695,7 +695,8 @@ public class WindowOnBackInvokedDispatcherTest {
/* frameTimeMillis = */ 0,
/* progress = */ progress,
/* triggerBack = */ false,
- /* swipeEdge = */ BackEvent.EDGE_LEFT);
+ /* swipeEdge = */ BackEvent.EDGE_LEFT,
+ /* departingAnimationTarget = */ null);
}
private void verifyImeCallackRegistrations() throws RemoteException {
diff --git a/core/tests/coretests/src/com/android/internal/app/MediaRouteDialogPresenterTest.kt b/core/tests/coretests/src/com/android/internal/app/MediaRouteDialogPresenterTest.kt
new file mode 100644
index 000000000000..e80d3a6e625f
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/app/MediaRouteDialogPresenterTest.kt
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2025 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.internal.app
+
+import android.content.Context
+import android.media.MediaRouter
+import android.testing.TestableLooper.RunWithLooper
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.kotlin.doReturn
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.stub
+
+@SmallTest
+@RunWithLooper(setAsMainLooper = true)
+@RunWith(AndroidJUnit4::class)
+class MediaRouteDialogPresenterTest {
+ private var selectedRoute: MediaRouter.RouteInfo = mock()
+ private var mediaRouter: MediaRouter = mock<MediaRouter> {
+ on { selectedRoute } doReturn selectedRoute
+ }
+ private var context: Context = mock<Context> {
+ on { getSystemServiceName(MediaRouter::class.java) } doReturn Context.MEDIA_ROUTER_SERVICE
+ on { getSystemService(MediaRouter::class.java) } doReturn mediaRouter
+ }
+
+ @Test
+ fun shouldShowChooserDialog_routeNotDefault_returnsFalse() {
+ selectedRoute.stub {
+ on { isDefault } doReturn false
+ on { matchesTypes(anyInt()) } doReturn true
+ }
+
+ assertThat(MediaRouteDialogPresenter.shouldShowChooserDialog(
+ context, MediaRouter.ROUTE_TYPE_REMOTE_DISPLAY))
+ .isEqualTo(false)
+ }
+
+ @Test
+ fun shouldShowChooserDialog_routeDefault_returnsTrue() {
+ selectedRoute.stub {
+ on { isDefault } doReturn true
+ on { matchesTypes(anyInt()) } doReturn true
+ }
+
+ assertThat(MediaRouteDialogPresenter.shouldShowChooserDialog(
+ context, MediaRouter.ROUTE_TYPE_REMOTE_DISPLAY))
+ .isEqualTo(true)
+ }
+
+ @Test
+ fun shouldShowChooserDialog_routeNotMatch_returnsTrue() {
+ selectedRoute.stub {
+ on { isDefault } doReturn false
+ on { matchesTypes(anyInt()) } doReturn false
+ }
+
+ assertThat(MediaRouteDialogPresenter.shouldShowChooserDialog(
+ context, MediaRouter.ROUTE_TYPE_REMOTE_DISPLAY))
+ .isEqualTo(true)
+ }
+
+ @Test
+ fun shouldShowChooserDialog_routeDefaultAndNotMatch_returnsTrue() {
+ selectedRoute.stub {
+ on { isDefault } doReturn true
+ on { matchesTypes(anyInt()) } doReturn false
+ }
+
+ assertThat(MediaRouteDialogPresenter.shouldShowChooserDialog(
+ context, MediaRouter.ROUTE_TYPE_REMOTE_DISPLAY))
+ .isEqualTo(true)
+ }
+} \ No newline at end of file
diff --git a/core/tests/coretests/src/com/android/internal/statusbar/DisableStatesTest.java b/core/tests/coretests/src/com/android/internal/statusbar/DisableStatesTest.java
new file mode 100644
index 000000000000..5b82696b81c3
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/statusbar/DisableStatesTest.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2025 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.internal.statusbar;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+import android.os.Parcel;
+import android.util.Pair;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.HashMap;
+import java.util.Map;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class DisableStatesTest {
+
+ @Test
+ public void testParcelable() {
+ Map<Integer, Pair<Integer, Integer>> displaysWithStates = new HashMap<>();
+ displaysWithStates.put(1, new Pair<>(10, 20));
+ displaysWithStates.put(2, new Pair<>(30, 40));
+ boolean animate = true;
+ DisableStates original = new DisableStates(displaysWithStates, animate);
+
+ Parcel parcel = Parcel.obtain();
+ original.writeToParcel(parcel, 0);
+ parcel.setDataPosition(0);
+ DisableStates restored = DisableStates.CREATOR.createFromParcel(parcel);
+
+ assertNotNull(restored);
+ assertEquals(original.displaysWithStates.size(), restored.displaysWithStates.size());
+ for (Map.Entry<Integer, Pair<Integer, Integer>> entry :
+ original.displaysWithStates.entrySet()) {
+ int displayId = entry.getKey();
+ Pair<Integer, Integer> originalDisplayStates = entry.getValue();
+ Pair<Integer, Integer> restoredDisplayStates = restored.displaysWithStates.get(
+ displayId);
+ assertEquals(originalDisplayStates.first, restoredDisplayStates.first);
+ assertEquals(originalDisplayStates.second, restoredDisplayStates.second);
+ }
+ assertEquals(original.animate, restored.animate);
+ }
+}
diff --git a/data/etc/Android.bp b/data/etc/Android.bp
index 8f85617acae3..98278f4529ff 100644
--- a/data/etc/Android.bp
+++ b/data/etc/Android.bp
@@ -184,6 +184,14 @@ prebuilt_etc {
}
prebuilt_etc {
+ name: "privapp_whitelist_com.android.statementservice",
+ system_ext_specific: true,
+ sub_dir: "permissions",
+ src: "com.android.statementservice.xml",
+ filename_from_src: true,
+}
+
+prebuilt_etc {
name: "privapp_whitelist_com.android.settings.intelligence",
product_specific: true,
sub_dir: "permissions",
diff --git a/data/etc/com.android.statementservice.xml b/data/etc/com.android.statementservice.xml
new file mode 100644
index 000000000000..e102af206395
--- /dev/null
+++ b/data/etc/com.android.statementservice.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2018 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
+ -->
+<permissions>
+ <privapp-permissions package="com.android.statementservice">
+ <permission name="android.permission.INTENT_FILTER_VERIFICATION_AGENT"/>
+ <permission name="android.permission.DOMAIN_VERIFICATION_AGENT"/>
+ <permission name="android.permission.INTERACT_ACROSS_USERS"/>
+ </privapp-permissions>
+</permissions>
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 9234902335c1..1dd0465f691e 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -623,12 +623,6 @@ applications that come with the platform
<permission name="android.permission.ACCESS_TEXT_CLASSIFIER_BY_TYPE"/>
</privapp-permissions>
- <privapp-permissions package="com.android.statementservice">
- <permission name="android.permission.INTENT_FILTER_VERIFICATION_AGENT"/>
- <permission name="android.permission.DOMAIN_VERIFICATION_AGENT"/>
- <permission name="android.permission.INTERACT_ACROSS_USERS"/>
- </privapp-permissions>
-
<privapp-permissions package="com.android.soundpicker">
<permission name="android.permission.INTERACT_ACROSS_USERS" />
</privapp-permissions>
diff --git a/graphics/java/android/graphics/Typeface.java b/graphics/java/android/graphics/Typeface.java
index d1aca34c7b8d..39cd4a89aae6 100644
--- a/graphics/java/android/graphics/Typeface.java
+++ b/graphics/java/android/graphics/Typeface.java
@@ -80,6 +80,7 @@ import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
+import java.util.function.BiConsumer;
/**
* The Typeface class specifies the typeface and intrinsic style of a font.
@@ -1550,14 +1551,21 @@ public class Typeface {
setDefault(defaults.get(0));
ArrayList<Typeface> oldGenerics = new ArrayList<>();
- oldGenerics.add(sSystemFontMap.get("sans-serif"));
- sSystemFontMap.put("sans-serif", genericFamilies.get(0));
+ BiConsumer<Typeface, String> swapTypeface = (typeface, key) -> {
+ oldGenerics.add(sSystemFontMap.get(key));
+ sSystemFontMap.put(key, typeface);
+ };
- oldGenerics.add(sSystemFontMap.get("serif"));
- sSystemFontMap.put("serif", genericFamilies.get(1));
+ Typeface sansSerif = genericFamilies.get(0);
+ swapTypeface.accept(sansSerif, "sans-serif");
+ swapTypeface.accept(Typeface.create(sansSerif, 100, false), "sans-serif-thin");
+ swapTypeface.accept(Typeface.create(sansSerif, 300, false), "sans-serif-light");
+ swapTypeface.accept(Typeface.create(sansSerif, 500, false), "sans-serif-medium");
+ swapTypeface.accept(Typeface.create(sansSerif, 700, false), "sans-serif-bold");
+ swapTypeface.accept(Typeface.create(sansSerif, 900, false), "sans-serif-black");
- oldGenerics.add(sSystemFontMap.get("monospace"));
- sSystemFontMap.put("monospace", genericFamilies.get(2));
+ swapTypeface.accept(genericFamilies.get(1), "serif");
+ swapTypeface.accept(genericFamilies.get(2), "monospace");
return new Pair<>(oldDefaults, oldGenerics);
}
diff --git a/libs/WindowManager/Shell/Android.bp b/libs/WindowManager/Shell/Android.bp
index bcb6c4f555f7..033c934056d6 100644
--- a/libs/WindowManager/Shell/Android.bp
+++ b/libs/WindowManager/Shell/Android.bp
@@ -26,9 +26,11 @@ package {
java_library {
name: "wm_shell_protolog-groups",
srcs: [
- ":protolog-common-src",
"src/com/android/wm/shell/protolog/ShellProtoLogGroup.java",
],
+ static_libs: [
+ "protolog-common-lib",
+ ],
}
filegroup {
@@ -159,12 +161,12 @@ java_library {
android_library {
name: "WindowManager-Shell",
srcs: [
- ":wm_shell_protolog_src",
// TODO(b/168581922) protologtool do not support kotlin(*.kt)
- "src/com/android/wm/shell/EventLogTags.logtags",
":wm_shell-aidls",
":wm_shell-shared-aidls",
":wm_shell-sources-kt",
+ ":wm_shell_protolog_src",
+ "src/com/android/wm/shell/EventLogTags.logtags",
],
resource_dirs: [
"res",
diff --git a/libs/WindowManager/Shell/aconfig/multitasking.aconfig b/libs/WindowManager/Shell/aconfig/multitasking.aconfig
index b6a1501831c0..19455a313a9d 100644
--- a/libs/WindowManager/Shell/aconfig/multitasking.aconfig
+++ b/libs/WindowManager/Shell/aconfig/multitasking.aconfig
@@ -211,3 +211,10 @@ flag {
description: "Makes the split divider snap 'magnetically' to available snap points during drag"
bug: "383631946"
}
+
+flag {
+ name: "enable_dynamic_insets_for_app_launch"
+ namespace: "multitasking"
+ description: "Enables dynamic insets for app launch so the window is properly cropped"
+ bug: "336511494"
+}
diff --git a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerViewTest.kt b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerViewTest.kt
index 14c15210252a..90011f4018f6 100644
--- a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerViewTest.kt
+++ b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerViewTest.kt
@@ -24,6 +24,9 @@ import android.graphics.PointF
import android.graphics.Rect
import android.os.Handler
import android.os.UserManager
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
+import android.platform.test.flag.junit.SetFlagsRule
import android.view.IWindowManager
import android.view.MotionEvent
import android.view.View
@@ -36,6 +39,7 @@ import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation
import com.android.internal.logging.testing.UiEventLoggerFake
import com.android.internal.protolog.ProtoLog
import com.android.internal.statusbar.IStatusBarService
+import com.android.wm.shell.Flags
import com.android.wm.shell.R
import com.android.wm.shell.ShellTaskOrganizer
import com.android.wm.shell.bubbles.Bubble
@@ -64,6 +68,10 @@ import com.android.wm.shell.shared.TransactionPool
import com.android.wm.shell.shared.animation.PhysicsAnimatorTestUtils
import com.android.wm.shell.shared.bubbles.BubbleBarLocation
import com.android.wm.shell.shared.bubbles.DeviceConfig
+import com.android.wm.shell.shared.bubbles.DragZone
+import com.android.wm.shell.shared.bubbles.DragZoneFactory
+import com.android.wm.shell.shared.bubbles.DragZoneFactory.SplitScreenModeChecker.SplitScreenMode
+import com.android.wm.shell.shared.bubbles.DraggedObject
import com.android.wm.shell.sysui.ShellCommandHandler
import com.android.wm.shell.sysui.ShellController
import com.android.wm.shell.sysui.ShellInit
@@ -88,6 +96,8 @@ class BubbleBarLayerViewTest {
const val SCREEN_HEIGHT = 1000
}
+ @get:Rule val setFlagsRule = SetFlagsRule()
+
@get:Rule val animatorTestRule: AnimatorTestRule = AnimatorTestRule(this)
private val context = ApplicationProvider.getApplicationContext<Context>()
@@ -101,6 +111,7 @@ class BubbleBarLayerViewTest {
private lateinit var bgExecutor: TestShellExecutor
private lateinit var bubbleLogger: BubbleLogger
private lateinit var testBubblesList: MutableList<Bubble>
+ private lateinit var dragZoneFactory: DragZoneFactory
@Before
fun setUp() {
@@ -134,6 +145,10 @@ class BubbleBarLayerViewTest {
whenever(bubbleData.bubbles).thenReturn(testBubblesList)
whenever(bubbleData.hasBubbles()).thenReturn(!testBubblesList.isEmpty())
+ dragZoneFactory = DragZoneFactory(context, deviceConfig,
+ { SplitScreenMode.UNSUPPORTED },
+ { false })
+
bubbleController =
createBubbleController(
bubbleData,
@@ -280,6 +295,7 @@ class BubbleBarLayerViewTest {
assertThat(uiEventLoggerFake.logs[0]).hasBubbleInfo(bubble)
}
+ @DisableFlags(Flags.FLAG_ENABLE_BUBBLE_TO_FULLSCREEN, Flags.FLAG_ENABLE_BUBBLE_ANYTHING)
@Test
fun testEventLogging_dragExpandedViewLeft() {
val bubble = createBubble("first")
@@ -287,7 +303,7 @@ class BubbleBarLayerViewTest {
getInstrumentation().runOnMainSync {
bubbleBarLayerView.showExpandedView(bubble)
- bubble.bubbleBarExpandedView!!.onContentVisibilityChanged(true)
+ bubble.bubbleBarExpandedView!!.onContentVisibilityChanged(true /* visible */)
}
waitForExpandedViewAnimation()
@@ -305,6 +321,7 @@ class BubbleBarLayerViewTest {
assertThat(uiEventLoggerFake.logs[0]).hasBubbleInfo(bubble)
}
+ @DisableFlags(Flags.FLAG_ENABLE_BUBBLE_TO_FULLSCREEN, Flags.FLAG_ENABLE_BUBBLE_ANYTHING)
@Test
fun testEventLogging_dragExpandedViewRight() {
val bubble = createBubble("first")
@@ -312,7 +329,7 @@ class BubbleBarLayerViewTest {
getInstrumentation().runOnMainSync {
bubbleBarLayerView.showExpandedView(bubble)
- bubble.bubbleBarExpandedView!!.onContentVisibilityChanged(true)
+ bubble.bubbleBarExpandedView!!.onContentVisibilityChanged(true /* visible */)
}
waitForExpandedViewAnimation()
@@ -330,6 +347,76 @@ class BubbleBarLayerViewTest {
assertThat(uiEventLoggerFake.logs[0]).hasBubbleInfo(bubble)
}
+ @EnableFlags(Flags.FLAG_ENABLE_BUBBLE_TO_FULLSCREEN, Flags.FLAG_ENABLE_CREATE_ANY_BUBBLE)
+ @Test
+ fun testEventLogging_dragExpandedViewLeft_bubbleAnything() {
+ val bubble = createBubble("first")
+ bubblePositioner.bubbleBarLocation = BubbleBarLocation.RIGHT
+
+ getInstrumentation().runOnMainSync {
+ bubbleBarLayerView.showExpandedView(bubble)
+ bubble.bubbleBarExpandedView!!.onContentVisibilityChanged(true /* visible */)
+ }
+ waitForExpandedViewAnimation()
+
+ val handleView = bubbleBarLayerView.findViewById<View>(R.id.bubble_bar_handle_view)
+ assertThat(handleView).isNotNull()
+
+ val dragZones = dragZoneFactory.createSortedDragZones(
+ DraggedObject.ExpandedView(BubbleBarLocation.RIGHT))
+ val rightDragZone = dragZones.filterIsInstance<DragZone.Bubble.Right>().first()
+ val rightPoint = PointF(rightDragZone.bounds.centerX().toFloat(),
+ rightDragZone.bounds.centerY().toFloat())
+ val leftDragZone = dragZones.filterIsInstance<DragZone.Bubble.Left>().first()
+ val leftPoint = PointF(leftDragZone.bounds.centerX().toFloat(),
+ leftDragZone.bounds.centerY().toFloat())
+
+ // Drag from right to left
+ handleView.dispatchTouchEvent(0L, MotionEvent.ACTION_DOWN, rightPoint)
+ handleView.dispatchTouchEvent(10L, MotionEvent.ACTION_MOVE, leftPoint)
+ handleView.dispatchTouchEvent(20L, MotionEvent.ACTION_UP, leftPoint)
+
+ assertThat(uiEventLoggerFake.numLogs()).isEqualTo(1)
+ assertThat(uiEventLoggerFake.logs[0].eventId)
+ .isEqualTo(BubbleLogger.Event.BUBBLE_BAR_MOVED_LEFT_DRAG_EXP_VIEW.id)
+ assertThat(uiEventLoggerFake.logs[0]).hasBubbleInfo(bubble)
+ }
+
+ @EnableFlags(Flags.FLAG_ENABLE_BUBBLE_TO_FULLSCREEN, Flags.FLAG_ENABLE_CREATE_ANY_BUBBLE)
+ @Test
+ fun testEventLogging_dragExpandedViewRight_bubbleAnything() {
+ val bubble = createBubble("first")
+ bubblePositioner.bubbleBarLocation = BubbleBarLocation.LEFT
+
+ getInstrumentation().runOnMainSync {
+ bubbleBarLayerView.showExpandedView(bubble)
+ bubble.bubbleBarExpandedView!!.onContentVisibilityChanged(true /* visible */)
+ }
+ waitForExpandedViewAnimation()
+
+ val handleView = bubbleBarLayerView.findViewById<View>(R.id.bubble_bar_handle_view)
+ assertThat(handleView).isNotNull()
+
+ val dragZones = dragZoneFactory.createSortedDragZones(
+ DraggedObject.ExpandedView(BubbleBarLocation.LEFT))
+ val rightDragZone = dragZones.filterIsInstance<DragZone.Bubble.Right>().first()
+ val rightPoint = PointF(rightDragZone.bounds.centerX().toFloat(),
+ rightDragZone.bounds.centerY().toFloat())
+ val leftDragZone = dragZones.filterIsInstance<DragZone.Bubble.Left>().first()
+ val leftPoint = PointF(leftDragZone.bounds.centerX().toFloat(),
+ leftDragZone.bounds.centerY().toFloat())
+
+ // Drag from left to right
+ handleView.dispatchTouchEvent(0L, MotionEvent.ACTION_DOWN, leftPoint)
+ handleView.dispatchTouchEvent(10L, MotionEvent.ACTION_MOVE, rightPoint)
+ handleView.dispatchTouchEvent(20L, MotionEvent.ACTION_UP, rightPoint)
+
+ assertThat(uiEventLoggerFake.numLogs()).isEqualTo(1)
+ assertThat(uiEventLoggerFake.logs[0].eventId)
+ .isEqualTo(BubbleLogger.Event.BUBBLE_BAR_MOVED_RIGHT_DRAG_EXP_VIEW.id)
+ assertThat(uiEventLoggerFake.logs[0]).hasBubbleInfo(bubble)
+ }
+
@Test
fun testUpdateExpandedView_updateLocation() {
bubblePositioner.bubbleBarLocation = BubbleBarLocation.RIGHT
@@ -385,7 +472,7 @@ class BubbleBarLayerViewTest {
bubbleLogger,
)
// Mark visible so we don't wait for task view before animations can start
- bubbleBarExpandedView.onContentVisibilityChanged(true)
+ bubbleBarExpandedView.onContentVisibilityChanged(true /* visible */)
val viewInfo = FakeBubbleFactory.createViewInfo(bubbleBarExpandedView)
return FakeBubbleFactory.createChatBubble(context, key, viewInfo).also {
diff --git a/libs/WindowManager/Shell/res/drawable/desktop_mode_ic_handle_menu_restart.xml b/libs/WindowManager/Shell/res/drawable/desktop_mode_ic_handle_menu_restart.xml
new file mode 100644
index 000000000000..d407884d3fcf
--- /dev/null
+++ b/libs/WindowManager/Shell/res/drawable/desktop_mode_ic_handle_menu_restart.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2025 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.
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="20dp"
+ android:height="20dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:fillColor="#1C1C14"
+ android:pathData="M6,13c0,-1.65 0.67,-3.15 1.76,-4.24L6.34,7.34C4.9,8.79 4,10.79 4,13c0,4.08 3.05,7.44 7,7.93v-2.02C8.17,18.43 6,15.97 6,13z"/>
+ <path
+ android:fillColor="#1C1C14"
+ android:pathData="M20,13c0,-4.42 -3.58,-8 -8,-8c-0.06,0 -0.12,0.01 -0.18,0.01v0l1.09,-1.09L11.5,2.5L8,6l3.5,3.5l1.41,-1.41l-1.08,-1.08C11.89,7.01 11.95,7 12,7c3.31,0 6,2.69 6,6c0,2.97 -2.17,5.43 -5,5.91v2.02C16.95,20.44 20,17.08 20,13z"/>
+</vector>
diff --git a/libs/WindowManager/Shell/res/drawable/desktop_windowing_transition_background.xml b/libs/WindowManager/Shell/res/drawable/desktop_windowing_transition_background.xml
index dd1a1b1dca13..75ec2ab9f6f9 100644
--- a/libs/WindowManager/Shell/res/drawable/desktop_windowing_transition_background.xml
+++ b/libs/WindowManager/Shell/res/drawable/desktop_windowing_transition_background.xml
@@ -18,15 +18,15 @@
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
<item android:id="@+id/indicator_solid">
<shape android:shape="rectangle">
- <solid android:color="@androidprv:color/materialColorPrimaryContainer" />
+ <solid android:color="@androidprv:color/materialColorPrimaryFixed" />
<corners android:radius="28dp" />
</shape>
</item>
<item android:id="@+id/indicator_stroke">
<shape android:shape="rectangle">
<corners android:radius="28dp" />
- <stroke android:width="1dp"
- android:color="@androidprv:color/materialColorPrimaryContainer"/>
+ <stroke android:width="2dp"
+ android:color="@androidprv:color/materialColorPrimaryFixed"/>
</shape>
</item>
</layer-list>
diff --git a/libs/WindowManager/Shell/res/layout/bubble_bar_manage_education.xml b/libs/WindowManager/Shell/res/layout/bubble_bar_manage_education.xml
index 9bb51a87c08f..ef30d8965452 100644
--- a/libs/WindowManager/Shell/res/layout/bubble_bar_manage_education.xml
+++ b/libs/WindowManager/Shell/res/layout/bubble_bar_manage_education.xml
@@ -34,6 +34,7 @@
android:src="@drawable/bubble_ic_settings"/>
<TextView
+ android:id="@+id/education_manage_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/bubble_popup_text_margin"
@@ -45,6 +46,7 @@
android:text="@string/bubble_bar_education_manage_title"/>
<TextView
+ android:id="@+id/education_manage_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/bubble_popup_text_margin"
diff --git a/libs/WindowManager/Shell/res/layout/bubble_bar_stack_education.xml b/libs/WindowManager/Shell/res/layout/bubble_bar_stack_education.xml
index 1616707954f5..9076d6a87678 100644
--- a/libs/WindowManager/Shell/res/layout/bubble_bar_stack_education.xml
+++ b/libs/WindowManager/Shell/res/layout/bubble_bar_stack_education.xml
@@ -34,6 +34,7 @@
android:src="@drawable/ic_floating_landscape"/>
<TextView
+ android:id="@+id/education_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/bubble_popup_text_margin"
@@ -45,6 +46,7 @@
android:text="@string/bubble_bar_education_stack_title"/>
<TextView
+ android:id="@+id/education_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/bubble_popup_text_margin"
diff --git a/libs/WindowManager/Shell/res/layout/bubble_manage_menu.xml b/libs/WindowManager/Shell/res/layout/bubble_manage_menu.xml
index 225303b2d942..17ebac95e1dd 100644
--- a/libs/WindowManager/Shell/res/layout/bubble_manage_menu.xml
+++ b/libs/WindowManager/Shell/res/layout/bubble_manage_menu.xml
@@ -40,6 +40,7 @@
android:tint="@color/bubbles_icon_tint"/>
<TextView
+ android:id="@+id/manage_dismiss"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
@@ -67,6 +68,7 @@
android:tint="@color/bubbles_icon_tint"/>
<TextView
+ android:id="@+id/manage_dont_bubble"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
diff --git a/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu.xml b/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu.xml
index bfaa40771894..30acf1ac6eda 100644
--- a/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu.xml
+++ b/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu.xml
@@ -163,6 +163,13 @@
android:text="@string/change_aspect_ratio_text"
android:src="@drawable/desktop_mode_ic_handle_menu_change_aspect_ratio"
style="@style/DesktopModeHandleMenuActionButton"/>
+
+ <com.android.wm.shell.windowdecor.HandleMenuActionButton
+ android:id="@+id/handle_menu_restart_button"
+ android:contentDescription="@string/handle_menu_restart_text"
+ android:text="@string/handle_menu_restart_text"
+ android:src="@drawable/desktop_mode_ic_handle_menu_restart"
+ style="@style/DesktopModeHandleMenuActionButton"/>
</LinearLayout>
<LinearLayout
@@ -186,14 +193,13 @@
<ImageButton
android:id="@+id/open_by_default_button"
- android:layout_width="20dp"
- android:layout_height="20dp"
android:layout_gravity="end|center_vertical"
- android:layout_marginStart="8dp"
- android:layout_marginEnd="16dp"
+ android:paddingStart="12dp"
+ android:paddingEnd="16dp"
android:contentDescription="@string/open_by_default_settings_text"
android:src="@drawable/desktop_mode_ic_handle_menu_open_by_default_settings"
- android:tint="@androidprv:color/materialColorOnSurface"/>
+ android:tint="@androidprv:color/materialColorOnSurface"
+ style="@style/DesktopModeHandleMenuWindowingButton"/>
</LinearLayout>
</LinearLayout>
diff --git a/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu_action_button.xml b/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu_action_button.xml
index 35e7de0e7c1e..0e5843f3e592 100644
--- a/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu_action_button.xml
+++ b/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu_action_button.xml
@@ -14,18 +14,7 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:app="http://schemas.android.com/apk/res-auto"
- xmlns:tools="http://schemas.android.com/tools"
- android:id="@+id/action_button"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:gravity="start|center_vertical"
- android:paddingHorizontal="16dp"
- android:importantForAccessibility="yes"
- android:orientation="horizontal"
- android:background="?android:attr/selectableItemBackground">
-
+<merge xmlns:android="http://schemas.android.com/apk/res/android">
<ImageView
android:id="@+id/image"
android:importantForAccessibility="no"
@@ -35,4 +24,4 @@
android:id="@+id/label"
android:importantForAccessibility="no"
style="@style/DesktopModeHandleMenuActionButtonTextView"/>
-</LinearLayout>
+</merge>
diff --git a/libs/WindowManager/Shell/res/values-af/strings.xml b/libs/WindowManager/Shell/res/values-af/strings.xml
index 1491c70023e7..f42fea6a0d17 100644
--- a/libs/WindowManager/Shell/res/values-af/strings.xml
+++ b/libs/WindowManager/Shell/res/values-af/strings.xml
@@ -100,8 +100,7 @@
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Nie opgelos nie?\nTik om terug te stel"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Geen kamerakwessies nie? Tik om toe te maak."</string>
<string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Die appkieslys kan hier gevind word"</string>
- <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
- <skip />
+ <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"Maak werkskermvensters oop om verskeie apps terselfdertyd oop te maak"</string>
<string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Keer enige tyd terug na volskerm vanaf die appkieslys"</string>
<string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Sien en doen meer"</string>
<string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Sleep ’n ander app in vir verdeelde skerm"</string>
@@ -122,8 +121,7 @@
<string name="handle_text" msgid="4419667835599523257">"Apphandvatsel"</string>
<string name="app_icon_text" msgid="2823268023931811747">"Appikoon"</string>
<string name="fullscreen_text" msgid="1162316685217676079">"Volskerm"</string>
- <!-- no translation found for desktop_text (9058641752519570266) -->
- <skip />
+ <string name="desktop_text" msgid="9058641752519570266">"Werkskermvensters"</string>
<string name="split_screen_text" msgid="1396336058129570886">"Verdeelde skerm"</string>
<string name="more_button_text" msgid="3655388105592893530">"Meer"</string>
<string name="float_button_text" msgid="9221657008391364581">"Sweef"</string>
@@ -134,10 +132,11 @@
<string name="new_window_text" msgid="6318648868380652280">"Nuwe venster"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"Bestuur vensters"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Verander aspekverhouding"</string>
+ <!-- no translation found for handle_menu_restart_text (3907767216238298098) -->
+ <skip />
<string name="close_text" msgid="4986518933445178928">"Maak toe"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Maak kieslys toe"</string>
- <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
- <skip />
+ <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (werkskermvensters)"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maksimeer skerm"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Verander grootte"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"App kan nie hierheen geskuif word nie"</string>
diff --git a/libs/WindowManager/Shell/res/values-am/strings.xml b/libs/WindowManager/Shell/res/values-am/strings.xml
index 8bd602c08508..c65bb3822ec2 100644
--- a/libs/WindowManager/Shell/res/values-am/strings.xml
+++ b/libs/WindowManager/Shell/res/values-am/strings.xml
@@ -100,8 +100,7 @@
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"አልተስተካከለም?\nለማህደር መታ ያድርጉ"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"ምንም የካሜራ ችግሮች የሉም? ለማሰናበት መታ ያድርጉ።"</string>
<string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"የመተግበሪያ ምናሌው እዚህ መገኘት ይችላል"</string>
- <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
- <skip />
+ <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"በርካታ መተግበሪያዎችን በአንድ ላይ ለመክፈት ወደ የዴስክቶፕ መስኮት ይግቡ"</string>
<string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"በማንኛውም ጊዜ ከመተግበሪያ ምናሌው ላይ ወደ ሙሉ ገጽ እይታ ይመለሱ"</string>
<string name="letterbox_education_dialog_title" msgid="7739895354143295358">"ተጨማሪ ይመልከቱ እና ያድርጉ"</string>
<string name="letterbox_education_split_screen_text" msgid="449233070804658627">"ለተከፈለ ማያ ገፅ ሌላ መተግበሪያ ይጎትቱ"</string>
@@ -122,8 +121,7 @@
<string name="handle_text" msgid="4419667835599523257">"የመተግበሪያ መያዣ"</string>
<string name="app_icon_text" msgid="2823268023931811747">"የመተግበሪያ አዶ"</string>
<string name="fullscreen_text" msgid="1162316685217676079">"ሙሉ ማያ"</string>
- <!-- no translation found for desktop_text (9058641752519570266) -->
- <skip />
+ <string name="desktop_text" msgid="9058641752519570266">"ዴስክቶፕ መስኮት"</string>
<string name="split_screen_text" msgid="1396336058129570886">"የተከፈለ ማያ ገፅ"</string>
<string name="more_button_text" msgid="3655388105592893530">"ተጨማሪ"</string>
<string name="float_button_text" msgid="9221657008391364581">"ተንሳፋፊ"</string>
@@ -134,10 +132,11 @@
<string name="new_window_text" msgid="6318648868380652280">"አዲስ መስኮት"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"መስኮቶችን አስተዳድር"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"ምጥጥነ ገፅታ ለውጥ"</string>
+ <!-- no translation found for handle_menu_restart_text (3907767216238298098) -->
+ <skip />
<string name="close_text" msgid="4986518933445178928">"ዝጋ"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"ምናሌ ዝጋ"</string>
- <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
- <skip />
+ <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (ዴስክቶፕ መስኮት)"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"የማያ ገጹ መጠን አሳድግ"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"መጠን ቀይር"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"መተግበሪያ ወደዚህ መንቀሳቀስ አይችልም"</string>
diff --git a/libs/WindowManager/Shell/res/values-ar/strings.xml b/libs/WindowManager/Shell/res/values-ar/strings.xml
index 70a23b73b6f5..d06d99203245 100644
--- a/libs/WindowManager/Shell/res/values-ar/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ar/strings.xml
@@ -100,8 +100,7 @@
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"ألم يتم حل المشكلة؟\nانقر للعودة"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"أليس هناك مشاكل في الكاميرا؟ انقر للإغلاق."</string>
<string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"يمكن العثور على قائمة التطبيقات هنا"</string>
- <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
- <skip />
+ <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"يمكنك الدخول إلى وضع عرض المحتوى في النافذة الحالية على سطح المكتب لفتح عدة تطبيقات معًا"</string>
<string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"يمكنك الرجوع إلى وضع ملء الشاشة في أي وقت من قائمة التطبيقات"</string>
<string name="letterbox_education_dialog_title" msgid="7739895354143295358">"استخدام تطبيقات متعدّدة في وقت واحد"</string>
<string name="letterbox_education_split_screen_text" msgid="449233070804658627">"اسحب تطبيقًا آخر لاستخدام وضع تقسيم الشاشة."</string>
@@ -122,8 +121,7 @@
<string name="handle_text" msgid="4419667835599523257">"مقبض التطبيق"</string>
<string name="app_icon_text" msgid="2823268023931811747">"رمز التطبيق"</string>
<string name="fullscreen_text" msgid="1162316685217676079">"ملء الشاشة"</string>
- <!-- no translation found for desktop_text (9058641752519570266) -->
- <skip />
+ <string name="desktop_text" msgid="9058641752519570266">"عرض المحتوى في النافذة الحالية على سطح المكتب"</string>
<string name="split_screen_text" msgid="1396336058129570886">"تقسيم الشاشة"</string>
<string name="more_button_text" msgid="3655388105592893530">"المزيد"</string>
<string name="float_button_text" msgid="9221657008391364581">"نافذة عائمة"</string>
@@ -134,10 +132,11 @@
<string name="new_window_text" msgid="6318648868380652280">"نافذة جديدة"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"إدارة النوافذ"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"تغيير نسبة العرض إلى الارتفاع"</string>
+ <!-- no translation found for handle_menu_restart_text (3907767216238298098) -->
+ <skip />
<string name="close_text" msgid="4986518933445178928">"إغلاق"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"إغلاق القائمة"</string>
- <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
- <skip />
+ <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"‫<xliff:g id="APP_NAME">%1$s</xliff:g> (عرض المحتوى في النافذة الحالية على سطح المكتب)"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"تكبير الشاشة إلى أقصى حدّ"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"تغيير الحجم"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"لا يمكن نقل التطبيق إلى هنا"</string>
diff --git a/libs/WindowManager/Shell/res/values-as/strings.xml b/libs/WindowManager/Shell/res/values-as/strings.xml
index 6872df6acbc9..1d1a048675ac 100644
--- a/libs/WindowManager/Shell/res/values-as/strings.xml
+++ b/libs/WindowManager/Shell/res/values-as/strings.xml
@@ -100,8 +100,7 @@
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"এইটো সমাধান কৰা নাই নেকি?\nপূৰ্বাৱস্থালৈ নিবলৈ টিপক"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"কেমেৰাৰ কোনো সমস্যা নাই নেকি? অগ্ৰাহ্য কৰিবলৈ টিপক।"</string>
<string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"এপৰ মেনু ইয়াত বিচাৰি পোৱা যাব"</string>
- <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
- <skip />
+ <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"একেলগে একাধিক এপ্‌ খুলিবলৈ ডেস্কটপ ৱিণ্ড’ৱিঙলৈ যাওক"</string>
<string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"এপৰ মেনুৰ পৰা যিকোনো সময়তে পূৰ্ণ স্ক্ৰীনলৈ উভতি যাওক"</string>
<string name="letterbox_education_dialog_title" msgid="7739895354143295358">"চাওক আৰু অধিক কৰক"</string>
<string name="letterbox_education_split_screen_text" msgid="449233070804658627">"বিভাজিত স্ক্ৰীনৰ বাবে অন্য এটা এপ্‌ টানি আনি এৰক"</string>
@@ -122,8 +121,7 @@
<string name="handle_text" msgid="4419667835599523257">"এপৰ হেণ্ডেল"</string>
<string name="app_icon_text" msgid="2823268023931811747">"এপৰ চিহ্ন"</string>
<string name="fullscreen_text" msgid="1162316685217676079">"সম্পূৰ্ণ স্ক্ৰীন"</string>
- <!-- no translation found for desktop_text (9058641752519570266) -->
- <skip />
+ <string name="desktop_text" msgid="9058641752519570266">"ডেস্কটপ ৱিণ্ড’ৱিং"</string>
<string name="split_screen_text" msgid="1396336058129570886">"বিভাজিত স্ক্ৰীন"</string>
<string name="more_button_text" msgid="3655388105592893530">"অধিক"</string>
<string name="float_button_text" msgid="9221657008391364581">"ওপঙা"</string>
@@ -134,10 +132,11 @@
<string name="new_window_text" msgid="6318648868380652280">"নতুন ৱিণ্ড’"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"ৱিণ্ড’ পৰিচালনা কৰক"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"আকাৰৰ অনুপাত সলনি কৰক"</string>
+ <!-- no translation found for handle_menu_restart_text (3907767216238298098) -->
+ <skip />
<string name="close_text" msgid="4986518933445178928">"বন্ধ কৰক"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"মেনু বন্ধ কৰক"</string>
- <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
- <skip />
+ <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (ডেস্কটপ ৱিণ্ড’ৱিং)"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"স্ক্ৰীন মেক্সিমাইজ কৰক"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"আকাৰ সলনি কৰক"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"ইয়ালৈ এপ্‌টো আনিব নোৱাৰি"</string>
diff --git a/libs/WindowManager/Shell/res/values-az/strings.xml b/libs/WindowManager/Shell/res/values-az/strings.xml
index 46309411d0c9..2770ede1a85a 100644
--- a/libs/WindowManager/Shell/res/values-az/strings.xml
+++ b/libs/WindowManager/Shell/res/values-az/strings.xml
@@ -100,8 +100,7 @@
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Düzəltməmisiniz?\nGeri qaytarmaq üçün toxunun"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Kamera problemi yoxdur? Qapatmaq üçün toxunun."</string>
<string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Tətbiq menyusunu burada tapa bilərsiniz"</string>
- <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
- <skip />
+ <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"Bir neçə tətbiqi birlikdə açmaq üçün masaüstü pəncərə rejiminə daxil olun"</string>
<string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"İstənilən vaxt tətbiq menyusundan tam ekrana qayıdın"</string>
<string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Ardını görün və edin"</string>
<string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Bölünmüş ekran üçün başqa tətbiq sürüşdürün"</string>
@@ -122,8 +121,7 @@
<string name="handle_text" msgid="4419667835599523257">"Tətbiq ləqəbi"</string>
<string name="app_icon_text" msgid="2823268023931811747">"Tətbiq ikonası"</string>
<string name="fullscreen_text" msgid="1162316685217676079">"Tam Ekran"</string>
- <!-- no translation found for desktop_text (9058641752519570266) -->
- <skip />
+ <string name="desktop_text" msgid="9058641752519570266">"Masaüstü pəncərə rejimi"</string>
<string name="split_screen_text" msgid="1396336058129570886">"Bölünmüş Ekran"</string>
<string name="more_button_text" msgid="3655388105592893530">"Ardı"</string>
<string name="float_button_text" msgid="9221657008391364581">"Üzən pəncərə"</string>
@@ -134,10 +132,11 @@
<string name="new_window_text" msgid="6318648868380652280">"Yeni pəncərə"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"Pəncərələri idarə edin"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Tərəflər nisbətini dəyişin"</string>
+ <!-- no translation found for handle_menu_restart_text (3907767216238298098) -->
+ <skip />
<string name="close_text" msgid="4986518933445178928">"Bağlayın"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Menyunu bağlayın"</string>
- <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
- <skip />
+ <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (Masaüstü pəncərə rejimi)"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Ekranı maksimum böyüdün"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Ölçüsünü dəyişin"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Tətbiqi bura köçürmək mümkün deyil"</string>
diff --git a/libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml b/libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml
index 4af564833ba1..615b558a23a9 100644
--- a/libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml
+++ b/libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml
@@ -100,8 +100,7 @@
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Problem nije rešen?\nDodirnite da biste vratili"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Nemate problema sa kamerom? Dodirnite da biste odbacili."</string>
<string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Meni aplikacije možete da pronađete ovde"</string>
- <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
- <skip />
+ <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"Uđite u prozorski prikaz za računare da biste istovremeno otvorili više aplikacija"</string>
<string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Vratite se na ceo ekran bilo kada iz menija aplikacije"</string>
<string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Vidite i uradite više"</string>
<string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Prevucite drugu aplikaciju da biste koristili podeljeni ekran"</string>
@@ -122,8 +121,7 @@
<string name="handle_text" msgid="4419667835599523257">"Identifikator aplikacije"</string>
<string name="app_icon_text" msgid="2823268023931811747">"Ikona aplikacije"</string>
<string name="fullscreen_text" msgid="1162316685217676079">"Preko celog ekrana"</string>
- <!-- no translation found for desktop_text (9058641752519570266) -->
- <skip />
+ <string name="desktop_text" msgid="9058641752519570266">"Prozorski prikaz za računare"</string>
<string name="split_screen_text" msgid="1396336058129570886">"Podeljeni ekran"</string>
<string name="more_button_text" msgid="3655388105592893530">"Još"</string>
<string name="float_button_text" msgid="9221657008391364581">"Plutajuće"</string>
@@ -134,10 +132,11 @@
<string name="new_window_text" msgid="6318648868380652280">"Novi prozor"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"Upravljajte prozorima"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Promeni razmeru"</string>
+ <!-- no translation found for handle_menu_restart_text (3907767216238298098) -->
+ <skip />
<string name="close_text" msgid="4986518933445178928">"Zatvorite"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Zatvorite meni"</string>
- <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
- <skip />
+ <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (prozorski prikaz za računare)"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Povećaj ekran"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Prilagodi"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Aplikacija ne može da se premesti ovde"</string>
diff --git a/libs/WindowManager/Shell/res/values-be/strings.xml b/libs/WindowManager/Shell/res/values-be/strings.xml
index 7719396b01d1..d83ed575a34f 100644
--- a/libs/WindowManager/Shell/res/values-be/strings.xml
+++ b/libs/WindowManager/Shell/res/values-be/strings.xml
@@ -100,8 +100,7 @@
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Не ўдалося выправіць?\nНацісніце, каб аднавіць"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Ніякіх праблем з камерай? Націсніце, каб адхіліць."</string>
<string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Меню праграмы шукайце тут"</string>
- <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
- <skip />
+ <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"Каб адкрыць некалькі праграм адначасова, увайдзіце ў рэжым вокнаў працоўнага стала"</string>
<string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Вы можаце вярнуцца ў поўнаэкранны рэжым у любы час з меню праграмы"</string>
<string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Адначасова выконвайце розныя задачы"</string>
<string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Перацягніце іншую праграму, каб выкарыстоўваць падзелены экран"</string>
@@ -122,8 +121,7 @@
<string name="handle_text" msgid="4419667835599523257">"Маркер праграмы"</string>
<string name="app_icon_text" msgid="2823268023931811747">"Значок праграмы"</string>
<string name="fullscreen_text" msgid="1162316685217676079">"На ўвесь экран"</string>
- <!-- no translation found for desktop_text (9058641752519570266) -->
- <skip />
+ <string name="desktop_text" msgid="9058641752519570266">"Рэжым вокнаў працоўнага стала"</string>
<string name="split_screen_text" msgid="1396336058129570886">"Падзяліць экран"</string>
<string name="more_button_text" msgid="3655388105592893530">"Яшчэ"</string>
<string name="float_button_text" msgid="9221657008391364581">"Зрабіць рухомым акном"</string>
@@ -134,10 +132,11 @@
<string name="new_window_text" msgid="6318648868380652280">"Новае акно"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"Кіраваць вокнамі"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Змяніць суадносіны бакоў"</string>
+ <!-- no translation found for handle_menu_restart_text (3907767216238298098) -->
+ <skip />
<string name="close_text" msgid="4986518933445178928">"Закрыць"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Закрыць меню"</string>
- <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
- <skip />
+ <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (рэжым вокнаў працоўнага стала)"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Разгарнуць на ўвесь экран"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Змяніць памер"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Нельга перамясціць сюды праграму"</string>
diff --git a/libs/WindowManager/Shell/res/values-bg/strings.xml b/libs/WindowManager/Shell/res/values-bg/strings.xml
index 514556e30fe0..9b91d3d540e9 100644
--- a/libs/WindowManager/Shell/res/values-bg/strings.xml
+++ b/libs/WindowManager/Shell/res/values-bg/strings.xml
@@ -100,8 +100,7 @@
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Проблемът не се отстрани?\nДокоснете за връщане в предишното състояние"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Нямате проблеми с камерата? Докоснете, за да отхвърлите."</string>
<string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Можете да намерите менюто на приложението тук"</string>
- <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
- <skip />
+ <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"Активирайте режима за настолни компютри, за да отворите няколко приложения едновременно"</string>
<string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Преминете към цял екран по всяко време от менюто на приложението"</string>
<string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Преглеждайте и правете повече неща"</string>
<string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Преместете друго приложение с плъзгане, за да преминете в режим за разделен екран"</string>
@@ -122,8 +121,7 @@
<string name="handle_text" msgid="4419667835599523257">"Манипулатор за приложението"</string>
<string name="app_icon_text" msgid="2823268023931811747">"Икона на приложението"</string>
<string name="fullscreen_text" msgid="1162316685217676079">"Цял екран"</string>
- <!-- no translation found for desktop_text (9058641752519570266) -->
- <skip />
+ <string name="desktop_text" msgid="9058641752519570266">"Режим за настолни компютри"</string>
<string name="split_screen_text" msgid="1396336058129570886">"Разделяне на екрана"</string>
<string name="more_button_text" msgid="3655388105592893530">"Още"</string>
<string name="float_button_text" msgid="9221657008391364581">"Плаващо"</string>
@@ -134,10 +132,11 @@
<string name="new_window_text" msgid="6318648868380652280">"Нов прозорец"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"Управление на прозорците"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Промяна на съотношението"</string>
+ <!-- no translation found for handle_menu_restart_text (3907767216238298098) -->
+ <skip />
<string name="close_text" msgid="4986518933445178928">"Затваряне"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Затваряне на менюто"</string>
- <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
- <skip />
+ <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (режим за настолни компютри)"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Увеличаване на екрана"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Нов размер"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Приложението не може да бъде преместено тук"</string>
diff --git a/libs/WindowManager/Shell/res/values-bn/strings.xml b/libs/WindowManager/Shell/res/values-bn/strings.xml
index d4488a220ac7..9fd156fe6dcd 100644
--- a/libs/WindowManager/Shell/res/values-bn/strings.xml
+++ b/libs/WindowManager/Shell/res/values-bn/strings.xml
@@ -100,8 +100,7 @@
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"এখনও সমাধান হয়নি?\nরিভার্ট করার জন্য ট্যাপ করুন"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"ক্যামেরা সংক্রান্ত সমস্যা নেই? বাতিল করতে ট্যাপ করুন।"</string>
<string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"অ্যাপ মেনু এখানে খুঁজে পাওয়া যাবে"</string>
- <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
- <skip />
+ <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"একসাথে একাধিক অ্যাপ খোলার জন্য ডেস্কটপ উইন্ডোইংয়ে এন্টার করুন"</string>
<string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"অ্যাপ মেনু থেকে ফুল-স্ক্রিন মোডে যেকোনও সময়ে ফিরে আসুন"</string>
<string name="letterbox_education_dialog_title" msgid="7739895354143295358">"দেখুন ও আরও অনেক কিছু করুন"</string>
<string name="letterbox_education_split_screen_text" msgid="449233070804658627">"স্প্লিট স্ক্রিনের ক্ষেত্রে অন্য কোনও অ্যাপ টেনে আনুন"</string>
@@ -122,8 +121,7 @@
<string name="handle_text" msgid="4419667835599523257">"অ্যাপের হ্যান্ডেল"</string>
<string name="app_icon_text" msgid="2823268023931811747">"অ্যাপ আইকন"</string>
<string name="fullscreen_text" msgid="1162316685217676079">"ফুলস্ক্রিন"</string>
- <!-- no translation found for desktop_text (9058641752519570266) -->
- <skip />
+ <string name="desktop_text" msgid="9058641752519570266">"ডেস্কটপ উইন্ডোইং"</string>
<string name="split_screen_text" msgid="1396336058129570886">"স্প্লিট স্ক্রিন"</string>
<string name="more_button_text" msgid="3655388105592893530">"আরও"</string>
<string name="float_button_text" msgid="9221657008391364581">"ফ্লোট"</string>
@@ -134,10 +132,11 @@
<string name="new_window_text" msgid="6318648868380652280">"নতুন উইন্ডো"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"উইন্ডো ম্যানেজ করুন"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"অ্যাস্পেক্ট রেশিও পরিবর্তন করুন"</string>
+ <!-- no translation found for handle_menu_restart_text (3907767216238298098) -->
+ <skip />
<string name="close_text" msgid="4986518933445178928">"বন্ধ করুন"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"\'মেনু\' বন্ধ করুন"</string>
- <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
- <skip />
+ <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (ডেস্কটপ উইন্ডোইং)"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"স্ক্রিন বড় করুন"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"ছোট বড় করুন"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"অ্যাপটি এখানে সরানো যাবে না"</string>
diff --git a/libs/WindowManager/Shell/res/values-bs/strings.xml b/libs/WindowManager/Shell/res/values-bs/strings.xml
index 23c467c0b4ba..4834ad86db67 100644
--- a/libs/WindowManager/Shell/res/values-bs/strings.xml
+++ b/libs/WindowManager/Shell/res/values-bs/strings.xml
@@ -100,8 +100,7 @@
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Nije popravljeno?\nDodirnite da vratite"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Nema problema s kamerom? Dodirnite da odbacite."</string>
<string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Ovdje možete pronaći meni aplikacije"</string>
- <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
- <skip />
+ <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"Ulazak u računarski prikaz prozora radi istovremenog otvaranja više aplikacija"</string>
<string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Povratak na prikaz preko cijelog ekrana bilo kada putem menija aplikacije"</string>
<string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Pogledajte i učinite više"</string>
<string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Prevucite još jednu aplikaciju za podijeljeni ekran"</string>
@@ -122,8 +121,7 @@
<string name="handle_text" msgid="4419667835599523257">"Ručica aplikacije"</string>
<string name="app_icon_text" msgid="2823268023931811747">"Ikona aplikacije"</string>
<string name="fullscreen_text" msgid="1162316685217676079">"Cijeli ekran"</string>
- <!-- no translation found for desktop_text (9058641752519570266) -->
- <skip />
+ <string name="desktop_text" msgid="9058641752519570266">"Računarski prikaz prozora"</string>
<string name="split_screen_text" msgid="1396336058129570886">"Podijeljeni ekran"</string>
<string name="more_button_text" msgid="3655388105592893530">"Više"</string>
<string name="float_button_text" msgid="9221657008391364581">"Lebdeći"</string>
@@ -134,10 +132,11 @@
<string name="new_window_text" msgid="6318648868380652280">"Novi prozor"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"Upravljanje prozorima"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Promjena formata slike"</string>
+ <!-- no translation found for handle_menu_restart_text (3907767216238298098) -->
+ <skip />
<string name="close_text" msgid="4986518933445178928">"Zatvaranje"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Zatvaranje menija"</string>
- <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
- <skip />
+ <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (računarski prikaz prozora)"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maksimiziraj ekran"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Promijeni veličinu"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Ne možete premjestiti aplikaciju ovdje"</string>
diff --git a/libs/WindowManager/Shell/res/values-ca/strings.xml b/libs/WindowManager/Shell/res/values-ca/strings.xml
index 893d16e6155e..662bd81fe460 100644
--- a/libs/WindowManager/Shell/res/values-ca/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ca/strings.xml
@@ -100,8 +100,7 @@
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"El problema no s\'ha resolt?\nToca per desfer els canvis"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"No tens cap problema amb la càmera? Toca per ignorar."</string>
<string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Pots trobar el menú de l\'aplicació aquí"</string>
- <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
- <skip />
+ <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"Accedeix a l\'enfinestrament d\'escriptori per obrir diverses aplicacions alhora"</string>
<string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Torna a la pantalla completa en qualsevol moment des del menú de l\'aplicació"</string>
<string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Consulta i fes més coses"</string>
<string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Arrossega una altra aplicació per utilitzar la pantalla dividida"</string>
@@ -122,8 +121,7 @@
<string name="handle_text" msgid="4419667835599523257">"Identificador de l\'aplicació"</string>
<string name="app_icon_text" msgid="2823268023931811747">"Icona de l\'aplicació"</string>
<string name="fullscreen_text" msgid="1162316685217676079">"Pantalla completa"</string>
- <!-- no translation found for desktop_text (9058641752519570266) -->
- <skip />
+ <string name="desktop_text" msgid="9058641752519570266">"Enfinestrament d\'escriptori"</string>
<string name="split_screen_text" msgid="1396336058129570886">"Pantalla dividida"</string>
<string name="more_button_text" msgid="3655388105592893530">"Més"</string>
<string name="float_button_text" msgid="9221657008391364581">"Flotant"</string>
@@ -134,10 +132,11 @@
<string name="new_window_text" msgid="6318648868380652280">"Finestra nova"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"Gestiona les finestres"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Canvia la relació d\'aspecte"</string>
+ <!-- no translation found for handle_menu_restart_text (3907767216238298098) -->
+ <skip />
<string name="close_text" msgid="4986518933445178928">"Tanca"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Tanca el menú"</string>
- <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
- <skip />
+ <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (enfinestrament d\'escriptori)"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maximitza la pantalla"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Canvia la mida"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"L\'aplicació no es pot moure aquí"</string>
diff --git a/libs/WindowManager/Shell/res/values-cs/strings.xml b/libs/WindowManager/Shell/res/values-cs/strings.xml
index a96344a0a365..12c9e294e9c4 100644
--- a/libs/WindowManager/Shell/res/values-cs/strings.xml
+++ b/libs/WindowManager/Shell/res/values-cs/strings.xml
@@ -100,8 +100,7 @@
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Nepomohlo to?\nKlepnutím se vrátíte"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Žádné problémy s fotoaparátem? Klepnutím zavřete."</string>
<string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Najdete tu nabídku aplikace"</string>
- <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
- <skip />
+ <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"Pokud chcete otevřít několik aplikací současně, přejděte do režimu s okny na ploše"</string>
<string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Na celou obrazovku se můžete kdykoli vrátit z nabídky aplikace"</string>
<string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Lepší zobrazení a více možností"</string>
<string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Přetáhnutím druhé aplikace použijete rozdělenou obrazovku"</string>
@@ -122,8 +121,7 @@
<string name="handle_text" msgid="4419667835599523257">"Popisovač aplikace"</string>
<string name="app_icon_text" msgid="2823268023931811747">"Ikona aplikace"</string>
<string name="fullscreen_text" msgid="1162316685217676079">"Celá obrazovka"</string>
- <!-- no translation found for desktop_text (9058641752519570266) -->
- <skip />
+ <string name="desktop_text" msgid="9058641752519570266">"Okna na ploše"</string>
<string name="split_screen_text" msgid="1396336058129570886">"Rozdělená obrazovka"</string>
<string name="more_button_text" msgid="3655388105592893530">"Více"</string>
<string name="float_button_text" msgid="9221657008391364581">"Plovoucí"</string>
@@ -134,10 +132,11 @@
<string name="new_window_text" msgid="6318648868380652280">"Nové okno"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"Spravovat okna"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Změnit poměr stran"</string>
+ <!-- no translation found for handle_menu_restart_text (3907767216238298098) -->
+ <skip />
<string name="close_text" msgid="4986518933445178928">"Zavřít"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Zavřít nabídku"</string>
- <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
- <skip />
+ <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (okna na ploše)"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maximalizovat obrazovku"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Změnit velikost"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Aplikaci sem nelze přesunout"</string>
diff --git a/libs/WindowManager/Shell/res/values-da/strings.xml b/libs/WindowManager/Shell/res/values-da/strings.xml
index 7d28fc842e59..5df06ea2b9f7 100644
--- a/libs/WindowManager/Shell/res/values-da/strings.xml
+++ b/libs/WindowManager/Shell/res/values-da/strings.xml
@@ -100,8 +100,7 @@
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Løste det ikke problemet?\nTryk for at fortryde"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Har du ingen problemer med dit kamera? Tryk for at afvise."</string>
<string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Appmenuen kan findes her"</string>
- <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
- <skip />
+ <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"Brug vinduer på skrivebordet for at åbne flere apps på én gang"</string>
<string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Gå tilbage til fuld skærm når som helst via appmenuen"</string>
<string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Se og gør mere"</string>
<string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Træk en anden app hertil for at bruge opdelt skærm"</string>
@@ -122,8 +121,7 @@
<string name="handle_text" msgid="4419667835599523257">"Apphåndtag"</string>
<string name="app_icon_text" msgid="2823268023931811747">"Appikon"</string>
<string name="fullscreen_text" msgid="1162316685217676079">"Fuld skærm"</string>
- <!-- no translation found for desktop_text (9058641752519570266) -->
- <skip />
+ <string name="desktop_text" msgid="9058641752519570266">"Vinduer på skrivebordet"</string>
<string name="split_screen_text" msgid="1396336058129570886">"Opdelt skærm"</string>
<string name="more_button_text" msgid="3655388105592893530">"Mere"</string>
<string name="float_button_text" msgid="9221657008391364581">"Svævende"</string>
@@ -134,10 +132,11 @@
<string name="new_window_text" msgid="6318648868380652280">"Nyt vindue"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"Administrer vinduer"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Skift billedformat"</string>
+ <!-- no translation found for handle_menu_restart_text (3907767216238298098) -->
+ <skip />
<string name="close_text" msgid="4986518933445178928">"Luk"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Luk menu"</string>
- <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
- <skip />
+ <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (vinduer på skrivebordet)"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maksimér skærm"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Tilpas størrelse"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Apps kan ikke flyttes hertil"</string>
diff --git a/libs/WindowManager/Shell/res/values-de/strings.xml b/libs/WindowManager/Shell/res/values-de/strings.xml
index 4cc11243b8bd..b3444e0ac2b8 100644
--- a/libs/WindowManager/Shell/res/values-de/strings.xml
+++ b/libs/WindowManager/Shell/res/values-de/strings.xml
@@ -100,8 +100,7 @@
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Das Problem ist nicht behoben?\nZum Rückgängigmachen tippen."</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Keine Probleme mit der Kamera? Zum Schließen tippen."</string>
<string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Das App-Menü findest du hier"</string>
- <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
- <skip />
+ <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"Über ein Desktop-Freiform-Fenster kannst du mehrere Apps gleichzeitig öffnen"</string>
<string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Über das App-Menü kannst du jederzeit zum Vollbildmodus zurückkehren"</string>
<string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Mehr sehen und erledigen"</string>
<string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Für Splitscreen-Modus weitere App hineinziehen"</string>
@@ -122,8 +121,7 @@
<string name="handle_text" msgid="4419667835599523257">"App-Ziehpunkt"</string>
<string name="app_icon_text" msgid="2823268023931811747">"App-Symbol"</string>
<string name="fullscreen_text" msgid="1162316685217676079">"Vollbild"</string>
- <!-- no translation found for desktop_text (9058641752519570266) -->
- <skip />
+ <string name="desktop_text" msgid="9058641752519570266">"Desktop-Freiform-Fenster"</string>
<string name="split_screen_text" msgid="1396336058129570886">"Splitscreen"</string>
<string name="more_button_text" msgid="3655388105592893530">"Mehr"</string>
<string name="float_button_text" msgid="9221657008391364581">"Frei schwebend"</string>
@@ -134,10 +132,11 @@
<string name="new_window_text" msgid="6318648868380652280">"Neues Fenster"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"Fenster verwalten"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Seitenverhältnis ändern"</string>
+ <!-- no translation found for handle_menu_restart_text (3907767216238298098) -->
+ <skip />
<string name="close_text" msgid="4986518933445178928">"Schließen"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Menü schließen"</string>
- <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
- <skip />
+ <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (Desktop-Freiform-Fenster)"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Bildschirm maximieren"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Größe ändern"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Die App kann nicht hierher verschoben werden"</string>
diff --git a/libs/WindowManager/Shell/res/values-el/strings.xml b/libs/WindowManager/Shell/res/values-el/strings.xml
index 0fb17ecdb278..c137513b4d7c 100644
--- a/libs/WindowManager/Shell/res/values-el/strings.xml
+++ b/libs/WindowManager/Shell/res/values-el/strings.xml
@@ -100,8 +100,7 @@
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Δεν διορθώθηκε;\nΠατήστε για επαναφορά."</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Δεν αντιμετωπίζετε προβλήματα με την κάμερα; Πατήστε για παράβλεψη."</string>
<string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Μπορείτε να βρείτε το μενού εφαρμογών εδώ"</string>
- <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
- <skip />
+ <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"Μεταβείτε στην προσαρμογή σε παράθυρο στην επιφάνεια εργασίας, για να ανοίξετε πολλές εφαρμογές μαζί"</string>
<string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Επιστρέψτε στην πλήρη οθόνη ανά πάσα στιγμή από το μενού της εφαρμογής"</string>
<string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Δείτε και κάντε περισσότερα"</string>
<string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Σύρετε σε μια άλλη εφαρμογή για διαχωρισμό οθόνης."</string>
@@ -122,8 +121,7 @@
<string name="handle_text" msgid="4419667835599523257">"Λαβή εφαρμογής"</string>
<string name="app_icon_text" msgid="2823268023931811747">"Εικονίδιο εφαρμογής"</string>
<string name="fullscreen_text" msgid="1162316685217676079">"Πλήρης οθόνη"</string>
- <!-- no translation found for desktop_text (9058641752519570266) -->
- <skip />
+ <string name="desktop_text" msgid="9058641752519570266">"Προσαρμογή σε παράθυρο στην επιφάνεια εργασίας"</string>
<string name="split_screen_text" msgid="1396336058129570886">"Διαχωρισμός οθόνης"</string>
<string name="more_button_text" msgid="3655388105592893530">"Περισσότερα"</string>
<string name="float_button_text" msgid="9221657008391364581">"Κινούμενο"</string>
@@ -134,10 +132,11 @@
<string name="new_window_text" msgid="6318648868380652280">"Νέο παράθυρο"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"Διαχείριση παραθύρων"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Αλλαγή λόγου διαστάσεων"</string>
+ <!-- no translation found for handle_menu_restart_text (3907767216238298098) -->
+ <skip />
<string name="close_text" msgid="4986518933445178928">"Κλείσιμο"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Κλείσιμο μενού"</string>
- <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
- <skip />
+ <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (Προσαρμογή σε παράθυρο στην επιφάνεια εργασίας)"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Μεγιστοποίηση οθόνης"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Αλλαγή μεγέθους"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Δεν είναι δυνατή η μετακίνηση της εφαρμογής εδώ"</string>
diff --git a/libs/WindowManager/Shell/res/values-en-rAU/strings.xml b/libs/WindowManager/Shell/res/values-en-rAU/strings.xml
index 2087bb4ad579..a3156bc551eb 100644
--- a/libs/WindowManager/Shell/res/values-en-rAU/strings.xml
+++ b/libs/WindowManager/Shell/res/values-en-rAU/strings.xml
@@ -100,8 +100,7 @@
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Didn’t fix it?\nTap to revert"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"No camera issues? Tap to dismiss."</string>
<string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"The app menu can be found here"</string>
- <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
- <skip />
+ <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"Enter desktop windowing to open multiple apps together"</string>
<string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Return to full screen at any time from the app menu"</string>
<string name="letterbox_education_dialog_title" msgid="7739895354143295358">"See and do more"</string>
<string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Drag in another app for split screen"</string>
@@ -122,8 +121,7 @@
<string name="handle_text" msgid="4419667835599523257">"App handle"</string>
<string name="app_icon_text" msgid="2823268023931811747">"App icon"</string>
<string name="fullscreen_text" msgid="1162316685217676079">"Full screen"</string>
- <!-- no translation found for desktop_text (9058641752519570266) -->
- <skip />
+ <string name="desktop_text" msgid="9058641752519570266">"Desktop windowing"</string>
<string name="split_screen_text" msgid="1396336058129570886">"Split screen"</string>
<string name="more_button_text" msgid="3655388105592893530">"More"</string>
<string name="float_button_text" msgid="9221657008391364581">"Float"</string>
@@ -134,10 +132,11 @@
<string name="new_window_text" msgid="6318648868380652280">"New window"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"Manage windows"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Change aspect ratio"</string>
+ <!-- no translation found for handle_menu_restart_text (3907767216238298098) -->
+ <skip />
<string name="close_text" msgid="4986518933445178928">"Close"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Close menu"</string>
- <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
- <skip />
+ <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (Desktop windowing)"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maximise screen"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Resize"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"App can\'t be moved here"</string>
diff --git a/libs/WindowManager/Shell/res/values-en-rCA/strings.xml b/libs/WindowManager/Shell/res/values-en-rCA/strings.xml
index 0802f83ab837..3e2de7cd3b56 100644
--- a/libs/WindowManager/Shell/res/values-en-rCA/strings.xml
+++ b/libs/WindowManager/Shell/res/values-en-rCA/strings.xml
@@ -132,6 +132,7 @@
<string name="new_window_text" msgid="6318648868380652280">"New Window"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"Manage Windows"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Change aspect ratio"</string>
+ <string name="handle_menu_restart_text" msgid="3907767216238298098">"Optimize View"</string>
<string name="close_text" msgid="4986518933445178928">"Close"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Close Menu"</string>
<string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (Desktop windowing)"</string>
diff --git a/libs/WindowManager/Shell/res/values-en-rGB/strings.xml b/libs/WindowManager/Shell/res/values-en-rGB/strings.xml
index 2087bb4ad579..a3156bc551eb 100644
--- a/libs/WindowManager/Shell/res/values-en-rGB/strings.xml
+++ b/libs/WindowManager/Shell/res/values-en-rGB/strings.xml
@@ -100,8 +100,7 @@
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Didn’t fix it?\nTap to revert"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"No camera issues? Tap to dismiss."</string>
<string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"The app menu can be found here"</string>
- <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
- <skip />
+ <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"Enter desktop windowing to open multiple apps together"</string>
<string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Return to full screen at any time from the app menu"</string>
<string name="letterbox_education_dialog_title" msgid="7739895354143295358">"See and do more"</string>
<string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Drag in another app for split screen"</string>
@@ -122,8 +121,7 @@
<string name="handle_text" msgid="4419667835599523257">"App handle"</string>
<string name="app_icon_text" msgid="2823268023931811747">"App icon"</string>
<string name="fullscreen_text" msgid="1162316685217676079">"Full screen"</string>
- <!-- no translation found for desktop_text (9058641752519570266) -->
- <skip />
+ <string name="desktop_text" msgid="9058641752519570266">"Desktop windowing"</string>
<string name="split_screen_text" msgid="1396336058129570886">"Split screen"</string>
<string name="more_button_text" msgid="3655388105592893530">"More"</string>
<string name="float_button_text" msgid="9221657008391364581">"Float"</string>
@@ -134,10 +132,11 @@
<string name="new_window_text" msgid="6318648868380652280">"New window"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"Manage windows"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Change aspect ratio"</string>
+ <!-- no translation found for handle_menu_restart_text (3907767216238298098) -->
+ <skip />
<string name="close_text" msgid="4986518933445178928">"Close"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Close menu"</string>
- <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
- <skip />
+ <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (Desktop windowing)"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maximise screen"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Resize"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"App can\'t be moved here"</string>
diff --git a/libs/WindowManager/Shell/res/values-en-rIN/strings.xml b/libs/WindowManager/Shell/res/values-en-rIN/strings.xml
index 2087bb4ad579..a3156bc551eb 100644
--- a/libs/WindowManager/Shell/res/values-en-rIN/strings.xml
+++ b/libs/WindowManager/Shell/res/values-en-rIN/strings.xml
@@ -100,8 +100,7 @@
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Didn’t fix it?\nTap to revert"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"No camera issues? Tap to dismiss."</string>
<string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"The app menu can be found here"</string>
- <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
- <skip />
+ <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"Enter desktop windowing to open multiple apps together"</string>
<string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Return to full screen at any time from the app menu"</string>
<string name="letterbox_education_dialog_title" msgid="7739895354143295358">"See and do more"</string>
<string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Drag in another app for split screen"</string>
@@ -122,8 +121,7 @@
<string name="handle_text" msgid="4419667835599523257">"App handle"</string>
<string name="app_icon_text" msgid="2823268023931811747">"App icon"</string>
<string name="fullscreen_text" msgid="1162316685217676079">"Full screen"</string>
- <!-- no translation found for desktop_text (9058641752519570266) -->
- <skip />
+ <string name="desktop_text" msgid="9058641752519570266">"Desktop windowing"</string>
<string name="split_screen_text" msgid="1396336058129570886">"Split screen"</string>
<string name="more_button_text" msgid="3655388105592893530">"More"</string>
<string name="float_button_text" msgid="9221657008391364581">"Float"</string>
@@ -134,10 +132,11 @@
<string name="new_window_text" msgid="6318648868380652280">"New window"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"Manage windows"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Change aspect ratio"</string>
+ <!-- no translation found for handle_menu_restart_text (3907767216238298098) -->
+ <skip />
<string name="close_text" msgid="4986518933445178928">"Close"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Close menu"</string>
- <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
- <skip />
+ <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (Desktop windowing)"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maximise screen"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Resize"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"App can\'t be moved here"</string>
diff --git a/libs/WindowManager/Shell/res/values-es-rUS/strings.xml b/libs/WindowManager/Shell/res/values-es-rUS/strings.xml
index bb70f6e419a5..6027f7df4272 100644
--- a/libs/WindowManager/Shell/res/values-es-rUS/strings.xml
+++ b/libs/WindowManager/Shell/res/values-es-rUS/strings.xml
@@ -100,8 +100,7 @@
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"¿No se resolvió?\nPresiona para revertir los cambios"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"¿No tienes problemas con la cámara? Presionar para descartar."</string>
<string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"El menú de la app se encuentra aquí"</string>
- <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
- <skip />
+ <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"Entra a la renderización en ventanas de escritorio para abrir varias apps a la vez"</string>
<string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Regresa a pantalla completa en cualquier momento desde el menú de la app"</string>
<string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Aprovecha más"</string>
<string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Arrastra otra app para el modo de pantalla dividida"</string>
@@ -122,8 +121,7 @@
<string name="handle_text" msgid="4419667835599523257">"Controlador de la app"</string>
<string name="app_icon_text" msgid="2823268023931811747">"Ícono de la app"</string>
<string name="fullscreen_text" msgid="1162316685217676079">"Pantalla completa"</string>
- <!-- no translation found for desktop_text (9058641752519570266) -->
- <skip />
+ <string name="desktop_text" msgid="9058641752519570266">"Renderización en ventanas de escritorio"</string>
<string name="split_screen_text" msgid="1396336058129570886">"Pantalla dividida"</string>
<string name="more_button_text" msgid="3655388105592893530">"Más"</string>
<string name="float_button_text" msgid="9221657008391364581">"Flotante"</string>
@@ -134,10 +132,11 @@
<string name="new_window_text" msgid="6318648868380652280">"Nueva ventana"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"Administrar ventanas"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Cambiar relación de aspecto"</string>
+ <!-- no translation found for handle_menu_restart_text (3907767216238298098) -->
+ <skip />
<string name="close_text" msgid="4986518933445178928">"Cerrar"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Cerrar menú"</string>
- <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
- <skip />
+ <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (renderización en ventanas de escritorio)"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maximizar pantalla"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Dividir pantalla"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"No se puede mover la app aquí"</string>
diff --git a/libs/WindowManager/Shell/res/values-es/strings.xml b/libs/WindowManager/Shell/res/values-es/strings.xml
index a6595aa9292d..81c9e1b214c4 100644
--- a/libs/WindowManager/Shell/res/values-es/strings.xml
+++ b/libs/WindowManager/Shell/res/values-es/strings.xml
@@ -100,8 +100,7 @@
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"¿No se ha solucionado?\nToca para revertir"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"¿No hay problemas con la cámara? Toca para cerrar."</string>
<string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"El menú de la aplicación se encuentra aquí"</string>
- <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
- <skip />
+ <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"Entra en el escritorio basado en ventanas si quieres abrir varias aplicaciones a la vez"</string>
<string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Vuelve a la pantalla completa en cualquier momento desde el menú de aplicaciones"</string>
<string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Consulta más información y haz más"</string>
<string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Arrastra otra aplicación para activar la pantalla dividida"</string>
@@ -122,8 +121,7 @@
<string name="handle_text" msgid="4419667835599523257">"Controlador de la aplicación"</string>
<string name="app_icon_text" msgid="2823268023931811747">"Icono de la aplicación"</string>
<string name="fullscreen_text" msgid="1162316685217676079">"Pantalla completa"</string>
- <!-- no translation found for desktop_text (9058641752519570266) -->
- <skip />
+ <string name="desktop_text" msgid="9058641752519570266">"Escritorio basado en ventanas"</string>
<string name="split_screen_text" msgid="1396336058129570886">"Pantalla dividida"</string>
<string name="more_button_text" msgid="3655388105592893530">"Más"</string>
<string name="float_button_text" msgid="9221657008391364581">"Flotante"</string>
@@ -134,10 +132,11 @@
<string name="new_window_text" msgid="6318648868380652280">"Ventana nueva"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"Gestionar ventanas"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Cambiar relación de aspecto"</string>
+ <!-- no translation found for handle_menu_restart_text (3907767216238298098) -->
+ <skip />
<string name="close_text" msgid="4986518933445178928">"Cerrar"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Cerrar menú"</string>
- <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
- <skip />
+ <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (escritorio basado en ventanas)"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maximizar pantalla"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Dividir pantalla"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"La aplicación no se puede mover aquí"</string>
diff --git a/libs/WindowManager/Shell/res/values-et/strings.xml b/libs/WindowManager/Shell/res/values-et/strings.xml
index 36086578dd4d..f43348d1f2dc 100644
--- a/libs/WindowManager/Shell/res/values-et/strings.xml
+++ b/libs/WindowManager/Shell/res/values-et/strings.xml
@@ -100,8 +100,7 @@
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Kas probleemi ei lahendatud?\nPuudutage ennistamiseks."</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Kas kaameraprobleeme pole? Puudutage loobumiseks."</string>
<string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Rakenduse menüü leiate siit"</string>
- <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
- <skip />
+ <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"Mitme rakenduse koos avamiseks kasutage töölaua aknaid"</string>
<string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Saate rakenduse menüüst igal ajal täisekraanile naasta"</string>
<string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Vaadake ja tehke rohkem"</string>
<string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Lohistage muusse rakendusse, et jagatud ekraanikuva kasutada"</string>
@@ -122,8 +121,7 @@
<string name="handle_text" msgid="4419667835599523257">"Rakenduse element"</string>
<string name="app_icon_text" msgid="2823268023931811747">"Rakenduse ikoon"</string>
<string name="fullscreen_text" msgid="1162316685217676079">"Täisekraan"</string>
- <!-- no translation found for desktop_text (9058641752519570266) -->
- <skip />
+ <string name="desktop_text" msgid="9058641752519570266">"Töölaua aknad"</string>
<string name="split_screen_text" msgid="1396336058129570886">"Jagatud ekraanikuva"</string>
<string name="more_button_text" msgid="3655388105592893530">"Rohkem"</string>
<string name="float_button_text" msgid="9221657008391364581">"Hõljuv"</string>
@@ -134,10 +132,11 @@
<string name="new_window_text" msgid="6318648868380652280">"Uus aken"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"Akende haldamine"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Kuvasuhte muutmine"</string>
+ <!-- no translation found for handle_menu_restart_text (3907767216238298098) -->
+ <skip />
<string name="close_text" msgid="4986518933445178928">"Sule"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Sule menüü"</string>
- <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
- <skip />
+ <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (töölaua aknad)"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Kuva täisekraanil"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Suuruse muutmine"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Rakendust ei saa siia teisaldada"</string>
diff --git a/libs/WindowManager/Shell/res/values-eu/strings.xml b/libs/WindowManager/Shell/res/values-eu/strings.xml
index 446839a8eb06..4ac27c2c248e 100644
--- a/libs/WindowManager/Shell/res/values-eu/strings.xml
+++ b/libs/WindowManager/Shell/res/values-eu/strings.xml
@@ -100,8 +100,7 @@
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Ez al da konpondu?\nLeheneratzeko, sakatu hau."</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Ez daukazu arazorik kamerarekin? Baztertzeko, sakatu hau."</string>
<string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Aplikazioaren menua dago hemen"</string>
- <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
- <skip />
+ <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"Sartu ordenagailuan leihoak erabiltzeko modua aplikazio bat baino gehiago batera irekitzeko"</string>
<string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Pantaila osoko modura itzultzeko, erabili aplikazioaren menua"</string>
<string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Ikusi eta egin gauza gehiago"</string>
<string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Pantaila zatitua ikusteko, arrastatu beste aplikazio bat"</string>
@@ -122,8 +121,7 @@
<string name="handle_text" msgid="4419667835599523257">"Aplikazioaren kontrol-puntua"</string>
<string name="app_icon_text" msgid="2823268023931811747">"Aplikazioaren ikonoa"</string>
<string name="fullscreen_text" msgid="1162316685217676079">"Pantaila osoa"</string>
- <!-- no translation found for desktop_text (9058641752519570266) -->
- <skip />
+ <string name="desktop_text" msgid="9058641752519570266">"Ordenagailuan leihoak erabiltzeko modua"</string>
<string name="split_screen_text" msgid="1396336058129570886">"Pantaila zatitzea"</string>
<string name="more_button_text" msgid="3655388105592893530">"Gehiago"</string>
<string name="float_button_text" msgid="9221657008391364581">"Leiho gainerakorra"</string>
@@ -134,10 +132,11 @@
<string name="new_window_text" msgid="6318648868380652280">"Leiho berria"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"Kudeatu leihoak"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Aldatu aspektu-erlazioa"</string>
+ <!-- no translation found for handle_menu_restart_text (3907767216238298098) -->
+ <skip />
<string name="close_text" msgid="4986518933445178928">"Itxi"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Itxi menua"</string>
- <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
- <skip />
+ <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (ordenagailuan leihoak erabiltzeko modua)"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Handitu pantaila"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Aldatu tamaina"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Aplikazioa ezin da hona ekarri"</string>
diff --git a/libs/WindowManager/Shell/res/values-fa/strings.xml b/libs/WindowManager/Shell/res/values-fa/strings.xml
index bf5c8f95cfb3..93a9438ba045 100644
--- a/libs/WindowManager/Shell/res/values-fa/strings.xml
+++ b/libs/WindowManager/Shell/res/values-fa/strings.xml
@@ -100,8 +100,7 @@
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"مشکل برطرف نشد؟\nبرای برگرداندن تک‌ضرب بزنید"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"دوربین مشکلی ندارد؟ برای بستن تک‌ضرب بزنید."</string>
<string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"منو برنامه را می‌توانید اینجا ببینید"</string>
- <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
- <skip />
+ <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"برای باز کردن هم‌زمان چند برنامه، وارد پردازش پنجره‌ای رایانه شوید"</string>
<string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"هروقت خواستید از منو برنامه به حالت تمام‌صفحه برگردید"</string>
<string name="letterbox_education_dialog_title" msgid="7739895354143295358">"از چندین برنامه به‌طور هم‌زمان استفاده کنید"</string>
<string name="letterbox_education_split_screen_text" msgid="449233070804658627">"برای حالت صفحهٔ دونیمه، در برنامه‌ای دیگر بکشید"</string>
@@ -122,8 +121,7 @@
<string name="handle_text" msgid="4419667835599523257">"دستگیره برنامه"</string>
<string name="app_icon_text" msgid="2823268023931811747">"نماد برنامه"</string>
<string name="fullscreen_text" msgid="1162316685217676079">"تمام‌صفحه"</string>
- <!-- no translation found for desktop_text (9058641752519570266) -->
- <skip />
+ <string name="desktop_text" msgid="9058641752519570266">"پردازش پنجره‌ای رایانه"</string>
<string name="split_screen_text" msgid="1396336058129570886">"صفحهٔ دونیمه"</string>
<string name="more_button_text" msgid="3655388105592893530">"بیشتر"</string>
<string name="float_button_text" msgid="9221657008391364581">"شناور"</string>
@@ -134,10 +132,11 @@
<string name="new_window_text" msgid="6318648868380652280">"پنجره جدید"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"مدیریت کردن پنجره‌ها"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"تغییر نسبت ابعادی"</string>
+ <!-- no translation found for handle_menu_restart_text (3907767216238298098) -->
+ <skip />
<string name="close_text" msgid="4986518933445178928">"بستن"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"بستن منو"</string>
- <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
- <skip />
+ <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (پردازش پنجره‌ای رایانه)"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"بزرگ کردن صفحه"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"تغییر اندازه"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"برنامه را نمی‌توان به اینجا منتقل کرد"</string>
diff --git a/libs/WindowManager/Shell/res/values-fi/strings.xml b/libs/WindowManager/Shell/res/values-fi/strings.xml
index a32e4fab9d81..7c9e6e6c5229 100644
--- a/libs/WindowManager/Shell/res/values-fi/strings.xml
+++ b/libs/WindowManager/Shell/res/values-fi/strings.xml
@@ -100,8 +100,7 @@
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Eikö ongelma ratkennut?\nKumoa napauttamalla"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Ei ongelmia kameran kanssa? Hylkää napauttamalla."</string>
<string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Sovellusvalikko löytyy täältä"</string>
- <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
- <skip />
+ <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"Siirry työpöydän ikkunointiin, niin voit avata useita sovelluksia kerralla"</string>
<string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Voit palata koko näytön tilaan milloin tahansa sovellusvalikosta"</string>
<string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Näe ja tee enemmän"</string>
<string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Käytä jaettua näyttöä vetämällä tähän toinen sovellus"</string>
@@ -122,8 +121,7 @@
<string name="handle_text" msgid="4419667835599523257">"Sovelluksen tunnus"</string>
<string name="app_icon_text" msgid="2823268023931811747">"Sovelluskuvake"</string>
<string name="fullscreen_text" msgid="1162316685217676079">"Koko näyttö"</string>
- <!-- no translation found for desktop_text (9058641752519570266) -->
- <skip />
+ <string name="desktop_text" msgid="9058641752519570266">"Työpöydän ikkunointi"</string>
<string name="split_screen_text" msgid="1396336058129570886">"Jaettu näyttö"</string>
<string name="more_button_text" msgid="3655388105592893530">"Lisää"</string>
<string name="float_button_text" msgid="9221657008391364581">"Kelluva ikkuna"</string>
@@ -134,10 +132,11 @@
<string name="new_window_text" msgid="6318648868380652280">"Uusi ikkuna"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"Hallinnoi ikkunoita"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Vaihda kuvasuhdetta"</string>
+ <!-- no translation found for handle_menu_restart_text (3907767216238298098) -->
+ <skip />
<string name="close_text" msgid="4986518933445178928">"Sulje"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Sulje valikko"</string>
- <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
- <skip />
+ <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (työpöydän ikkunointi)"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Suurenna näyttö"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Muuta kokoa"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Sovellusta ei voi siirtää tänne"</string>
diff --git a/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml b/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml
index 7037377156c6..c78b3130cdac 100644
--- a/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml
+++ b/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml
@@ -100,8 +100,7 @@
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Problème non résolu?\nTouchez pour rétablir"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Aucun problème d\'appareil photo? Touchez pour ignorer."</string>
<string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Le menu de l\'appli se trouve ici"</string>
- <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
- <skip />
+ <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"Accéder au fenêtrage du bureau pour ouvrir plusieurs applis simultanément"</string>
<string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Revenir au mode Plein écran à tout moment à partir du menu de l\'appli"</string>
<string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Voir et en faire plus"</string>
<string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Faites glisser une autre appli pour utiliser l\'écran partagé"</string>
@@ -122,8 +121,7 @@
<string name="handle_text" msgid="4419667835599523257">"Poignée de l\'appli"</string>
<string name="app_icon_text" msgid="2823268023931811747">"Icône de l\'appli"</string>
<string name="fullscreen_text" msgid="1162316685217676079">"Plein écran"</string>
- <!-- no translation found for desktop_text (9058641752519570266) -->
- <skip />
+ <string name="desktop_text" msgid="9058641752519570266">"Fenêtrage du bureau"</string>
<string name="split_screen_text" msgid="1396336058129570886">"Écran divisé"</string>
<string name="more_button_text" msgid="3655388105592893530">"Plus"</string>
<string name="float_button_text" msgid="9221657008391364581">"Flottant"</string>
@@ -134,10 +132,11 @@
<string name="new_window_text" msgid="6318648868380652280">"Nouvelle fenêtre"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"Gérer les fenêtres"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Modifier les proportions"</string>
+ <!-- no translation found for handle_menu_restart_text (3907767216238298098) -->
+ <skip />
<string name="close_text" msgid="4986518933445178928">"Fermer"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Fermer le menu"</string>
- <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
- <skip />
+ <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (fenêtrage du bureau)"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Agrandir l\'écran"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Redimensionner"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Impossible de déplacer l\'appli ici"</string>
diff --git a/libs/WindowManager/Shell/res/values-fr/strings.xml b/libs/WindowManager/Shell/res/values-fr/strings.xml
index fb2ebfd55583..708212fe77bc 100644
--- a/libs/WindowManager/Shell/res/values-fr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-fr/strings.xml
@@ -100,8 +100,7 @@
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Problème non résolu ?\nAppuyez pour rétablir"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Aucun problème d\'appareil photo ? Appuyez pour ignorer."</string>
<string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Le menu de l\'application se trouve ici"</string>
- <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
- <skip />
+ <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"Utiliser le fenêtrage de bureau pour ouvrir plusieurs applications simultanément"</string>
<string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Revenir en plein écran à tout moment depuis le menu de l\'application"</string>
<string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Voir et interagir plus"</string>
<string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Faites glisser une autre appli pour utiliser l\'écran partagé"</string>
@@ -122,8 +121,7 @@
<string name="handle_text" msgid="4419667835599523257">"Poignée de l\'appli"</string>
<string name="app_icon_text" msgid="2823268023931811747">"Icône d\'application"</string>
<string name="fullscreen_text" msgid="1162316685217676079">"Plein écran"</string>
- <!-- no translation found for desktop_text (9058641752519570266) -->
- <skip />
+ <string name="desktop_text" msgid="9058641752519570266">"Fenêtrage de bureau"</string>
<string name="split_screen_text" msgid="1396336058129570886">"Écran partagé"</string>
<string name="more_button_text" msgid="3655388105592893530">"Plus"</string>
<string name="float_button_text" msgid="9221657008391364581">"Flottante"</string>
@@ -134,10 +132,11 @@
<string name="new_window_text" msgid="6318648868380652280">"Nouvelle fenêtre"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"Gérer les fenêtres"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Modifier le format"</string>
+ <!-- no translation found for handle_menu_restart_text (3907767216238298098) -->
+ <skip />
<string name="close_text" msgid="4986518933445178928">"Fermer"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Fermer le menu"</string>
- <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
- <skip />
+ <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (fenêtrage de bureau)"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Mettre en plein écran"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Redimensionner"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Impossible de déplacer l\'appli ici"</string>
diff --git a/libs/WindowManager/Shell/res/values-gl/strings.xml b/libs/WindowManager/Shell/res/values-gl/strings.xml
index 895cf47e3d53..a0a4df17edfe 100644
--- a/libs/WindowManager/Shell/res/values-gl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-gl/strings.xml
@@ -100,8 +100,7 @@
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Non se solucionaron os problemas?\nToca para reverter o seu tratamento"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Non hai problemas coa cámara? Tocar para ignorar."</string>
<string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Aquí podes ver o menú da aplicación"</string>
- <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
- <skip />
+ <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"Vai ao escritorio baseado en ventás se queres abrir varias aplicacións á vez"</string>
<string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Volve á pantalla completa en calquera momento desde o menú da aplicación"</string>
<string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Ver e facer máis"</string>
<string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Arrastra outra aplicación para usar a pantalla dividida"</string>
@@ -122,8 +121,7 @@
<string name="handle_text" msgid="4419667835599523257">"Controlador da aplicación"</string>
<string name="app_icon_text" msgid="2823268023931811747">"Icona de aplicación"</string>
<string name="fullscreen_text" msgid="1162316685217676079">"Pantalla completa"</string>
- <!-- no translation found for desktop_text (9058641752519570266) -->
- <skip />
+ <string name="desktop_text" msgid="9058641752519570266">"Escritorio baseado en ventás"</string>
<string name="split_screen_text" msgid="1396336058129570886">"Pantalla dividida"</string>
<string name="more_button_text" msgid="3655388105592893530">"Máis"</string>
<string name="float_button_text" msgid="9221657008391364581">"Flotante"</string>
@@ -134,10 +132,11 @@
<string name="new_window_text" msgid="6318648868380652280">"Ventá nova"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"Xestionar as ventás"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Cambiar a proporción"</string>
+ <!-- no translation found for handle_menu_restart_text (3907767216238298098) -->
+ <skip />
<string name="close_text" msgid="4986518933445178928">"Pechar"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Pechar o menú"</string>
- <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
- <skip />
+ <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (escritorio baseado en ventás)"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maximizar pantalla"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Cambiar tamaño"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Non se pode mover aquí a aplicación"</string>
diff --git a/libs/WindowManager/Shell/res/values-gu/strings.xml b/libs/WindowManager/Shell/res/values-gu/strings.xml
index 9c8cf96be294..2a546fa2a23c 100644
--- a/libs/WindowManager/Shell/res/values-gu/strings.xml
+++ b/libs/WindowManager/Shell/res/values-gu/strings.xml
@@ -43,10 +43,8 @@
<string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"ડાબે 50%"</string>
<string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"ડાબે 30%"</string>
<string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"જમણી સ્ક્રીન સ્ક્રીન"</string>
- <!-- no translation found for accessibility_action_divider_swap_vertical (3644891227133372072) -->
- <skip />
- <!-- no translation found for accessibility_action_divider_swap_horizontal (2722197605446631628) -->
- <skip />
+ <string name="accessibility_action_divider_swap_vertical" msgid="3644891227133372072">"એકદમ ઉપરની ઍપને એકદમ નીચેની સાથે સ્વૉપ કરો"</string>
+ <string name="accessibility_action_divider_swap_horizontal" msgid="2722197605446631628">"ડાબી ઍપને જમણી સાથે સ્વૉપ કરો"</string>
<string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"શીર્ષ પૂર્ણ સ્ક્રીન"</string>
<string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"શીર્ષ 70%"</string>
<string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"શીર્ષ 50%"</string>
@@ -102,8 +100,7 @@
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"સુધારો નથી થયો?\nપહેલાંના પર પાછું ફેરવવા માટે ટૅપ કરો"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"કૅમેરામાં કોઈ સમસ્યા નથી? છોડી દેવા માટે ટૅપ કરો."</string>
<string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"ઍપ મેનૂ અહીં જોવા મળી શકે છે"</string>
- <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
- <skip />
+ <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"એકથી વધુ ઍપ એકસાથે ખોલવા માટે ડેસ્કટૉપ વિન્ડોઇંગ દાખલ કરો"</string>
<string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"ઍપ મેનૂમાંથી કોઈપણ સમયે પૂર્ણ સ્ક્રીન પર પાછા ફરો"</string>
<string name="letterbox_education_dialog_title" msgid="7739895354143295358">"જુઓ અને બીજું ઘણું કરો"</string>
<string name="letterbox_education_split_screen_text" msgid="449233070804658627">"વિભાજિત સ્ક્રીન માટે કોઈ અન્ય ઍપમાં ખેંચો"</string>
@@ -116,20 +113,15 @@
<string name="letterbox_restart_restart" msgid="8529976234412442973">"ફરી શરૂ કરો"</string>
<string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"ફરીથી બતાવશો નહીં"</string>
<string name="letterbox_reachability_reposition_text" msgid="3522042240665748268">"આ ઍપને ખસેડવા માટે\nબે વાર ટૅપ કરો"</string>
- <!-- no translation found for maximize_button_text (8106849394538234709) -->
- <skip />
- <!-- no translation found for restore_button_text (5377571986086775288) -->
- <skip />
- <!-- no translation found for minimize_button_text (5213953162664451152) -->
- <skip />
- <!-- no translation found for close_button_text (4544839489310949894) -->
- <skip />
+ <string name="maximize_button_text" msgid="8106849394538234709">"<xliff:g id="APP_NAME">%1$s</xliff:g>નું કદ મહત્તમ કરો"</string>
+ <string name="restore_button_text" msgid="5377571986086775288">"<xliff:g id="APP_NAME">%1$s</xliff:g>ને રિસ્ટોર કરો"</string>
+ <string name="minimize_button_text" msgid="5213953162664451152">"<xliff:g id="APP_NAME">%1$s</xliff:g>નું કદ ન્યૂનતમ કરો"</string>
+ <string name="close_button_text" msgid="4544839489310949894">"<xliff:g id="APP_NAME">%1$s</xliff:g> બંધ કરો"</string>
<string name="back_button_text" msgid="1469718707134137085">"પાછળ"</string>
<string name="handle_text" msgid="4419667835599523257">"ઍપનું હૅન્ડલ"</string>
<string name="app_icon_text" msgid="2823268023931811747">"ઍપનું આઇકન"</string>
<string name="fullscreen_text" msgid="1162316685217676079">"પૂર્ણસ્ક્રીન"</string>
- <!-- no translation found for desktop_text (9058641752519570266) -->
- <skip />
+ <string name="desktop_text" msgid="9058641752519570266">"ડેસ્કટૉપ વિન્ડોઇંગ"</string>
<string name="split_screen_text" msgid="1396336058129570886">"સ્ક્રીનને વિભાજિત કરો"</string>
<string name="more_button_text" msgid="3655388105592893530">"વધુ"</string>
<string name="float_button_text" msgid="9221657008391364581">"ફ્લોટિંગ વિન્ડો"</string>
@@ -140,10 +132,11 @@
<string name="new_window_text" msgid="6318648868380652280">"નવી વિન્ડો"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"વિન્ડો મેનેજ કરો"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"સાપેક્ષ ગુણોત્તર બદલો"</string>
+ <!-- no translation found for handle_menu_restart_text (3907767216238298098) -->
+ <skip />
<string name="close_text" msgid="4986518933445178928">"બંધ કરો"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"મેનૂ બંધ કરો"</string>
- <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
- <skip />
+ <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (ડેસ્કટૉપ વિન્ડોઇંગ)"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"સ્ક્રીન કરો મોટી કરો"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"કદ બદલો"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"ઍપ અહીં ખસેડી શકાતી નથી"</string>
@@ -161,14 +154,10 @@
<string name="maximize_menu_talkback_action_snap_left_text" msgid="500309467459084564">"ડાબી બાજુ વિન્ડોનું કદ બદલો"</string>
<string name="maximize_menu_talkback_action_snap_right_text" msgid="7010831426654467163">"જમણી બાજુ વિન્ડોનું કદ બદલો"</string>
<string name="maximize_menu_talkback_action_maximize_restore_text" msgid="4942610897847934859">"વિન્ડોનું કદ મહત્તમ કરો અથવા રિસ્ટોર કરો"</string>
- <!-- no translation found for app_header_talkback_action_maximize_button_text (8776156791095878638) -->
- <skip />
- <!-- no translation found for app_header_talkback_action_restore_button_text (2153022340772980863) -->
- <skip />
- <!-- no translation found for app_header_talkback_action_minimize_button_text (7491054416186901764) -->
- <skip />
- <!-- no translation found for app_header_talkback_action_close_button_text (5159612596378268926) -->
- <skip />
+ <string name="app_header_talkback_action_maximize_button_text" msgid="8776156791095878638">"ઍપની વિન્ડોનું કદ મહત્તમ કરો"</string>
+ <string name="app_header_talkback_action_restore_button_text" msgid="2153022340772980863">"વિન્ડોનું કદ રિસ્ટોર કરો"</string>
+ <string name="app_header_talkback_action_minimize_button_text" msgid="7491054416186901764">"ઍપની વિન્ડોનું કદ ન્યૂનતમ કરો"</string>
+ <string name="app_header_talkback_action_close_button_text" msgid="5159612596378268926">"ઍપની વિન્ડો બંધ કરો"</string>
<string name="open_by_default_settings_text" msgid="2526548548598185500">"\'ડિફૉલ્ટ તરીકે ખોલો\' સેટિંગ"</string>
<string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"આ ઍપ માટે વેબ લિંક ખોલવાની રીત પસંદ કરો"</string>
<string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"ઍપમાં"</string>
diff --git a/libs/WindowManager/Shell/res/values-hi/strings.xml b/libs/WindowManager/Shell/res/values-hi/strings.xml
index 985dafffa68a..c2eb2b207740 100644
--- a/libs/WindowManager/Shell/res/values-hi/strings.xml
+++ b/libs/WindowManager/Shell/res/values-hi/strings.xml
@@ -100,8 +100,7 @@
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"क्या समस्या ठीक नहीं हुई?\nपहले जैसा करने के लिए टैप करें"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"क्या कैमरे से जुड़ी कोई समस्या नहीं है? खारिज करने के लिए टैप करें."</string>
<string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"ऐप्लिकेशन मेन्यू यहां पाया जा सकता है"</string>
- <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
- <skip />
+ <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"एक साथ कई ऐप्लिकेशन खोलने के लिए, डेस्कटॉप विंडोविंग का इस्तेमाल करें"</string>
<string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"ऐप्लिकेशन मेन्यू से फ़ुल स्क्रीन मोड पर किसी भी समय वापस जाएं"</string>
<string name="letterbox_education_dialog_title" msgid="7739895354143295358">"पूरी जानकारी लेकर, बेहतर तरीके से काम करें"</string>
<string name="letterbox_education_split_screen_text" msgid="449233070804658627">"स्प्लिट स्क्रीन का इस्तेमाल करने के लिए, किसी अन्य ऐप्लिकेशन को खींचें और छोड़ें"</string>
@@ -122,8 +121,7 @@
<string name="handle_text" msgid="4419667835599523257">"ऐप्लिकेशन का हैंडल"</string>
<string name="app_icon_text" msgid="2823268023931811747">"ऐप्लिकेशन आइकॉन"</string>
<string name="fullscreen_text" msgid="1162316685217676079">"फ़ुलस्क्रीन"</string>
- <!-- no translation found for desktop_text (9058641752519570266) -->
- <skip />
+ <string name="desktop_text" msgid="9058641752519570266">"डेस्कटॉप विंडोविंग"</string>
<string name="split_screen_text" msgid="1396336058129570886">"स्प्लिट स्क्रीन मोड"</string>
<string name="more_button_text" msgid="3655388105592893530">"ज़्यादा देखें"</string>
<string name="float_button_text" msgid="9221657008391364581">"फ़्लोट"</string>
@@ -134,10 +132,11 @@
<string name="new_window_text" msgid="6318648868380652280">"नई विंडो"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"विंडो मैनेज करें"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"आसपेक्ट रेशियो (लंबाई-चौड़ाई का अनुपात) बदलें"</string>
+ <!-- no translation found for handle_menu_restart_text (3907767216238298098) -->
+ <skip />
<string name="close_text" msgid="4986518933445178928">"बंद करें"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"मेन्यू बंद करें"</string>
- <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
- <skip />
+ <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (डेस्कटॉप विंडोविंग)"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"स्क्रीन को बड़ा करें"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"साइज़ बदलें"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"ऐप्लिकेशन को यहां मूव नहीं किया जा सकता"</string>
diff --git a/libs/WindowManager/Shell/res/values-hr/strings.xml b/libs/WindowManager/Shell/res/values-hr/strings.xml
index 4640bf3b8293..41315fe466c2 100644
--- a/libs/WindowManager/Shell/res/values-hr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-hr/strings.xml
@@ -100,8 +100,7 @@
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Problem nije riješen?\nDodirnite za vraćanje"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Nemate problema s fotoaparatom? Dodirnite za odbacivanje."</string>
<string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Izbornik aplikacije možete pronaći ovdje"</string>
- <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
- <skip />
+ <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"Otvorite prikaz u prozorima na računalu da biste otvorili više aplikacija zajedno"</string>
<string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Vratite se na cijeli zaslon bilo kad iz izbornika aplikacije"</string>
<string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Gledajte i učinite više"</string>
<string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Povucite drugu aplikaciju unutra da biste podijelili zaslon"</string>
@@ -122,8 +121,7 @@
<string name="handle_text" msgid="4419667835599523257">"Pokazivač aplikacije"</string>
<string name="app_icon_text" msgid="2823268023931811747">"Ikona aplikacije"</string>
<string name="fullscreen_text" msgid="1162316685217676079">"Puni zaslon"</string>
- <!-- no translation found for desktop_text (9058641752519570266) -->
- <skip />
+ <string name="desktop_text" msgid="9058641752519570266">"Prikaz u prozorima na računalu"</string>
<string name="split_screen_text" msgid="1396336058129570886">"Razdvojeni zaslon"</string>
<string name="more_button_text" msgid="3655388105592893530">"Više"</string>
<string name="float_button_text" msgid="9221657008391364581">"Plutajući"</string>
@@ -134,10 +132,11 @@
<string name="new_window_text" msgid="6318648868380652280">"Novi prozor"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"Upravljanje prozorima"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Promijeni omjer slike"</string>
+ <!-- no translation found for handle_menu_restart_text (3907767216238298098) -->
+ <skip />
<string name="close_text" msgid="4986518933445178928">"Zatvorite"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Zatvorite izbornik"</string>
- <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
- <skip />
+ <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (prikaz u prozorima na računalu)"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maksimalno povećaj zaslon"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Promijeni veličinu"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Aplikacija se ne može premjestiti ovdje"</string>
diff --git a/libs/WindowManager/Shell/res/values-hu/strings.xml b/libs/WindowManager/Shell/res/values-hu/strings.xml
index 711e2f0bfbbe..0ebf2cb23946 100644
--- a/libs/WindowManager/Shell/res/values-hu/strings.xml
+++ b/libs/WindowManager/Shell/res/values-hu/strings.xml
@@ -100,8 +100,7 @@
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Nem sikerült a hiba kijavítása?\nKoppintson a visszaállításhoz."</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Nincsenek problémái kamerával? Koppintson az elvetéshez."</string>
<string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Az alkalmazásmenü itt található"</string>
- <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
- <skip />
+ <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"Asztali ablakkezelési módba lépve egyidejűleg több alkalmazást is megnyithat"</string>
<string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Az alkalmazásmenüből bármikor visszatérhet a teljes képernyőre"</string>
<string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Több mindent láthat és tehet"</string>
<string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Húzzon ide egy másik alkalmazást az osztott képernyő használatához"</string>
@@ -122,8 +121,7 @@
<string name="handle_text" msgid="4419667835599523257">"App fogópontja"</string>
<string name="app_icon_text" msgid="2823268023931811747">"Alkalmazásikon"</string>
<string name="fullscreen_text" msgid="1162316685217676079">"Teljes képernyő"</string>
- <!-- no translation found for desktop_text (9058641752519570266) -->
- <skip />
+ <string name="desktop_text" msgid="9058641752519570266">"Asztali ablakkezelési mód"</string>
<string name="split_screen_text" msgid="1396336058129570886">"Osztott képernyő"</string>
<string name="more_button_text" msgid="3655388105592893530">"Továbbiak"</string>
<string name="float_button_text" msgid="9221657008391364581">"Lebegő"</string>
@@ -134,10 +132,11 @@
<string name="new_window_text" msgid="6318648868380652280">"Új ablak"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"Ablakok kezelése"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Méretarány módosítása"</string>
+ <!-- no translation found for handle_menu_restart_text (3907767216238298098) -->
+ <skip />
<string name="close_text" msgid="4986518933445178928">"Bezárás"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Menü bezárása"</string>
- <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
- <skip />
+ <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (Asztali ablakkezelési mód)"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Képernyő méretének maximalizálása"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Átméretezés"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Az alkalmazás nem helyezhető át ide"</string>
diff --git a/libs/WindowManager/Shell/res/values-hy/strings.xml b/libs/WindowManager/Shell/res/values-hy/strings.xml
index d7b1b07b6917..a792f5bd8844 100644
--- a/libs/WindowManager/Shell/res/values-hy/strings.xml
+++ b/libs/WindowManager/Shell/res/values-hy/strings.xml
@@ -100,8 +100,7 @@
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Չհաջողվե՞ց շտկել։\nՀպեք՝ փոփոխությունները չեղարկելու համար։"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Տեսախցիկի հետ կապված խնդիրներ չկա՞ն։ Փակելու համար հպեք։"</string>
<string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Հավելվածի ընտրացանկն այստեղ է"</string>
- <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
- <skip />
+ <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"Անցեք համակարգչային պատուհաններին՝ միաժամանակ մի քանի հավելված բացելու համար"</string>
<string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Ցանկացած ժամանակ հավելվածի ընտրացանկից վերադարձեք լիաէկրան ռեժիմ"</string>
<string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Միաժամանակ կատարեք մի քանի առաջադրանք"</string>
<string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Քաշեք մյուս հավելվածի մեջ՝ էկրանի տրոհումն օգտագործելու համար"</string>
@@ -122,8 +121,7 @@
<string name="handle_text" msgid="4419667835599523257">"Հավելվածի կեղծանուն"</string>
<string name="app_icon_text" msgid="2823268023931811747">"Հավելվածի պատկերակ"</string>
<string name="fullscreen_text" msgid="1162316685217676079">"Լիաէկրան"</string>
- <!-- no translation found for desktop_text (9058641752519570266) -->
- <skip />
+ <string name="desktop_text" msgid="9058641752519570266">"Համակարգչային պատուհաններ"</string>
<string name="split_screen_text" msgid="1396336058129570886">"Տրոհված էկրան"</string>
<string name="more_button_text" msgid="3655388105592893530">"Ավելին"</string>
<string name="float_button_text" msgid="9221657008391364581">"Լողացող պատուհան"</string>
@@ -134,10 +132,11 @@
<string name="new_window_text" msgid="6318648868380652280">"Նոր պատուհան"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"Կառավարել պատուհանները"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Փոխել կողմերի հարաբերակցությունը"</string>
+ <!-- no translation found for handle_menu_restart_text (3907767216238298098) -->
+ <skip />
<string name="close_text" msgid="4986518933445178928">"Փակել"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Փակել ընտրացանկը"</string>
- <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
- <skip />
+ <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (համակարգչային պատուհաններ)"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Ծավալել էկրանը"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Փոխել չափը"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Հավելվածը հնարավոր չէ տեղափոխել այստեղ"</string>
diff --git a/libs/WindowManager/Shell/res/values-in/strings.xml b/libs/WindowManager/Shell/res/values-in/strings.xml
index 1d1927f9dfbc..720104511ce6 100644
--- a/libs/WindowManager/Shell/res/values-in/strings.xml
+++ b/libs/WindowManager/Shell/res/values-in/strings.xml
@@ -100,8 +100,7 @@
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Tidak dapat diperbaiki?\nKetuk untuk mengembalikan"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Tidak ada masalah kamera? Ketuk untuk menutup."</string>
<string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Menu aplikasi dapat ditemukan di sini"</string>
- <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
- <skip />
+ <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"Masuk ke mode jendela desktop untuk membuka beberapa aplikasi secara bersamaan"</string>
<string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Kembali ke layar penuh kapan saja dari menu aplikasi"</string>
<string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Lihat dan lakukan lebih banyak hal"</string>
<string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Tarik aplikasi lain untuk menggunakan layar terpisah"</string>
@@ -122,8 +121,7 @@
<string name="handle_text" msgid="4419667835599523257">"Penanganan aplikasi"</string>
<string name="app_icon_text" msgid="2823268023931811747">"Ikon Aplikasi"</string>
<string name="fullscreen_text" msgid="1162316685217676079">"Layar Penuh"</string>
- <!-- no translation found for desktop_text (9058641752519570266) -->
- <skip />
+ <string name="desktop_text" msgid="9058641752519570266">"Mode jendela desktop"</string>
<string name="split_screen_text" msgid="1396336058129570886">"Layar Terpisah"</string>
<string name="more_button_text" msgid="3655388105592893530">"Lainnya"</string>
<string name="float_button_text" msgid="9221657008391364581">"Mengambang"</string>
@@ -134,10 +132,11 @@
<string name="new_window_text" msgid="6318648868380652280">"Jendela Baru"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"Kelola Jendela"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Ubah rasio aspek"</string>
+ <!-- no translation found for handle_menu_restart_text (3907767216238298098) -->
+ <skip />
<string name="close_text" msgid="4986518933445178928">"Tutup"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Tutup Menu"</string>
- <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
- <skip />
+ <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (Mode jendela desktop)"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Perbesar Layar"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Ubah ukuran"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Aplikasi tidak dapat dipindahkan ke sini"</string>
diff --git a/libs/WindowManager/Shell/res/values-is/strings.xml b/libs/WindowManager/Shell/res/values-is/strings.xml
index e94a4c8a08ea..8488c308e725 100644
--- a/libs/WindowManager/Shell/res/values-is/strings.xml
+++ b/libs/WindowManager/Shell/res/values-is/strings.xml
@@ -100,8 +100,7 @@
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Ennþá vesen?\nÝttu til að afturkalla"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Ekkert myndavélavesen? Ýttu til að hunsa."</string>
<string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Hér finnurðu forritavalmyndina"</string>
- <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
- <skip />
+ <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"Kveiktu á gluggastillingu í tölvu til að opna mörg forrit samtímis"</string>
<string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Þú getur skipt aftur í allan skjáinn hvenær sem er af forritavalmyndinni"</string>
<string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Sjáðu og gerðu meira"</string>
<string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Dragðu annað forrit inn til að nota skjáskiptingu"</string>
@@ -122,8 +121,7 @@
<string name="handle_text" msgid="4419667835599523257">"Handfang forrits"</string>
<string name="app_icon_text" msgid="2823268023931811747">"Tákn forrits"</string>
<string name="fullscreen_text" msgid="1162316685217676079">"Allur skjárinn"</string>
- <!-- no translation found for desktop_text (9058641752519570266) -->
- <skip />
+ <string name="desktop_text" msgid="9058641752519570266">"Gluggastilling í tölvu"</string>
<string name="split_screen_text" msgid="1396336058129570886">"Skjáskipting"</string>
<string name="more_button_text" msgid="3655388105592893530">"Meira"</string>
<string name="float_button_text" msgid="9221657008391364581">"Reikult"</string>
@@ -134,10 +132,11 @@
<string name="new_window_text" msgid="6318648868380652280">"Nýr gluggi"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"Stjórna gluggum"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Breyta myndhlutfalli"</string>
+ <!-- no translation found for handle_menu_restart_text (3907767216238298098) -->
+ <skip />
<string name="close_text" msgid="4986518933445178928">"Loka"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Loka valmynd"</string>
- <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
- <skip />
+ <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (gluggastilling í tölvu)"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Stækka skjá"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Breyta stærð"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Ekki er hægt að færa forritið hingað"</string>
diff --git a/libs/WindowManager/Shell/res/values-it/strings.xml b/libs/WindowManager/Shell/res/values-it/strings.xml
index 5f8334fa011c..4fc60ddc51f8 100644
--- a/libs/WindowManager/Shell/res/values-it/strings.xml
+++ b/libs/WindowManager/Shell/res/values-it/strings.xml
@@ -100,8 +100,7 @@
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Il problema non si è risolto?\nTocca per ripristinare"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Nessun problema con la fotocamera? Tocca per ignorare."</string>
<string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Il menu dell\'app si trova qui"</string>
- <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
- <skip />
+ <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"Attiva il windowing del desktop per aprire più app contemporaneamente"</string>
<string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Torna allo schermo intero in qualsiasi momento dal menu dell\'app"</string>
<string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Visualizza più contenuti e fai di più"</string>
<string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Trascina in un\'altra app per usare lo schermo diviso"</string>
@@ -122,8 +121,7 @@
<string name="handle_text" msgid="4419667835599523257">"Punto di manipolazione app"</string>
<string name="app_icon_text" msgid="2823268023931811747">"Icona dell\'app"</string>
<string name="fullscreen_text" msgid="1162316685217676079">"Schermo intero"</string>
- <!-- no translation found for desktop_text (9058641752519570266) -->
- <skip />
+ <string name="desktop_text" msgid="9058641752519570266">"Windowing del desktop"</string>
<string name="split_screen_text" msgid="1396336058129570886">"Schermo diviso"</string>
<string name="more_button_text" msgid="3655388105592893530">"Altro"</string>
<string name="float_button_text" msgid="9221657008391364581">"Mobile"</string>
@@ -134,10 +132,11 @@
<string name="new_window_text" msgid="6318648868380652280">"Nuova finestra"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"Gestisci finestre"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Cambia proporzioni"</string>
+ <!-- no translation found for handle_menu_restart_text (3907767216238298098) -->
+ <skip />
<string name="close_text" msgid="4986518933445178928">"Chiudi"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Chiudi il menu"</string>
- <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
- <skip />
+ <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (windowing del desktop)"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Massimizza schermo"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Ridimensiona"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Impossibile spostare l\'app qui"</string>
diff --git a/libs/WindowManager/Shell/res/values-iw/strings.xml b/libs/WindowManager/Shell/res/values-iw/strings.xml
index c3e85230c319..be14b0081451 100644
--- a/libs/WindowManager/Shell/res/values-iw/strings.xml
+++ b/libs/WindowManager/Shell/res/values-iw/strings.xml
@@ -100,8 +100,7 @@
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"הבעיה לא נפתרה?\nאפשר ללחוץ כדי לחזור לגרסה הקודמת"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"אין בעיות במצלמה? אפשר ללחוץ כדי לסגור."</string>
<string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"תפריט האפליקציה נמצא כאן"</string>
- <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
- <skip />
+ <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"‏כדי לפתוח כמה אפליקציות יחד, צריך להיכנס למצב \"שינוי דינמי של חלונות במחשב\" (desktop windowing)"</string>
<string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"אפשר לחזור למסך מלא בכל שלב מתפריט האפליקציה"</string>
<string name="letterbox_education_dialog_title" msgid="7739895354143295358">"רוצה לראות ולעשות יותר?"</string>
<string name="letterbox_education_split_screen_text" msgid="449233070804658627">"צריך לגרור אפליקציה אחרת כדי להשתמש במסך המפוצל"</string>
@@ -122,8 +121,7 @@
<string name="handle_text" msgid="4419667835599523257">"נקודת אחיזה לאפליקציה"</string>
<string name="app_icon_text" msgid="2823268023931811747">"סמל האפליקציה"</string>
<string name="fullscreen_text" msgid="1162316685217676079">"מסך מלא"</string>
- <!-- no translation found for desktop_text (9058641752519570266) -->
- <skip />
+ <string name="desktop_text" msgid="9058641752519570266">"שינוי דינמי של חלונות במחשב"</string>
<string name="split_screen_text" msgid="1396336058129570886">"מסך מפוצל"</string>
<string name="more_button_text" msgid="3655388105592893530">"עוד"</string>
<string name="float_button_text" msgid="9221657008391364581">"בלונים"</string>
@@ -134,10 +132,11 @@
<string name="new_window_text" msgid="6318648868380652280">"חלון חדש"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"ניהול החלונות"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"שינוי יחס הגובה-רוחב"</string>
+ <!-- no translation found for handle_menu_restart_text (3907767216238298098) -->
+ <skip />
<string name="close_text" msgid="4986518933445178928">"סגירה"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"סגירת התפריט"</string>
- <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
- <skip />
+ <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"‫<xliff:g id="APP_NAME">%1$s</xliff:g> (שינוי דינמי של חלונות במחשב)"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"הגדלת המסך"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"שינוי הגודל"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"לא ניתן להעביר את האפליקציה לכאן"</string>
diff --git a/libs/WindowManager/Shell/res/values-ja/strings.xml b/libs/WindowManager/Shell/res/values-ja/strings.xml
index 1a73dd3b7715..761df5cc560c 100644
--- a/libs/WindowManager/Shell/res/values-ja/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ja/strings.xml
@@ -100,8 +100,7 @@
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"修正されなかった場合は、\nタップすると元に戻ります"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"カメラに関する問題でない場合は、タップすると閉じます。"</string>
<string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"アプリメニューはここにあります"</string>
- <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
- <skip />
+ <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"デスクトップ ウィンドウに切り替えて複数のアプリを同時に開けます"</string>
<string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"アプリメニューからいつでも全画面表示に戻れます"</string>
<string name="letterbox_education_dialog_title" msgid="7739895354143295358">"表示を拡大して機能を強化"</string>
<string name="letterbox_education_split_screen_text" msgid="449233070804658627">"分割画面にするにはもう 1 つのアプリをドラッグしてください"</string>
@@ -122,8 +121,7 @@
<string name="handle_text" msgid="4419667835599523257">"アプリハンドル"</string>
<string name="app_icon_text" msgid="2823268023931811747">"アプリのアイコン"</string>
<string name="fullscreen_text" msgid="1162316685217676079">"全画面表示"</string>
- <!-- no translation found for desktop_text (9058641752519570266) -->
- <skip />
+ <string name="desktop_text" msgid="9058641752519570266">"デスクトップ ウィンドウ"</string>
<string name="split_screen_text" msgid="1396336058129570886">"分割画面"</string>
<string name="more_button_text" msgid="3655388105592893530">"その他"</string>
<string name="float_button_text" msgid="9221657008391364581">"フローティング"</string>
@@ -134,10 +132,11 @@
<string name="new_window_text" msgid="6318648868380652280">"新しいウィンドウ"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"ウィンドウを管理する"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"アスペクト比を変更"</string>
+ <!-- no translation found for handle_menu_restart_text (3907767216238298098) -->
+ <skip />
<string name="close_text" msgid="4986518933445178928">"閉じる"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"メニューを閉じる"</string>
- <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
- <skip />
+ <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g>(デスクトップ ウィンドウ)"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"画面の最大化"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"サイズ変更"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"アプリはここに移動できません"</string>
diff --git a/libs/WindowManager/Shell/res/values-ka/strings.xml b/libs/WindowManager/Shell/res/values-ka/strings.xml
index 4bbfaefb36a2..d5c44fb8a963 100644
--- a/libs/WindowManager/Shell/res/values-ka/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ka/strings.xml
@@ -100,8 +100,7 @@
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"არ გამოსწორდა?\nშეეხეთ წინა ვერსიის დასაბრუნებლად"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"კამერას პრობლემები არ აქვს? შეეხეთ უარყოფისთვის."</string>
<string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"აპის მენიუ შეგიძლიათ იხილოთ აქ"</string>
- <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
- <skip />
+ <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"რამდენიმე აპის ერთდროულად გასახსნელად შედით დესკტოპის ფანჯრის რეჟიმში"</string>
<string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"სრულ ეკრანზე ნებისმიერ დროს შეგიძლიათ დაბრუნდეთ აპის მენიუდან"</string>
<string name="letterbox_education_dialog_title" msgid="7739895354143295358">"მეტის ნახვა და გაკეთება"</string>
<string name="letterbox_education_split_screen_text" msgid="449233070804658627">"ეკრანის გასაყოფად ჩავლებით გადაიტანეთ სხვა აპში"</string>
@@ -122,8 +121,7 @@
<string name="handle_text" msgid="4419667835599523257">"აპის იდენტიფიკატორი"</string>
<string name="app_icon_text" msgid="2823268023931811747">"აპის ხატულა"</string>
<string name="fullscreen_text" msgid="1162316685217676079">"სრულ ეკრანზე"</string>
- <!-- no translation found for desktop_text (9058641752519570266) -->
- <skip />
+ <string name="desktop_text" msgid="9058641752519570266">"დესკტოპის ფანჯრის რეჟიმი"</string>
<string name="split_screen_text" msgid="1396336058129570886">"ეკრანის გაყოფა"</string>
<string name="more_button_text" msgid="3655388105592893530">"სხვა"</string>
<string name="float_button_text" msgid="9221657008391364581">"ფარფატი"</string>
@@ -134,10 +132,11 @@
<string name="new_window_text" msgid="6318648868380652280">"ახალი ფანჯარა"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"ფანჯრების მართვა"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"თანაფარდობის შეცვლა"</string>
+ <!-- no translation found for handle_menu_restart_text (3907767216238298098) -->
+ <skip />
<string name="close_text" msgid="4986518933445178928">"დახურვა"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"მენიუს დახურვა"</string>
- <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
- <skip />
+ <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (დესკტოპის ფანჯრის რეჟიმი)"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"აპლიკაციის გაშლა სრულ ეკრანზე"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"ზომის შეცვლა"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"აპის აქ გადატანა შეუძლებელია"</string>
diff --git a/libs/WindowManager/Shell/res/values-kk/strings.xml b/libs/WindowManager/Shell/res/values-kk/strings.xml
index 2f6404d4680d..3a9711e2337a 100644
--- a/libs/WindowManager/Shell/res/values-kk/strings.xml
+++ b/libs/WindowManager/Shell/res/values-kk/strings.xml
@@ -100,8 +100,7 @@
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Жөнделмеді ме?\nҚайтару үшін түртіңіз."</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Камерада қателер шықпады ма? Жабу үшін түртіңіз."</string>
<string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Қолданба мәзірін осы жерден табуға болады."</string>
- <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
- <skip />
+ <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"Бірнеше қолданбаны бірге ашу үшін компьютерлік терезелер режиміне кіріңіз."</string>
<string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Қолданба мәзірінен кез келген уақытта толық экранға оралыңыз."</string>
<string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Қосымша ақпаратты қарап, әрекеттер жасау"</string>
<string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Экранды бөлу үшін басқа қолданбаға өтіңіз."</string>
@@ -122,8 +121,7 @@
<string name="handle_text" msgid="4419667835599523257">"Қолданба идентификаторы"</string>
<string name="app_icon_text" msgid="2823268023931811747">"Қолданба белгішесі"</string>
<string name="fullscreen_text" msgid="1162316685217676079">"Толық экран"</string>
- <!-- no translation found for desktop_text (9058641752519570266) -->
- <skip />
+ <string name="desktop_text" msgid="9058641752519570266">"Компьютерлік терезелер режимі"</string>
<string name="split_screen_text" msgid="1396336058129570886">"Экранды бөлу"</string>
<string name="more_button_text" msgid="3655388105592893530">"Қосымша"</string>
<string name="float_button_text" msgid="9221657008391364581">"Қалқыма"</string>
@@ -134,10 +132,11 @@
<string name="new_window_text" msgid="6318648868380652280">"Жаңа терезе"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"Терезелерді басқару"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Арақатынасты өзгерту"</string>
+ <!-- no translation found for handle_menu_restart_text (3907767216238298098) -->
+ <skip />
<string name="close_text" msgid="4986518933445178928">"Жабу"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Мәзірді жабу"</string>
- <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
- <skip />
+ <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (компьютерлік терезелер режимі)"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Экранды ұлғайту"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Өлшемін өзгерту"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Қолданба бұл жерге қойылмайды."</string>
diff --git a/libs/WindowManager/Shell/res/values-km/strings.xml b/libs/WindowManager/Shell/res/values-km/strings.xml
index d3942fdc1e6c..c4b5a9b2009f 100644
--- a/libs/WindowManager/Shell/res/values-km/strings.xml
+++ b/libs/WindowManager/Shell/res/values-km/strings.xml
@@ -100,8 +100,7 @@
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"មិនបាន​ដោះស្រាយ​បញ្ហានេះទេឬ?\nចុចដើម្បី​ត្រឡប់"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"មិនមាន​បញ្ហាពាក់ព័ន្ធនឹង​កាមេរ៉ាទេឬ? ចុចដើម្បី​ច្រានចោល។"</string>
<string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"អាចរកឃើញម៉ឺនុយកម្មវិធីនៅទីនេះ"</string>
- <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
- <skip />
+ <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"ចូលមុខងារវិនដូកុំព្យូទ័រ ដើម្បីបើកកម្មវិធីច្រើនជាមួយគ្នា"</string>
<string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"ត្រឡប់ទៅអេក្រង់ពេញវិញនៅពេលណាក៏បានពីម៉ឺនុយកម្មវិធី"</string>
<string name="letterbox_education_dialog_title" msgid="7739895354143295358">"មើលឃើញ និងធ្វើបានកាន់តែច្រើន"</string>
<string name="letterbox_education_split_screen_text" msgid="449233070804658627">"អូស​កម្មវិធី​មួយ​ទៀត​ចូល ដើម្បី​ប្រើ​មុខងារ​បំបែកអេក្រង់"</string>
@@ -122,8 +121,7 @@
<string name="handle_text" msgid="4419667835599523257">"ឈ្មោះអ្នកប្រើប្រាស់កម្មវិធី"</string>
<string name="app_icon_text" msgid="2823268023931811747">"រូប​កម្មវិធី"</string>
<string name="fullscreen_text" msgid="1162316685217676079">"អេក្រង់​ពេញ"</string>
- <!-- no translation found for desktop_text (9058641752519570266) -->
- <skip />
+ <string name="desktop_text" msgid="9058641752519570266">"មុខងារវិនដូកុំព្យូទ័រ"</string>
<string name="split_screen_text" msgid="1396336058129570886">"មុខងារ​បំបែក​អេក្រង់"</string>
<string name="more_button_text" msgid="3655388105592893530">"ច្រើនទៀត"</string>
<string name="float_button_text" msgid="9221657008391364581">"អណ្ដែត"</string>
@@ -134,10 +132,11 @@
<string name="new_window_text" msgid="6318648868380652280">"វិនដូ​ថ្មី"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"គ្រប់គ្រង​វិនដូ"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"ប្ដូរ​​សមាមាត្រ"</string>
+ <!-- no translation found for handle_menu_restart_text (3907767216238298098) -->
+ <skip />
<string name="close_text" msgid="4986518933445178928">"បិទ"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"បិទ​ម៉ឺនុយ"</string>
- <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
- <skip />
+ <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (មុខងារវិនដូកុំព្យូទ័រ)"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"ពង្រីកអេក្រង់"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"ប្ដូរ​ទំហំ"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"មិនអាចផ្លាស់ទីកម្មវិធីមកទីនេះបានទេ"</string>
diff --git a/libs/WindowManager/Shell/res/values-kn/strings.xml b/libs/WindowManager/Shell/res/values-kn/strings.xml
index fb15e34d934b..9d3ec2a347af 100644
--- a/libs/WindowManager/Shell/res/values-kn/strings.xml
+++ b/libs/WindowManager/Shell/res/values-kn/strings.xml
@@ -100,8 +100,7 @@
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"ಅದನ್ನು ಸರಿಪಡಿಸಲಿಲ್ಲವೇ?\nಹಿಂತಿರುಗಿಸಲು ಟ್ಯಾಪ್ ಮಾಡಿ"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"ಕ್ಯಾಮರಾ ಸಮಸ್ಯೆಗಳಿಲ್ಲವೇ? ವಜಾಗೊಳಿಸಲು ಟ್ಯಾಪ್ ಮಾಡಿ."</string>
<string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"ಆ್ಯಪ್ ಮೆನುವನ್ನು ಇಲ್ಲಿ ಕಾಣಬಹುದು"</string>
- <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
- <skip />
+ <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"ಹಲವು ಆ್ಯಪ್‌ಗಳನ್ನು ಒಟ್ಟಿಗೆ ತೆರೆಯಲು ಡೆಸ್ಕ್‌ಟಾಪ್ ವಿಂಡೋಯಿಂಗ್ ಅನ್ನು ನಮೂದಿಸಿ"</string>
<string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"ಆ್ಯಪ್ ಮೆನುವಿನಿಂದ ಯಾವಾಗ ಬೇಕಾದರೂ ಫುಲ್‌ಸ್ಕ್ರೀನ್‌ಗೆ ಹಿಂತಿರುಗಿ"</string>
<string name="letterbox_education_dialog_title" msgid="7739895354143295358">"ನೋಡಿ ಮತ್ತು ಹೆಚ್ಚಿನದನ್ನು ಮಾಡಿ"</string>
<string name="letterbox_education_split_screen_text" msgid="449233070804658627">"ಸ್ಪ್ಲಿಟ್‌ ಸ್ಕ್ರೀನ್‌ಗಾಗಿ ಮತ್ತೊಂದು ಆ್ಯಪ್‌ನಲ್ಲಿ ಡ್ರ್ಯಾಗ್ ಮಾಡಿ"</string>
@@ -122,8 +121,7 @@
<string name="handle_text" msgid="4419667835599523257">"ಆ್ಯಪ್ ಹ್ಯಾಂಡಲ್"</string>
<string name="app_icon_text" msgid="2823268023931811747">"ಆ್ಯಪ್ ಐಕಾನ್"</string>
<string name="fullscreen_text" msgid="1162316685217676079">"ಫುಲ್‌ಸ್ಕ್ರೀನ್"</string>
- <!-- no translation found for desktop_text (9058641752519570266) -->
- <skip />
+ <string name="desktop_text" msgid="9058641752519570266">"ಡೆಸ್ಕ್‌ಟಾಪ್ ವಿಂಡೋಯಿಂಗ್"</string>
<string name="split_screen_text" msgid="1396336058129570886">"ಸ್ಪ್ಲಿಟ್ ಸ್ಕ್ರೀನ್"</string>
<string name="more_button_text" msgid="3655388105592893530">"ಇನ್ನಷ್ಟು"</string>
<string name="float_button_text" msgid="9221657008391364581">"ಫ್ಲೋಟ್"</string>
@@ -134,10 +132,11 @@
<string name="new_window_text" msgid="6318648868380652280">"ಹೊಸ ವಿಂಡೋ"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"ವಿಂಡೋಗಳನ್ನು ನಿರ್ವಹಿಸಿ"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"ದೃಶ್ಯಾನುಪಾತವನ್ನು ಬದಲಾಯಿಸಿ"</string>
+ <!-- no translation found for handle_menu_restart_text (3907767216238298098) -->
+ <skip />
<string name="close_text" msgid="4986518933445178928">"ಮುಚ್ಚಿ"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"ಮೆನು ಮುಚ್ಚಿ"</string>
- <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
- <skip />
+ <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (ಡೆಸ್ಕ್‌ಟಾಪ್ ವಿಂಡೋಯಿಂಗ್)"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"ಸ್ಕ್ರೀನ್ ಅನ್ನು ಮ್ಯಾಕ್ಸಿಮೈಸ್ ಮಾಡಿ"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"ಮರುಗಾತ್ರಗೊಳಿಸಿ"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"ಆ್ಯಪ್ ಅನ್ನು ಇಲ್ಲಿಗೆ ಸರಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ"</string>
diff --git a/libs/WindowManager/Shell/res/values-ko/strings.xml b/libs/WindowManager/Shell/res/values-ko/strings.xml
index dbfd32a7dcb1..5206b83ef17a 100644
--- a/libs/WindowManager/Shell/res/values-ko/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ko/strings.xml
@@ -100,8 +100,7 @@
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"해결되지 않았나요?\n되돌리려면 탭하세요."</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"카메라에 문제가 없나요? 닫으려면 탭하세요."</string>
<string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"앱 메뉴는 여기에서 찾을 수 있습니다."</string>
- <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
- <skip />
+ <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"데스크톱 윈도윙을 실행하여 여러 앱을 함께 열 수 있습니다."</string>
<string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"언제든지 앱 메뉴에서 전체 화면으로 돌아갈 수 있습니다."</string>
<string name="letterbox_education_dialog_title" msgid="7739895354143295358">"더 많은 정보를 보고 더 많은 작업을 처리하세요"</string>
<string name="letterbox_education_split_screen_text" msgid="449233070804658627">"화면 분할을 사용하려면 다른 앱을 드래그해 가져옵니다."</string>
@@ -122,8 +121,7 @@
<string name="handle_text" msgid="4419667835599523257">"앱 핸들"</string>
<string name="app_icon_text" msgid="2823268023931811747">"앱 아이콘"</string>
<string name="fullscreen_text" msgid="1162316685217676079">"전체 화면"</string>
- <!-- no translation found for desktop_text (9058641752519570266) -->
- <skip />
+ <string name="desktop_text" msgid="9058641752519570266">"데스크톱 윈도잉"</string>
<string name="split_screen_text" msgid="1396336058129570886">"화면 분할"</string>
<string name="more_button_text" msgid="3655388105592893530">"더보기"</string>
<string name="float_button_text" msgid="9221657008391364581">"플로팅"</string>
@@ -134,10 +132,11 @@
<string name="new_window_text" msgid="6318648868380652280">"새 창"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"창 관리"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"가로세로 비율 변경"</string>
+ <!-- no translation found for handle_menu_restart_text (3907767216238298098) -->
+ <skip />
<string name="close_text" msgid="4986518933445178928">"닫기"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"메뉴 닫기"</string>
- <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
- <skip />
+ <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g>(데스크톱 윈도윙)"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"화면 최대화"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"크기 조절"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"앱을 여기로 이동할 수 없음"</string>
diff --git a/libs/WindowManager/Shell/res/values-ky/strings.xml b/libs/WindowManager/Shell/res/values-ky/strings.xml
index 36194cdaaa47..810a63841cfb 100644
--- a/libs/WindowManager/Shell/res/values-ky/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ky/strings.xml
@@ -100,8 +100,7 @@
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Оңдолгон жокпу?\nАртка кайтаруу үчүн таптаңыз"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Камерада маселе жокпу? Этибарга албоо үчүн таптаңыз."</string>
<string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Колдонмонун менюсун ушул жерден таба аласыз"</string>
- <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
- <skip />
+ <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"Бир убакта бир нече колдонмону ачуу үчүн иш тактанын терезелери режимине өтүңүз"</string>
<string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Каалаган убакта колдонмонун менюсунан толук экранга кайта аласыз"</string>
<string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Көрүп, көбүрөөк нерселерди жасаңыз"</string>
<string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Экранды бөлүү үчүн башка колдонмону сүйрөңүз"</string>
@@ -122,8 +121,7 @@
<string name="handle_text" msgid="4419667835599523257">"Колдонмонун маркери"</string>
<string name="app_icon_text" msgid="2823268023931811747">"Колдонмонун сүрөтчөсү"</string>
<string name="fullscreen_text" msgid="1162316685217676079">"Толук экран"</string>
- <!-- no translation found for desktop_text (9058641752519570266) -->
- <skip />
+ <string name="desktop_text" msgid="9058641752519570266">"Иш тактанын терезелери"</string>
<string name="split_screen_text" msgid="1396336058129570886">"Экранды бөлүү"</string>
<string name="more_button_text" msgid="3655388105592893530">"Дагы"</string>
<string name="float_button_text" msgid="9221657008391364581">"Калкыма"</string>
@@ -134,10 +132,11 @@
<string name="new_window_text" msgid="6318648868380652280">"Жаңы терезе"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"Терезелерди тескөө"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Тараптардын катнашын өзгөртүү"</string>
+ <!-- no translation found for handle_menu_restart_text (3907767216238298098) -->
+ <skip />
<string name="close_text" msgid="4986518933445178928">"Жабуу"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Менюну жабуу"</string>
- <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
- <skip />
+ <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (Иш тактанын терезелери)"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Экранды чоңойтуу"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Өлчөмүн өзгөртүү"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Колдонмону бул жерге жылдырууга болбойт"</string>
diff --git a/libs/WindowManager/Shell/res/values-lo/strings.xml b/libs/WindowManager/Shell/res/values-lo/strings.xml
index 671f92447204..7a4fb61bdf0d 100644
--- a/libs/WindowManager/Shell/res/values-lo/strings.xml
+++ b/libs/WindowManager/Shell/res/values-lo/strings.xml
@@ -100,8 +100,7 @@
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"ບໍ່ໄດ້ແກ້ໄຂມັນບໍ?\nແຕະເພື່ອແປງກັບຄືນ"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"ບໍ່ມີບັນຫາກ້ອງຖ່າຍຮູບບໍ? ແຕະເພື່ອ​ປິດ​ໄວ້."</string>
<string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"ສາມາດເບິ່ງເມນູແອັບໄດ້ບ່ອນນີ້"</string>
- <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
- <skip />
+ <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"ເຂົ້າສູ່ໜ້າຈໍເດັສທັອບເພື່ອເປີດຫຼາຍແອັບພ້ອມກັນ"</string>
<string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"ກັບຄືນໄປຫາໂໝດເຕັມຈໍໄດ້ທຸກເວລາຈາກເມນູແອັບ"</string>
<string name="letterbox_education_dialog_title" msgid="7739895354143295358">"ເບິ່ງ ແລະ ເຮັດຫຼາຍຂຶ້ນ"</string>
<string name="letterbox_education_split_screen_text" msgid="449233070804658627">"ລາກໄປໄວ້ໃນແອັບອື່ນເພື່ອແບ່ງໜ້າຈໍ"</string>
@@ -122,8 +121,7 @@
<string name="handle_text" msgid="4419667835599523257">"ຊື່ຜູ້ໃຊ້ແອັບ"</string>
<string name="app_icon_text" msgid="2823268023931811747">"ໄອຄອນແອັບ"</string>
<string name="fullscreen_text" msgid="1162316685217676079">"ເຕັມຈໍ"</string>
- <!-- no translation found for desktop_text (9058641752519570266) -->
- <skip />
+ <string name="desktop_text" msgid="9058641752519570266">"ໜ້າຈໍເດັສທັອບ"</string>
<string name="split_screen_text" msgid="1396336058129570886">"ແບ່ງໜ້າຈໍ"</string>
<string name="more_button_text" msgid="3655388105592893530">"ເພີ່ມເຕີມ"</string>
<string name="float_button_text" msgid="9221657008391364581">"ລອຍ"</string>
@@ -134,10 +132,11 @@
<string name="new_window_text" msgid="6318648868380652280">"ໜ້າຈໍໃໝ່"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"ຈັດການໜ້າຈໍ"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"ປ່ຽນອັດຕາສ່ວນຮູບ"</string>
+ <!-- no translation found for handle_menu_restart_text (3907767216238298098) -->
+ <skip />
<string name="close_text" msgid="4986518933445178928">"ປິດ"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"ປິດເມນູ"</string>
- <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
- <skip />
+ <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (ໜ້າຈໍເດັສທັອບ)"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"ປັບຈໍໃຫຍ່ສຸດ"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"ປັບຂະໜາດ"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"ບໍ່ສາມາດຍ້າຍແອັບມາບ່ອນນີ້ໄດ້"</string>
diff --git a/libs/WindowManager/Shell/res/values-lt/strings.xml b/libs/WindowManager/Shell/res/values-lt/strings.xml
index 794c5ab02c19..75619eaefd17 100644
--- a/libs/WindowManager/Shell/res/values-lt/strings.xml
+++ b/libs/WindowManager/Shell/res/values-lt/strings.xml
@@ -100,8 +100,7 @@
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Nepavyko pataisyti?\nPalieskite, kad grąžintumėte"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Nėra jokių problemų dėl kameros? Palieskite, kad atsisakytumėte."</string>
<string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Programos meniu rasite čia"</string>
- <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
- <skip />
+ <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"Įjunkite versijos staliniams kompiuteriams rodinį, kad galėtumėte vienu metu atidaryti kelias programas"</string>
<string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Bet kada iš programos meniu grįžkite į viso ekrano režimą"</string>
<string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Daugiau turinio ir funkcijų"</string>
<string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Vilkite kitoje programoje, kad galėtumėte naudoti išskaidyto ekrano režimą"</string>
@@ -122,8 +121,7 @@
<string name="handle_text" msgid="4419667835599523257">"Programos kreipinys"</string>
<string name="app_icon_text" msgid="2823268023931811747">"Programos piktograma"</string>
<string name="fullscreen_text" msgid="1162316685217676079">"Visas ekranas"</string>
- <!-- no translation found for desktop_text (9058641752519570266) -->
- <skip />
+ <string name="desktop_text" msgid="9058641752519570266">"Versijos staliniams kompiuteriams rodinys"</string>
<string name="split_screen_text" msgid="1396336058129570886">"Išskaidyto ekrano režimas"</string>
<string name="more_button_text" msgid="3655388105592893530">"Daugiau"</string>
<string name="float_button_text" msgid="9221657008391364581">"Slankusis langas"</string>
@@ -134,10 +132,11 @@
<string name="new_window_text" msgid="6318648868380652280">"Naujas langas"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"Tvarkyti langus"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Keisti kraštinių santykį"</string>
+ <!-- no translation found for handle_menu_restart_text (3907767216238298098) -->
+ <skip />
<string name="close_text" msgid="4986518933445178928">"Uždaryti"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Uždaryti meniu"</string>
- <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
- <skip />
+ <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"„<xliff:g id="APP_NAME">%1$s</xliff:g>“ (versijos staliniams kompiuteriams rodinys)"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Išskleisti ekraną"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Pakeisti dydį"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Programos negalima perkelti čia"</string>
diff --git a/libs/WindowManager/Shell/res/values-lv/strings.xml b/libs/WindowManager/Shell/res/values-lv/strings.xml
index 5b44112c76d5..5eea17c60053 100644
--- a/libs/WindowManager/Shell/res/values-lv/strings.xml
+++ b/libs/WindowManager/Shell/res/values-lv/strings.xml
@@ -100,8 +100,7 @@
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Vai problēma netika novērsta?\nPieskarieties, lai atjaunotu."</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Vai nav problēmu ar kameru? Pieskarieties, lai nerādītu."</string>
<string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Šeit ir pieejama lietotņu izvēlne"</string>
- <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
- <skip />
+ <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"Lai atvērtu vairākas lietotnes vienlaikus, pārejiet uz darbvirsmas logošanu"</string>
<string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"No lietotnes izvēlnes varat jebkurā brīdī atgriezties pilnekrāna režīmā."</string>
<string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Uzziniet un paveiciet vairāk"</string>
<string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Lai izmantotu sadalītu ekrānu, ievelciet vēl vienu lietotni"</string>
@@ -122,8 +121,7 @@
<string name="handle_text" msgid="4419667835599523257">"Lietotnes turis"</string>
<string name="app_icon_text" msgid="2823268023931811747">"Lietotnes ikona"</string>
<string name="fullscreen_text" msgid="1162316685217676079">"Pilnekrāna režīms"</string>
- <!-- no translation found for desktop_text (9058641752519570266) -->
- <skip />
+ <string name="desktop_text" msgid="9058641752519570266">"Darbvirsmas logošana"</string>
<string name="split_screen_text" msgid="1396336058129570886">"Sadalīt ekrānu"</string>
<string name="more_button_text" msgid="3655388105592893530">"Vairāk"</string>
<string name="float_button_text" msgid="9221657008391364581">"Peldošs"</string>
@@ -134,10 +132,11 @@
<string name="new_window_text" msgid="6318648868380652280">"Jauns logs"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"Pārvaldīt logus"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Mainīt malu attiecību"</string>
+ <!-- no translation found for handle_menu_restart_text (3907767216238298098) -->
+ <skip />
<string name="close_text" msgid="4986518933445178928">"Aizvērt"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Aizvērt izvēlni"</string>
- <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
- <skip />
+ <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (darbvirsmas logošana)"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maksimizēt ekrānu"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Mainīt lielumu"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Lietotni nevar pārvietot šeit."</string>
diff --git a/libs/WindowManager/Shell/res/values-mk/strings.xml b/libs/WindowManager/Shell/res/values-mk/strings.xml
index 96bf9b67144e..4653aa2ca7c5 100644
--- a/libs/WindowManager/Shell/res/values-mk/strings.xml
+++ b/libs/WindowManager/Shell/res/values-mk/strings.xml
@@ -100,8 +100,7 @@
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Не се поправи?\nДопрете за враќање"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Нема проблеми со камерата? Допрете за отфрлање."</string>
<string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Менито со апликации може да го најдете овде"</string>
- <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
- <skip />
+ <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"Влезете во режимот со прозорци на работната површина за да отворите повеќе апликации заедно"</string>
<string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Вратете се на цел екран од менито со апликации кога сакате"</string>
<string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Погледнете и направете повеќе"</string>
<string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Повлечете друга апликација за поделен екран"</string>
@@ -122,8 +121,7 @@
<string name="handle_text" msgid="4419667835599523257">"Прекар на апликацијата"</string>
<string name="app_icon_text" msgid="2823268023931811747">"Икона на апликацијата"</string>
<string name="fullscreen_text" msgid="1162316685217676079">"Цел екран"</string>
- <!-- no translation found for desktop_text (9058641752519570266) -->
- <skip />
+ <string name="desktop_text" msgid="9058641752519570266">"Режим со прозорци на работната површина"</string>
<string name="split_screen_text" msgid="1396336058129570886">"Поделен екран"</string>
<string name="more_button_text" msgid="3655388105592893530">"Повеќе"</string>
<string name="float_button_text" msgid="9221657008391364581">"Лебдечко"</string>
@@ -134,10 +132,11 @@
<string name="new_window_text" msgid="6318648868380652280">"Нов прозорец"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"Управувајте со прозорците"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Промени го соодносот"</string>
+ <!-- no translation found for handle_menu_restart_text (3907767216238298098) -->
+ <skip />
<string name="close_text" msgid="4986518933445178928">"Затворете"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Затворете го менито"</string>
- <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
- <skip />
+ <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (режим со прозорци на работната површина)"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Максимизирај го екранот"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Смени големина"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Апликацијата не може да се премести овде"</string>
diff --git a/libs/WindowManager/Shell/res/values-ml/strings.xml b/libs/WindowManager/Shell/res/values-ml/strings.xml
index 8085601076ef..50c2f6e1a8b2 100644
--- a/libs/WindowManager/Shell/res/values-ml/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ml/strings.xml
@@ -100,8 +100,7 @@
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"അത് പരിഹരിച്ചില്ലേ?\nപുനഃസ്ഥാപിക്കാൻ ടാപ്പ് ചെയ്യുക"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"ക്യാമറാ പ്രശ്നങ്ങളൊന്നുമില്ലേ? നിരസിക്കാൻ ടാപ്പ് ചെയ്യുക."</string>
<string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"ആപ്പ് മെനു ഇവിടെ കണ്ടെത്താനാകും"</string>
- <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
- <skip />
+ <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"ഒന്നിലധികം ആപ്പുകൾ ഒരുമിച്ച് തുറക്കാൻ ഡെസ്‌ക്‌‌ടോപ്പ് വിൻഡോയിംഗിൽ പ്രവേശിക്കുക"</string>
<string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"ആപ്പ് മെനുവിൽ നിന്ന് ഏതുസമയത്തും പൂർണ്ണ സ്‌ക്രീനിലേക്ക് മടങ്ങുക"</string>
<string name="letterbox_education_dialog_title" msgid="7739895354143295358">"കൂടുതൽ കാണുക, ചെയ്യുക"</string>
<string name="letterbox_education_split_screen_text" msgid="449233070804658627">"സ്‌ക്രീൻ വിഭജന മോഡിന്, മറ്റൊരു ആപ്പ് വലിച്ചിടുക"</string>
@@ -122,8 +121,7 @@
<string name="handle_text" msgid="4419667835599523257">"ആപ്പ് ഹാൻഡിൽ"</string>
<string name="app_icon_text" msgid="2823268023931811747">"ആപ്പ് ഐക്കൺ"</string>
<string name="fullscreen_text" msgid="1162316685217676079">"പൂർണ്ണസ്ക്രീൻ"</string>
- <!-- no translation found for desktop_text (9058641752519570266) -->
- <skip />
+ <string name="desktop_text" msgid="9058641752519570266">"ഡെസ്ക്ടോപ്പ് വിൻഡോയിംഗ്"</string>
<string name="split_screen_text" msgid="1396336058129570886">"സ്‌ക്രീൻ വിഭജനം"</string>
<string name="more_button_text" msgid="3655388105592893530">"കൂടുതൽ"</string>
<string name="float_button_text" msgid="9221657008391364581">"ഫ്ലോട്ട്"</string>
@@ -134,10 +132,11 @@
<string name="new_window_text" msgid="6318648868380652280">"പുതിയ വിന്‍ഡോ"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"വിൻഡോകൾ മാനേജ് ചെയ്യുക"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"വീക്ഷണ അനുപാതം മാറ്റുക"</string>
+ <!-- no translation found for handle_menu_restart_text (3907767216238298098) -->
+ <skip />
<string name="close_text" msgid="4986518933445178928">"അടയ്ക്കുക"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"മെനു അടയ്ക്കുക"</string>
- <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
- <skip />
+ <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (ഡെസ്ക്ടോപ്പ് വിൻഡോയിംഗ്)"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"സ്‌ക്രീൻ വലുതാക്കുക"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"വലുപ്പം മാറ്റുക"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"ആപ്പ് ഇവിടേക്ക് നീക്കാനാകില്ല"</string>
diff --git a/libs/WindowManager/Shell/res/values-mn/strings.xml b/libs/WindowManager/Shell/res/values-mn/strings.xml
index 5efe62d7d837..80e83a5a3901 100644
--- a/libs/WindowManager/Shell/res/values-mn/strings.xml
+++ b/libs/WindowManager/Shell/res/values-mn/strings.xml
@@ -100,8 +100,7 @@
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Үүнийг засаагүй юу?\nБуцаахын тулд товшино уу"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Камерын асуудал байхгүй юу? Хаахын тулд товшино уу."</string>
<string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Аппын цэсийг эндээс олох боломжтой"</string>
- <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
- <skip />
+ <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"Олон аппыг хамтад нь нээхийн тулд дэлгэцийн цонх үүсгэх гэж орно уу"</string>
<string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Аппын цэсээс бүтэн дэлгэц рүү хүссэн үедээ буцна уу"</string>
<string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Харж илүү ихийг хий"</string>
<string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Дэлгэц хуваах горимд ашиглахын тулд өөр аппыг чирнэ үү"</string>
@@ -122,8 +121,7 @@
<string name="handle_text" msgid="4419667835599523257">"Аппын бариул"</string>
<string name="app_icon_text" msgid="2823268023931811747">"Aппын дүрс тэмдэг"</string>
<string name="fullscreen_text" msgid="1162316685217676079">"Бүтэн дэлгэц"</string>
- <!-- no translation found for desktop_text (9058641752519570266) -->
- <skip />
+ <string name="desktop_text" msgid="9058641752519570266">"Дэлгэцийн цонх үүсгэх онцлог"</string>
<string name="split_screen_text" msgid="1396336058129570886">"Дэлгэцийг хуваах"</string>
<string name="more_button_text" msgid="3655388105592893530">"Бусад"</string>
<string name="float_button_text" msgid="9221657008391364581">"Хөвөгч"</string>
@@ -134,10 +132,11 @@
<string name="new_window_text" msgid="6318648868380652280">"Шинэ цонх"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"Цонхнуудыг удирдах"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Аспектын харьцааг өөрчлөх"</string>
+ <!-- no translation found for handle_menu_restart_text (3907767216238298098) -->
+ <skip />
<string name="close_text" msgid="4986518933445178928">"Хаах"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Цэсийг хаах"</string>
- <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
- <skip />
+ <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (Дэлгэцийн цонх үүсгэх онцлог)"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Дэлгэцийг томруулах"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Хэмжээг өөрчлөх"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Аппыг ийш зөөх боломжгүй"</string>
diff --git a/libs/WindowManager/Shell/res/values-mr/strings.xml b/libs/WindowManager/Shell/res/values-mr/strings.xml
index e10c8d82440d..deb0bafa2058 100644
--- a/libs/WindowManager/Shell/res/values-mr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-mr/strings.xml
@@ -100,8 +100,7 @@
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"निराकरण झाले नाही?\nरिव्हर्ट करण्यासाठी कृपया टॅप करा"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"कॅमेराशी संबंधित कोणत्याही समस्या नाहीत का? डिसमिस करण्‍यासाठी टॅप करा."</string>
<string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"ॲप मेनू इथे आढळू शकतो"</string>
- <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
- <skip />
+ <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"एकाहून अधिक ॲप्स एकत्र उघडण्यासाठी डेस्कटॉप विंडोइंग एंटर करा"</string>
<string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"ॲप मेनूमधून कधीही फुल स्क्रीनवर परत या"</string>
<string name="letterbox_education_dialog_title" msgid="7739895354143295358">"पहा आणि आणखी बरेच काही करा"</string>
<string name="letterbox_education_split_screen_text" msgid="449233070804658627">"स्प्लिट स्क्रीन वापरण्यासाठी दुसरे ॲप ड्रॅग करा"</string>
@@ -122,8 +121,7 @@
<string name="handle_text" msgid="4419667835599523257">"अ‍ॅपचे हँडल"</string>
<string name="app_icon_text" msgid="2823268023931811747">"अ‍ॅप आयकन"</string>
<string name="fullscreen_text" msgid="1162316685217676079">"फुलस्‍क्रीन"</string>
- <!-- no translation found for desktop_text (9058641752519570266) -->
- <skip />
+ <string name="desktop_text" msgid="9058641752519570266">"डेस्कटॉप विंडोइंग"</string>
<string name="split_screen_text" msgid="1396336058129570886">"स्प्लिट स्क्रीन"</string>
<string name="more_button_text" msgid="3655388105592893530">"आणखी"</string>
<string name="float_button_text" msgid="9221657008391364581">"फ्लोट"</string>
@@ -134,10 +132,11 @@
<string name="new_window_text" msgid="6318648868380652280">"नवीन विंडो"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"विंडो व्यवस्थापित करा"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"आस्पेक्ट रेशो बदला"</string>
+ <!-- no translation found for handle_menu_restart_text (3907767216238298098) -->
+ <skip />
<string name="close_text" msgid="4986518933445178928">"बंद करा"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"मेनू बंद करा"</string>
- <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
- <skip />
+ <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (डेस्कटॉप विंडोइंग)"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"स्क्रीन मोठी करा"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"आकार बदला"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"अ‍ॅप इथे हलवू शकत नाही"</string>
diff --git a/libs/WindowManager/Shell/res/values-ms/strings.xml b/libs/WindowManager/Shell/res/values-ms/strings.xml
index e18e7ec68640..1f56033e8c97 100644
--- a/libs/WindowManager/Shell/res/values-ms/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ms/strings.xml
@@ -100,8 +100,7 @@
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Isu tidak dibetulkan?\nKetik untuk kembali"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Tiada isu kamera? Ketik untuk mengetepikan."</string>
<string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Menu apl boleh ditemukan di sini"</string>
- <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
- <skip />
+ <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"Masuki tetingkap desktop untuk membuka berbilang apl serentak"</string>
<string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Kembali kepada skrin penuh pada bila-bila masa daripada menu apl"</string>
<string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Lihat dan lakukan lebih"</string>
<string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Seret masuk apl lain untuk menggunakan skrin pisah"</string>
@@ -122,8 +121,7 @@
<string name="handle_text" msgid="4419667835599523257">"Pengendalian apl"</string>
<string name="app_icon_text" msgid="2823268023931811747">"Ikon Apl"</string>
<string name="fullscreen_text" msgid="1162316685217676079">"Skrin penuh"</string>
- <!-- no translation found for desktop_text (9058641752519570266) -->
- <skip />
+ <string name="desktop_text" msgid="9058641752519570266">"Tetingkap desktop"</string>
<string name="split_screen_text" msgid="1396336058129570886">"Skrin Pisah"</string>
<string name="more_button_text" msgid="3655388105592893530">"Lagi"</string>
<string name="float_button_text" msgid="9221657008391364581">"Terapung"</string>
@@ -134,10 +132,11 @@
<string name="new_window_text" msgid="6318648868380652280">"Tetingkap Baharu"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"Urus Tetingkap"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Tukar nisbah bidang"</string>
+ <!-- no translation found for handle_menu_restart_text (3907767216238298098) -->
+ <skip />
<string name="close_text" msgid="4986518933445178928">"Tutup"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Tutup Menu"</string>
- <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
- <skip />
+ <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (Tetingkap desktop)"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maksimumkan Skrin"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Ubah saiz"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Apl tidak boleh dialihkan ke sini"</string>
diff --git a/libs/WindowManager/Shell/res/values-my/strings.xml b/libs/WindowManager/Shell/res/values-my/strings.xml
index 334a79799d53..061ad0405d89 100644
--- a/libs/WindowManager/Shell/res/values-my/strings.xml
+++ b/libs/WindowManager/Shell/res/values-my/strings.xml
@@ -100,8 +100,7 @@
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"ကောင်းမသွားဘူးလား။\nပြန်ပြောင်းရန် တို့ပါ"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"ကင်မရာပြဿနာ မရှိဘူးလား။ ပယ်ရန် တို့ပါ။"</string>
<string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"အက်ပ်မီနူးကို ဤနေရာတွင် တွေ့နိုင်သည်"</string>
- <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
- <skip />
+ <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"အက်ပ်များစွာကို အတူတကွဖွင့်ရန်အတွက် ဒက်စ်တော့ဝင်းဒိုးမုဒ်သို့ ဝင်ရောက်နိုင်သည်"</string>
<string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"အက်ပ်မီနူးမှ ဖန်သားပြင်အပြည့်သို့ အချိန်မရွေး ပြန်သွားနိုင်သည်"</string>
<string name="letterbox_education_dialog_title" msgid="7739895354143295358">"ကြည့်ပြီး ပိုမိုလုပ်ဆောင်ပါ"</string>
<string name="letterbox_education_split_screen_text" msgid="449233070804658627">"မျက်နှာပြင် ခွဲ၍ပြသခြင်းအတွက် အက်ပ်နောက်တစ်ခုကို ဖိဆွဲပါ"</string>
@@ -122,8 +121,7 @@
<string name="handle_text" msgid="4419667835599523257">"အက်ပ်သုံးသူအမည်"</string>
<string name="app_icon_text" msgid="2823268023931811747">"အက်ပ်သင်္ကေတ"</string>
<string name="fullscreen_text" msgid="1162316685217676079">"ဖန်သားပြင်အပြည့်"</string>
- <!-- no translation found for desktop_text (9058641752519570266) -->
- <skip />
+ <string name="desktop_text" msgid="9058641752519570266">"ဒက်စ်တော့ဝင်းဒိုးမုဒ်"</string>
<string name="split_screen_text" msgid="1396336058129570886">"မျက်နှာပြင် ခွဲ၍ပြသရန်"</string>
<string name="more_button_text" msgid="3655388105592893530">"ပိုပြပါ"</string>
<string name="float_button_text" msgid="9221657008391364581">"မျှောရန်"</string>
@@ -134,10 +132,11 @@
<string name="new_window_text" msgid="6318648868380652280">"ဝင်းဒိုးအသစ်"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"ဝင်းဒိုးများ စီမံရန်"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"အချိုးအစား ပြောင်းရန်"</string>
+ <!-- no translation found for handle_menu_restart_text (3907767216238298098) -->
+ <skip />
<string name="close_text" msgid="4986518933445178928">"ပိတ်ရန်"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"မီနူး ပိတ်ရန်"</string>
- <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
- <skip />
+ <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (ဒက်စ်တော့ဝင်းဒိုးမုဒ်)"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"စခရင်ကို ချဲ့မည်"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"အရွယ်ပြင်ရန်"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"အက်ပ်ကို ဤနေရာသို့ ရွှေ့၍မရပါ"</string>
diff --git a/libs/WindowManager/Shell/res/values-nb/strings.xml b/libs/WindowManager/Shell/res/values-nb/strings.xml
index 7e21b475b2fa..232fb2b04f92 100644
--- a/libs/WindowManager/Shell/res/values-nb/strings.xml
+++ b/libs/WindowManager/Shell/res/values-nb/strings.xml
@@ -100,8 +100,7 @@
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Ble ikke problemet løst?\nTrykk for å gå tilbake"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Har du ingen kameraproblemer? Trykk for å lukke."</string>
<string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Her finner du appmenyen"</string>
- <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
- <skip />
+ <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"Start datamaskin-vindusvisning for å åpne flere apper samtidig"</string>
<string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Du kan gå tilbake til fullskjermmodusen når som helst fra appmenyen"</string>
<string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Se og gjør mer"</string>
<string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Dra inn en annen app for å bruke delt skjerm"</string>
@@ -122,8 +121,7 @@
<string name="handle_text" msgid="4419667835599523257">"Apphåndtak"</string>
<string name="app_icon_text" msgid="2823268023931811747">"Appikon"</string>
<string name="fullscreen_text" msgid="1162316685217676079">"Fullskjerm"</string>
- <!-- no translation found for desktop_text (9058641752519570266) -->
- <skip />
+ <string name="desktop_text" msgid="9058641752519570266">"Datamaskin-vindusvisning"</string>
<string name="split_screen_text" msgid="1396336058129570886">"Delt skjerm"</string>
<string name="more_button_text" msgid="3655388105592893530">"Mer"</string>
<string name="float_button_text" msgid="9221657008391364581">"Svevende"</string>
@@ -134,10 +132,11 @@
<string name="new_window_text" msgid="6318648868380652280">"Nytt vindu"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"Administrer vinduene"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Endre høyde/bredde-forholdet"</string>
+ <!-- no translation found for handle_menu_restart_text (3907767216238298098) -->
+ <skip />
<string name="close_text" msgid="4986518933445178928">"Lukk"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Lukk menyen"</string>
- <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
- <skip />
+ <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (datamaskin-vindusvisning)"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maksimer skjermen"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Endre størrelse"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Appen kan ikke flyttes hit"</string>
diff --git a/libs/WindowManager/Shell/res/values-ne/strings.xml b/libs/WindowManager/Shell/res/values-ne/strings.xml
index 976f8378606a..2d6ab7d816ab 100644
--- a/libs/WindowManager/Shell/res/values-ne/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ne/strings.xml
@@ -100,8 +100,7 @@
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"समस्या हल भएन?\nपहिलेको जस्तै बनाउन ट्याप गर्नुहोस्"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"क्यामेरासम्बन्धी कुनै पनि समस्या छैन? खारेज गर्न ट्याप गर्नुहोस्।"</string>
<string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"एपको मेनु यहाँ भेट्टाउन सकिन्छ"</string>
- <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
- <skip />
+ <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"एकभन्दा बढी एपहरू सँगै देखाउन डेस्कटप विन्डोइङ हाल्नुहोस्"</string>
<string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"जुनसुकै बेला एपको मेनुबाट फुल स्क्रिनमा फर्कनुहोस्"</string>
<string name="letterbox_education_dialog_title" msgid="7739895354143295358">"थप कुरा हेर्नुहोस् र गर्नुहोस्"</string>
<string name="letterbox_education_split_screen_text" msgid="449233070804658627">"स्प्लिट स्क्रिन मोड प्रयोग गर्न अर्को एप ड्रयाग एन्ड ड्रप गर्नुहोस्"</string>
@@ -122,8 +121,7 @@
<string name="handle_text" msgid="4419667835599523257">"एपको ह्यान्डल"</string>
<string name="app_icon_text" msgid="2823268023931811747">"एपको आइकन"</string>
<string name="fullscreen_text" msgid="1162316685217676079">"फुल स्क्रिन"</string>
- <!-- no translation found for desktop_text (9058641752519570266) -->
- <skip />
+ <string name="desktop_text" msgid="9058641752519570266">"डेस्कटप विन्डोइङ"</string>
<string name="split_screen_text" msgid="1396336058129570886">"स्प्लिट स्क्रिन"</string>
<string name="more_button_text" msgid="3655388105592893530">"थप"</string>
<string name="float_button_text" msgid="9221657008391364581">"फ्लोट"</string>
@@ -134,10 +132,11 @@
<string name="new_window_text" msgid="6318648868380652280">"नयाँ विन्डो"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"विन्डोहरू व्यवस्थापन गर्नुहोस्"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"एस्पेक्ट रेसियो परिवर्तन गर्नुहोस्"</string>
+ <!-- no translation found for handle_menu_restart_text (3907767216238298098) -->
+ <skip />
<string name="close_text" msgid="4986518933445178928">"बन्द गर्नुहोस्"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"मेनु बन्द गर्नुहोस्"</string>
- <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
- <skip />
+ <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (डेस्कटप विन्डोइङ)"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"स्क्रिन ठुलो बनाउनुहोस्"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"आकार बदल्नुहोस्"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"एप सारेर यहाँ ल्याउन सकिएन"</string>
diff --git a/libs/WindowManager/Shell/res/values-nl/strings.xml b/libs/WindowManager/Shell/res/values-nl/strings.xml
index 7178e418a5aa..23f251f8ef60 100644
--- a/libs/WindowManager/Shell/res/values-nl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-nl/strings.xml
@@ -100,8 +100,7 @@
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Is dit geen oplossing?\nTik om terug te zetten."</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Geen cameraproblemen? Tik om te sluiten."</string>
<string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Het app-menu vind je hier"</string>
- <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
- <skip />
+ <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"Ga naar de desktopvensterfunctie om meerdere apps tegelijk te openen"</string>
<string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Ga wanneer je wilt terug naar volledig scherm vanuit het app-menu"</string>
<string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Zie en doe meer"</string>
<string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Sleep een andere app hier naartoe om het scherm te splitsen"</string>
@@ -122,8 +121,7 @@
<string name="handle_text" msgid="4419667835599523257">"App-handgreep"</string>
<string name="app_icon_text" msgid="2823268023931811747">"App-icoon"</string>
<string name="fullscreen_text" msgid="1162316685217676079">"Volledig scherm"</string>
- <!-- no translation found for desktop_text (9058641752519570266) -->
- <skip />
+ <string name="desktop_text" msgid="9058641752519570266">"Desktopvensterfunctie"</string>
<string name="split_screen_text" msgid="1396336058129570886">"Gesplitst scherm"</string>
<string name="more_button_text" msgid="3655388105592893530">"Meer"</string>
<string name="float_button_text" msgid="9221657008391364581">"Zwevend"</string>
@@ -134,10 +132,11 @@
<string name="new_window_text" msgid="6318648868380652280">"Nieuw venster"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"Vensters beheren"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Beeldverhouding wijzigen"</string>
+ <!-- no translation found for handle_menu_restart_text (3907767216238298098) -->
+ <skip />
<string name="close_text" msgid="4986518933445178928">"Sluiten"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Menu sluiten"</string>
- <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
- <skip />
+ <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (desktopvensterfunctie)"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Scherm maximaliseren"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Formaat aanpassen"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Kan de app niet hierheen verplaatsen"</string>
diff --git a/libs/WindowManager/Shell/res/values-or/strings.xml b/libs/WindowManager/Shell/res/values-or/strings.xml
index 19cc8ced6517..8b7f0c758ac7 100644
--- a/libs/WindowManager/Shell/res/values-or/strings.xml
+++ b/libs/WindowManager/Shell/res/values-or/strings.xml
@@ -100,8 +100,7 @@
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"ଏହାର ସମାଧାନ ହୋଇନାହିଁ?\nଫେରିଯିବା ପାଇଁ ଟାପ କରନ୍ତୁ"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"କ୍ୟାମେରାରେ କିଛି ସମସ୍ୟା ନାହିଁ? ଖାରଜ କରିବାକୁ ଟାପ କରନ୍ତୁ।"</string>
<string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"ଆପ ମେନୁ ଏଠାରେ ମିଳିପାରିବ"</string>
- <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
- <skip />
+ <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"ଏକାଠି ଏକାଧିକ ଆପ୍ସ ଖୋଲିବାକୁ ଡେସ୍କଟପ ୱିଣ୍ଡୋଇଂରେ ଏଣ୍ଟର କରନ୍ତୁ"</string>
<string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"ଆପ ମେନୁରୁ ଯେ କୌଣସି ସମୟରେ ପୂର୍ଣ୍ଣ ସ୍କ୍ରିନକୁ ଫେରନ୍ତୁ"</string>
<string name="letterbox_education_dialog_title" msgid="7739895354143295358">"ଦେଖନ୍ତୁ ଏବଂ ଆହୁରି ଅନେକ କିଛି କରନ୍ତୁ"</string>
<string name="letterbox_education_split_screen_text" msgid="449233070804658627">"ସ୍ପ୍ଲିଟ ସ୍କ୍ରିନ ପାଇଁ ଅନ୍ୟ ଏକ ଆପକୁ ଡ୍ରାଗ କରନ୍ତୁ"</string>
@@ -122,8 +121,7 @@
<string name="handle_text" msgid="4419667835599523257">"ଆପର ହେଣ୍ଡେଲ"</string>
<string name="app_icon_text" msgid="2823268023931811747">"ଆପ ଆଇକନ"</string>
<string name="fullscreen_text" msgid="1162316685217676079">"ପୂର୍ଣ୍ଣସ୍କ୍ରିନ"</string>
- <!-- no translation found for desktop_text (9058641752519570266) -->
- <skip />
+ <string name="desktop_text" msgid="9058641752519570266">"ଡେସ୍କଟପ ୱିଣ୍ଡୋଇଂ"</string>
<string name="split_screen_text" msgid="1396336058129570886">"ସ୍ପ୍ଲିଟ ସ୍କ୍ରିନ"</string>
<string name="more_button_text" msgid="3655388105592893530">"ଅଧିକ"</string>
<string name="float_button_text" msgid="9221657008391364581">"ଫ୍ଲୋଟ"</string>
@@ -134,10 +132,11 @@
<string name="new_window_text" msgid="6318648868380652280">"ନୂଆ ୱିଣ୍ଡୋ"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"ୱିଣ୍ଡୋଗୁଡ଼ିକୁ ପରିଚାଳନା କରନ୍ତୁ"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"ଚଉଡ଼ା ଓ ଉଚ୍ଚତାର ଅନୁପାତ ପରିବର୍ତ୍ତନ କରନ୍ତୁ"</string>
+ <!-- no translation found for handle_menu_restart_text (3907767216238298098) -->
+ <skip />
<string name="close_text" msgid="4986518933445178928">"ବନ୍ଦ କରନ୍ତୁ"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"ମେନୁ ବନ୍ଦ କରନ୍ତୁ"</string>
- <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
- <skip />
+ <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (ଡେସ୍କଟପ ୱିଣ୍ଡୋଇଂ)"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"ସ୍କ୍ରିନକୁ ବଡ଼ କରନ୍ତୁ"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"ରିସାଇଜ କରନ୍ତୁ"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"ଆପକୁ ଏଠାକୁ ମୁଭ କରାଯାଇପାରିବ ନାହିଁ"</string>
diff --git a/libs/WindowManager/Shell/res/values-pa/strings.xml b/libs/WindowManager/Shell/res/values-pa/strings.xml
index 7e8929003017..e074a073de00 100644
--- a/libs/WindowManager/Shell/res/values-pa/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pa/strings.xml
@@ -100,8 +100,7 @@
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"ਕੀ ਇਹ ਠੀਕ ਨਹੀਂ ਹੋਈ?\nਵਾਪਸ ਉਹੀ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"ਕੀ ਕੈਮਰੇ ਸੰਬੰਧੀ ਕੋਈ ਸਮੱਸਿਆ ਨਹੀਂ ਹੈ? ਖਾਰਜ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ।"</string>
<string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"ਐਪ ਮੀਨੂ ਇੱਥੇ ਮਿਲ ਸਕਦਾ ਹੈ"</string>
- <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
- <skip />
+ <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"ਕਈ ਐਪਾਂ ਨੂੰ ਇਕੱਠੇ ਖੋਲ੍ਹਣ ਲਈ ਡੈਸਕਟਾਪ ਵਿੰਡੋ ਵਿੱਚ ਦਾਖਲ ਹੋਵੋ"</string>
<string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"ਐਪ ਮੀਨੂ ਤੋਂ ਕਿਸੇ ਵੀ ਸਮੇਂ ਪੂਰੀ ਸਕ੍ਰੀਨ \'ਤੇ ਵਾਪਸ ਜਾਓ"</string>
<string name="letterbox_education_dialog_title" msgid="7739895354143295358">"ਦੇਖੋ ਅਤੇ ਹੋਰ ਬਹੁਤ ਕੁਝ ਕਰੋ"</string>
<string name="letterbox_education_split_screen_text" msgid="449233070804658627">"ਸਪਲਿਟ ਸਕ੍ਰੀਨ ਦੇ ਲਈ ਕਿਸੇ ਹੋਰ ਐਪ ਵਿੱਚ ਘਸੀਟੋ"</string>
@@ -122,8 +121,7 @@
<string name="handle_text" msgid="4419667835599523257">"ਐਪ ਹੈਂਡਲ"</string>
<string name="app_icon_text" msgid="2823268023931811747">"ਐਪ ਪ੍ਰਤੀਕ"</string>
<string name="fullscreen_text" msgid="1162316685217676079">"ਪੂਰੀ-ਸਕ੍ਰੀਨ"</string>
- <!-- no translation found for desktop_text (9058641752519570266) -->
- <skip />
+ <string name="desktop_text" msgid="9058641752519570266">"ਡੈਸਕਟਾਪ ਵਿੰਡੋ"</string>
<string name="split_screen_text" msgid="1396336058129570886">"ਸਪਲਿਟ ਸਕ੍ਰੀਨ"</string>
<string name="more_button_text" msgid="3655388105592893530">"ਹੋਰ"</string>
<string name="float_button_text" msgid="9221657008391364581">"ਫ਼ਲੋਟ"</string>
@@ -134,10 +132,11 @@
<string name="new_window_text" msgid="6318648868380652280">"ਨਵੀਂ ਵਿੰਡੋ"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"ਵਿੰਡੋਆਂ ਦਾ ਪ੍ਰਬੰਧਨ ਕਰੋ"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"ਆਕਾਰ ਅਨੁਪਾਤ ਬਦਲੋ"</string>
+ <!-- no translation found for handle_menu_restart_text (3907767216238298098) -->
+ <skip />
<string name="close_text" msgid="4986518933445178928">"ਬੰਦ ਕਰੋ"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"ਮੀਨੂ ਬੰਦ ਕਰੋ"</string>
- <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
- <skip />
+ <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (ਡੈਸਕਟਾਪ ਵਿੰਡੋ)"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"ਸਕ੍ਰੀਨ ਦਾ ਆਕਾਰ ਵਧਾਓ"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"ਆਕਾਰ ਬਦਲੋ"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"ਐਪ ਨੂੰ ਇੱਥੇ ਨਹੀਂ ਲਿਜਾਇਆ ਜਾ ਸਕਦਾ"</string>
diff --git a/libs/WindowManager/Shell/res/values-pl/strings.xml b/libs/WindowManager/Shell/res/values-pl/strings.xml
index 58a691f83cca..861d94723317 100644
--- a/libs/WindowManager/Shell/res/values-pl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pl/strings.xml
@@ -100,8 +100,7 @@
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Naprawa się nie udała?\nKliknij, aby cofnąć"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Brak problemów z aparatem? Kliknij, aby zamknąć"</string>
<string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Tu znajdziesz menu aplikacji"</string>
- <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
- <skip />
+ <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"Aby otworzyć kilka aplikacji jednocześnie, przejdź do trybu okien na pulpicie"</string>
<string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Z menu aplikacji w każdej chwili możesz wrócić do pełnego ekranu"</string>
<string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Zobacz i zrób więcej"</string>
<string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Aby podzielić ekran, przeciągnij drugą aplikację"</string>
@@ -122,8 +121,7 @@
<string name="handle_text" msgid="4419667835599523257">"Uchwyt aplikacji"</string>
<string name="app_icon_text" msgid="2823268023931811747">"Ikona aplikacji"</string>
<string name="fullscreen_text" msgid="1162316685217676079">"Pełny ekran"</string>
- <!-- no translation found for desktop_text (9058641752519570266) -->
- <skip />
+ <string name="desktop_text" msgid="9058641752519570266">"Tryb okien na pulpicie"</string>
<string name="split_screen_text" msgid="1396336058129570886">"Podzielony ekran"</string>
<string name="more_button_text" msgid="3655388105592893530">"Więcej"</string>
<string name="float_button_text" msgid="9221657008391364581">"Pływające"</string>
@@ -134,10 +132,11 @@
<string name="new_window_text" msgid="6318648868380652280">"Nowe okno"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"Zarządzaj oknami"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Zmień format obrazu"</string>
+ <!-- no translation found for handle_menu_restart_text (3907767216238298098) -->
+ <skip />
<string name="close_text" msgid="4986518933445178928">"Zamknij"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Zamknij menu"</string>
- <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
- <skip />
+ <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (tryb okien na pulpicie)"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maksymalizuj ekran"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Zmień rozmiar"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Nie można przenieść aplikacji tutaj"</string>
diff --git a/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml b/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml
index 2d9bc2bc5b80..53db421ac0f0 100644
--- a/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml
@@ -100,8 +100,7 @@
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"O problema não foi corrigido?\nToque para reverter"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Não tem problemas com a câmera? Toque para dispensar."</string>
<string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"O menu do app está aqui"</string>
- <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
- <skip />
+ <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"Abra vários apps ao mesmo tempo usando o modo janela para computador"</string>
<string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Volte para a tela cheia a qualquer momento no menu do app"</string>
<string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Veja e faça mais"</string>
<string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Arraste outro app para dividir a tela"</string>
@@ -122,8 +121,7 @@
<string name="handle_text" msgid="4419667835599523257">"Identificador do app"</string>
<string name="app_icon_text" msgid="2823268023931811747">"Ícone do app"</string>
<string name="fullscreen_text" msgid="1162316685217676079">"Tela cheia"</string>
- <!-- no translation found for desktop_text (9058641752519570266) -->
- <skip />
+ <string name="desktop_text" msgid="9058641752519570266">"Modo janela para computador"</string>
<string name="split_screen_text" msgid="1396336058129570886">"Tela dividida"</string>
<string name="more_button_text" msgid="3655388105592893530">"Mais"</string>
<string name="float_button_text" msgid="9221657008391364581">"Ponto flutuante"</string>
@@ -134,10 +132,11 @@
<string name="new_window_text" msgid="6318648868380652280">"Nova janela"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"Gerenciar janelas"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Mudar a proporção"</string>
+ <!-- no translation found for handle_menu_restart_text (3907767216238298098) -->
+ <skip />
<string name="close_text" msgid="4986518933445178928">"Fechar"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Fechar menu"</string>
- <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
- <skip />
+ <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (modo janela para computador)"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Ampliar tela"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Redimensionar"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Não é possível mover o app para cá"</string>
diff --git a/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml b/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml
index 765a2071a037..5a4da3c07389 100644
--- a/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml
@@ -100,8 +100,7 @@
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Não foi corrigido?\nToque para reverter"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Nenhum problema com a câmara? Toque para ignorar."</string>
<string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"O menu da app pode ser encontrado aqui"</string>
- <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
- <skip />
+ <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"Entre no modo de janelas de computador para abrir várias apps em conjunto"</string>
<string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Regresse ao ecrã inteiro em qualquer altura a partir do menu da app"</string>
<string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Veja e faça mais"</string>
<string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Arraste outra app para usar o ecrã dividido"</string>
@@ -122,8 +121,7 @@
<string name="handle_text" msgid="4419667835599523257">"Indicador da app"</string>
<string name="app_icon_text" msgid="2823268023931811747">"Ícone da app"</string>
<string name="fullscreen_text" msgid="1162316685217676079">"Ecrã inteiro"</string>
- <!-- no translation found for desktop_text (9058641752519570266) -->
- <skip />
+ <string name="desktop_text" msgid="9058641752519570266">"Janelas de computador"</string>
<string name="split_screen_text" msgid="1396336058129570886">"Ecrã dividido"</string>
<string name="more_button_text" msgid="3655388105592893530">"Mais"</string>
<string name="float_button_text" msgid="9221657008391364581">"Flutuar"</string>
@@ -134,10 +132,11 @@
<string name="new_window_text" msgid="6318648868380652280">"Nova janela"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"Gerir janelas"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Alterar formato"</string>
+ <!-- no translation found for handle_menu_restart_text (3907767216238298098) -->
+ <skip />
<string name="close_text" msgid="4986518933445178928">"Fechar"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Fechar menu"</string>
- <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
- <skip />
+ <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (janelas de computador)"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maximizar ecrã"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Redimensionar"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Não é possível mover a app para aqui"</string>
diff --git a/libs/WindowManager/Shell/res/values-pt/strings.xml b/libs/WindowManager/Shell/res/values-pt/strings.xml
index 2d9bc2bc5b80..53db421ac0f0 100644
--- a/libs/WindowManager/Shell/res/values-pt/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pt/strings.xml
@@ -100,8 +100,7 @@
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"O problema não foi corrigido?\nToque para reverter"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Não tem problemas com a câmera? Toque para dispensar."</string>
<string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"O menu do app está aqui"</string>
- <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
- <skip />
+ <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"Abra vários apps ao mesmo tempo usando o modo janela para computador"</string>
<string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Volte para a tela cheia a qualquer momento no menu do app"</string>
<string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Veja e faça mais"</string>
<string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Arraste outro app para dividir a tela"</string>
@@ -122,8 +121,7 @@
<string name="handle_text" msgid="4419667835599523257">"Identificador do app"</string>
<string name="app_icon_text" msgid="2823268023931811747">"Ícone do app"</string>
<string name="fullscreen_text" msgid="1162316685217676079">"Tela cheia"</string>
- <!-- no translation found for desktop_text (9058641752519570266) -->
- <skip />
+ <string name="desktop_text" msgid="9058641752519570266">"Modo janela para computador"</string>
<string name="split_screen_text" msgid="1396336058129570886">"Tela dividida"</string>
<string name="more_button_text" msgid="3655388105592893530">"Mais"</string>
<string name="float_button_text" msgid="9221657008391364581">"Ponto flutuante"</string>
@@ -134,10 +132,11 @@
<string name="new_window_text" msgid="6318648868380652280">"Nova janela"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"Gerenciar janelas"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Mudar a proporção"</string>
+ <!-- no translation found for handle_menu_restart_text (3907767216238298098) -->
+ <skip />
<string name="close_text" msgid="4986518933445178928">"Fechar"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Fechar menu"</string>
- <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
- <skip />
+ <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (modo janela para computador)"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Ampliar tela"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Redimensionar"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Não é possível mover o app para cá"</string>
diff --git a/libs/WindowManager/Shell/res/values-ro/strings.xml b/libs/WindowManager/Shell/res/values-ro/strings.xml
index 68d045fdbe29..159040082a40 100644
--- a/libs/WindowManager/Shell/res/values-ro/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ro/strings.xml
@@ -100,8 +100,7 @@
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Nu ai remediat problema?\nAtinge pentru a reveni"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Nu ai probleme cu camera foto? Atinge pentru a închide."</string>
<string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Meniul aplicației poate fi găsit aici"</string>
- <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
- <skip />
+ <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"Accesează afișarea în ferestre pe desktop pentru a deschide mai multe aplicații simultan"</string>
<string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Revino oricând la ecranul complet din meniul aplicației"</string>
<string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Vezi și fă mai multe"</string>
<string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Trage în altă aplicație pentru a folosi ecranul împărțit"</string>
@@ -122,8 +121,7 @@
<string name="handle_text" msgid="4419667835599523257">"Handle de aplicație"</string>
<string name="app_icon_text" msgid="2823268023931811747">"Pictograma aplicației"</string>
<string name="fullscreen_text" msgid="1162316685217676079">"Ecran complet"</string>
- <!-- no translation found for desktop_text (9058641752519570266) -->
- <skip />
+ <string name="desktop_text" msgid="9058641752519570266">"Ferestre pe desktop"</string>
<string name="split_screen_text" msgid="1396336058129570886">"Ecran împărțit"</string>
<string name="more_button_text" msgid="3655388105592893530">"Mai multe"</string>
<string name="float_button_text" msgid="9221657008391364581">"Flotantă"</string>
@@ -134,10 +132,11 @@
<string name="new_window_text" msgid="6318648868380652280">"Fereastră nouă"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"Gestionează ferestrele"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Schimbă raportul de dimensiuni"</string>
+ <!-- no translation found for handle_menu_restart_text (3907767216238298098) -->
+ <skip />
<string name="close_text" msgid="4986518933445178928">"Închide"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Închide meniul"</string>
- <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
- <skip />
+ <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (ferestre pe desktop)"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maximizează fereastra"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Redimensionează"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Aplicația nu poate fi mutată aici"</string>
diff --git a/libs/WindowManager/Shell/res/values-ru/strings.xml b/libs/WindowManager/Shell/res/values-ru/strings.xml
index 8e683b8afb79..bd7fa6e72b35 100644
--- a/libs/WindowManager/Shell/res/values-ru/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ru/strings.xml
@@ -100,8 +100,7 @@
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Не помогло?\nНажмите, чтобы отменить изменения."</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Нет проблем с камерой? Нажмите, чтобы закрыть."</string>
<string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Здесь вы найдете меню приложения"</string>
- <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
- <skip />
+ <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"Чтобы открыть сразу несколько приложений, перейдите в режим компьютерных окон"</string>
<string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Вернуться в полноэкранный режим можно из меню приложения"</string>
<string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Выполняйте несколько задач одновременно"</string>
<string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Перетащите сюда другое приложение, чтобы использовать разделение экрана."</string>
@@ -122,8 +121,7 @@
<string name="handle_text" msgid="4419667835599523257">"Обозначение приложения"</string>
<string name="app_icon_text" msgid="2823268023931811747">"Значок приложения"</string>
<string name="fullscreen_text" msgid="1162316685217676079">"Полноэкранный режим"</string>
- <!-- no translation found for desktop_text (9058641752519570266) -->
- <skip />
+ <string name="desktop_text" msgid="9058641752519570266">"Режим компьютерных окон"</string>
<string name="split_screen_text" msgid="1396336058129570886">"Разделить экран"</string>
<string name="more_button_text" msgid="3655388105592893530">"Ещё"</string>
<string name="float_button_text" msgid="9221657008391364581">"Плавающее окно"</string>
@@ -134,10 +132,11 @@
<string name="new_window_text" msgid="6318648868380652280">"Новое окно"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"Управление окнами"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Изменить соотношение сторон"</string>
+ <!-- no translation found for handle_menu_restart_text (3907767216238298098) -->
+ <skip />
<string name="close_text" msgid="4986518933445178928">"Закрыть"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Закрыть меню"</string>
- <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
- <skip />
+ <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (режим компьютерных окон)"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Развернуть на весь экран"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Изменить размер"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Приложение нельзя сюда переместить"</string>
diff --git a/libs/WindowManager/Shell/res/values-si/strings.xml b/libs/WindowManager/Shell/res/values-si/strings.xml
index 6d548395bfac..7a72f99034ac 100644
--- a/libs/WindowManager/Shell/res/values-si/strings.xml
+++ b/libs/WindowManager/Shell/res/values-si/strings.xml
@@ -100,8 +100,7 @@
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"එය විසඳුවේ නැතිද?\nප්‍රතිවර්තනය කිරීමට තට්ටු කරන්න"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"කැමරා ගැටලු නොමැතිද? ඉවත දැමීමට තට්ටු කරන්න"</string>
<string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"යෙදුම් මෙනුව මෙතැනින් සොයා ගත හැක"</string>
- <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
- <skip />
+ <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"බහු යෙදුම් එකවර විවෘත කිරීමට ඩෙස්ක්ටොප් කවුළුවට ඇතුළු වන්න"</string>
<string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"යෙදුම් මෙනුවෙන් ඕනෑම වේලාවක පූර්ණ තිරය වෙත ආපසු යන්න"</string>
<string name="letterbox_education_dialog_title" msgid="7739895354143295358">"බලන්න සහ තවත් දේ කරන්න"</string>
<string name="letterbox_education_split_screen_text" msgid="449233070804658627">"බෙදුම් තිරය සඳහා වෙනත් යෙදුමකට අදින්න"</string>
@@ -122,8 +121,7 @@
<string name="handle_text" msgid="4419667835599523257">"යෙදුම් හසුරුව"</string>
<string name="app_icon_text" msgid="2823268023931811747">"යෙදුම් නිරූපකය"</string>
<string name="fullscreen_text" msgid="1162316685217676079">"පූර්ණ තිරය"</string>
- <!-- no translation found for desktop_text (9058641752519570266) -->
- <skip />
+ <string name="desktop_text" msgid="9058641752519570266">"ඩෙස්ක්ටොප් කවුළුකරණය"</string>
<string name="split_screen_text" msgid="1396336058129570886">"බෙදුම් තිරය"</string>
<string name="more_button_text" msgid="3655388105592893530">"තව"</string>
<string name="float_button_text" msgid="9221657008391364581">"පාවෙන"</string>
@@ -134,10 +132,11 @@
<string name="new_window_text" msgid="6318648868380652280">"නව කවුළුව"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"කවුළු කළමනාකරණය කරන්න"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"දර්ශන අනුපාතය වෙනස් කරන්න"</string>
+ <!-- no translation found for handle_menu_restart_text (3907767216238298098) -->
+ <skip />
<string name="close_text" msgid="4986518933445178928">"වසන්න"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"මෙනුව වසන්න"</string>
- <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
- <skip />
+ <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (ඩෙස්ක්ටොප් කවුළුකරණය)"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"තිරය උපරිම කරන්න"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"ප්‍රතිප්‍රමාණය කරන්න"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"යෙදුම මෙතැනට ගෙන යා නොහැක"</string>
diff --git a/libs/WindowManager/Shell/res/values-sk/strings.xml b/libs/WindowManager/Shell/res/values-sk/strings.xml
index c04b03873dc7..f0f3e50dccd4 100644
--- a/libs/WindowManager/Shell/res/values-sk/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sk/strings.xml
@@ -100,8 +100,7 @@
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Nevyriešilo sa to?\nKlepnutím sa vráťte."</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Nemáte problémy s kamerou? Klepnutím zatvoríte."</string>
<string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Ponuku aplikácie nájdete tu"</string>
- <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
- <skip />
+ <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"Aktivujte windowing na pracovnej ploche a otvorte viac aplikácií naraz"</string>
<string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Z ponuky aplikácie sa môžete kedykoľvek vrátiť na celú obrazovku"</string>
<string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Zobrazte si a zvládnite toho viac"</string>
<string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Rozdelenú obrazovku môžete použiť presunutím do inej aplikácie"</string>
@@ -122,8 +121,7 @@
<string name="handle_text" msgid="4419667835599523257">"Rukoväť aplikácie"</string>
<string name="app_icon_text" msgid="2823268023931811747">"Ikona aplikácie"</string>
<string name="fullscreen_text" msgid="1162316685217676079">"Celá obrazovka"</string>
- <!-- no translation found for desktop_text (9058641752519570266) -->
- <skip />
+ <string name="desktop_text" msgid="9058641752519570266">"Windowing na pracovnej ploche"</string>
<string name="split_screen_text" msgid="1396336058129570886">"Rozdelená obrazovka"</string>
<string name="more_button_text" msgid="3655388105592893530">"Viac"</string>
<string name="float_button_text" msgid="9221657008391364581">"Plávajúce"</string>
@@ -134,10 +132,11 @@
<string name="new_window_text" msgid="6318648868380652280">"Nové okno"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"Spravovať okná"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Zmeniť pomer strán"</string>
+ <!-- no translation found for handle_menu_restart_text (3907767216238298098) -->
+ <skip />
<string name="close_text" msgid="4986518933445178928">"Zavrieť"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Zavrieť ponuku"</string>
- <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
- <skip />
+ <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (windowing na pracovnej ploche)"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maximalizovať obrazovku"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Zmeniť veľkosť"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Aplikácia sa sem nedá presunúť"</string>
diff --git a/libs/WindowManager/Shell/res/values-sl/strings.xml b/libs/WindowManager/Shell/res/values-sl/strings.xml
index 22d7bfe978cc..d86cf6baca67 100644
--- a/libs/WindowManager/Shell/res/values-sl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sl/strings.xml
@@ -100,8 +100,7 @@
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"To ni odpravilo težave?\nDotaknite se za povrnitev"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Nimate težav s fotoaparatom? Dotaknite se za opustitev."</string>
<string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Meni aplikacije najdete tukaj"</string>
- <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
- <skip />
+ <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"Preklopite v namizni način prikaza več oken hkrati, če želite odpreti več aplikacij hkrati"</string>
<string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"V meniju aplikacije se lahko kadar koli vrnete v celozaslonski način"</string>
<string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Oglejte si in naredite več"</string>
<string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Za razdeljeni zaslon povlecite sem še eno aplikacijo."</string>
@@ -122,8 +121,7 @@
<string name="handle_text" msgid="4419667835599523257">"Identifikator aplikacije"</string>
<string name="app_icon_text" msgid="2823268023931811747">"Ikona aplikacije"</string>
<string name="fullscreen_text" msgid="1162316685217676079">"Celozaslonsko"</string>
- <!-- no translation found for desktop_text (9058641752519570266) -->
- <skip />
+ <string name="desktop_text" msgid="9058641752519570266">"Namizni način prikaza več oken hkrati"</string>
<string name="split_screen_text" msgid="1396336058129570886">"Razdeljen zaslon"</string>
<string name="more_button_text" msgid="3655388105592893530">"Več"</string>
<string name="float_button_text" msgid="9221657008391364581">"Lebdeče"</string>
@@ -134,10 +132,11 @@
<string name="new_window_text" msgid="6318648868380652280">"Novo okno"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"Upravljanje oken"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Sprememba razmerja stranic"</string>
+ <!-- no translation found for handle_menu_restart_text (3907767216238298098) -->
+ <skip />
<string name="close_text" msgid="4986518933445178928">"Zapri"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Zapri meni"</string>
- <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
- <skip />
+ <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (namizni način prikaza več oken hkrati)"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maksimiraj zaslon"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Spremeni velikost"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Aplikacije ni mogoče premakniti sem"</string>
diff --git a/libs/WindowManager/Shell/res/values-sq/strings.xml b/libs/WindowManager/Shell/res/values-sq/strings.xml
index 9330ec3edde5..ca4fe6fd3615 100644
--- a/libs/WindowManager/Shell/res/values-sq/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sq/strings.xml
@@ -100,8 +100,7 @@
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Nuk u rregullua?\nTrokit për ta rikthyer"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Nuk ka probleme me kamerën? Trokit për ta shpërfillur."</string>
<string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Menyja e aplikacioneve mund të gjendet këtu"</string>
- <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
- <skip />
+ <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"Kalo te ndërfaqja me dritare në desktop për të hapur disa aplikacione së bashku"</string>
<string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Kthehu tek ekrani i plotë në çdo kohë nga menyja e aplikacioneve"</string>
<string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Shiko dhe bëj më shumë"</string>
<string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Zvarrite në një aplikacion tjetër për ekranin e ndarë"</string>
@@ -122,8 +121,7 @@
<string name="handle_text" msgid="4419667835599523257">"Emërtimi i aplikacionit"</string>
<string name="app_icon_text" msgid="2823268023931811747">"Ikona e aplikacionit"</string>
<string name="fullscreen_text" msgid="1162316685217676079">"Ekrani i plotë"</string>
- <!-- no translation found for desktop_text (9058641752519570266) -->
- <skip />
+ <string name="desktop_text" msgid="9058641752519570266">"Ndërfaqja me dritare në desktop"</string>
<string name="split_screen_text" msgid="1396336058129570886">"Ekrani i ndarë"</string>
<string name="more_button_text" msgid="3655388105592893530">"Më shumë"</string>
<string name="float_button_text" msgid="9221657008391364581">"Pluskuese"</string>
@@ -134,10 +132,11 @@
<string name="new_window_text" msgid="6318648868380652280">"Dritare e re"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"Menaxho dritaret"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Ndrysho raportin e pamjes"</string>
+ <!-- no translation found for handle_menu_restart_text (3907767216238298098) -->
+ <skip />
<string name="close_text" msgid="4986518933445178928">"Mbyll"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Mbyll menynë"</string>
- <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
- <skip />
+ <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (ndërfaqja me dritare në desktop)"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maksimizo ekranin"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Ndrysho përmasat"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Aplikacioni nuk mund të zhvendoset këtu"</string>
diff --git a/libs/WindowManager/Shell/res/values-sr/strings.xml b/libs/WindowManager/Shell/res/values-sr/strings.xml
index 5bdad159b146..58d9398d8d6c 100644
--- a/libs/WindowManager/Shell/res/values-sr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sr/strings.xml
@@ -100,8 +100,7 @@
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Проблем није решен?\nДодирните да бисте вратили"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Немате проблема са камером? Додирните да бисте одбацили."</string>
<string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Мени апликације можете да пронађете овде"</string>
- <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
- <skip />
+ <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"Уђите у прозорски приказ за рачунаре да бисте истовремено отворили више апликација"</string>
<string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Вратите се на цео екран било када из менија апликације"</string>
<string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Видите и урадите више"</string>
<string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Превуците другу апликацију да бисте користили подељени екран"</string>
@@ -122,8 +121,7 @@
<string name="handle_text" msgid="4419667835599523257">"Идентификатор апликације"</string>
<string name="app_icon_text" msgid="2823268023931811747">"Икона апликације"</string>
<string name="fullscreen_text" msgid="1162316685217676079">"Преко целог екрана"</string>
- <!-- no translation found for desktop_text (9058641752519570266) -->
- <skip />
+ <string name="desktop_text" msgid="9058641752519570266">"Прозорски приказ за рачунаре"</string>
<string name="split_screen_text" msgid="1396336058129570886">"Подељени екран"</string>
<string name="more_button_text" msgid="3655388105592893530">"Још"</string>
<string name="float_button_text" msgid="9221657008391364581">"Плутајуће"</string>
@@ -134,10 +132,11 @@
<string name="new_window_text" msgid="6318648868380652280">"Нови прозор"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"Управљајте прозорима"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Промени размеру"</string>
+ <!-- no translation found for handle_menu_restart_text (3907767216238298098) -->
+ <skip />
<string name="close_text" msgid="4986518933445178928">"Затворите"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Затворите мени"</string>
- <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
- <skip />
+ <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (прозорски приказ за рачунаре)"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Повећај екран"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Прилагоди"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Апликација не може да се премести овде"</string>
diff --git a/libs/WindowManager/Shell/res/values-sv/strings.xml b/libs/WindowManager/Shell/res/values-sv/strings.xml
index 40a0a5aa6288..5231a673d1f6 100644
--- a/libs/WindowManager/Shell/res/values-sv/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sv/strings.xml
@@ -100,8 +100,7 @@
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Löstes inte problemet?\nTryck för att återställa"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Inga problem med kameran? Tryck för att ignorera."</string>
<string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Appmenyn finns här"</string>
- <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
- <skip />
+ <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"Använd fönsterstapling för att öppna flera appar samtidigt"</string>
<string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Återgå till helskärm när som helst från appmenyn"</string>
<string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Se och gör mer"</string>
<string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Dra till en annan app för att dela upp skärmen"</string>
@@ -122,8 +121,7 @@
<string name="handle_text" msgid="4419667835599523257">"Apphandtag"</string>
<string name="app_icon_text" msgid="2823268023931811747">"Appikon"</string>
<string name="fullscreen_text" msgid="1162316685217676079">"Helskärm"</string>
- <!-- no translation found for desktop_text (9058641752519570266) -->
- <skip />
+ <string name="desktop_text" msgid="9058641752519570266">"Fönsterstapling"</string>
<string name="split_screen_text" msgid="1396336058129570886">"Delad skärm"</string>
<string name="more_button_text" msgid="3655388105592893530">"Mer"</string>
<string name="float_button_text" msgid="9221657008391364581">"Svävande"</string>
@@ -134,10 +132,11 @@
<string name="new_window_text" msgid="6318648868380652280">"Nytt fönster"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"Hantera fönster"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Ändra bildformat"</string>
+ <!-- no translation found for handle_menu_restart_text (3907767216238298098) -->
+ <skip />
<string name="close_text" msgid="4986518933445178928">"Stäng"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Stäng menyn"</string>
- <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
- <skip />
+ <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (fönsterstapling)"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maximera skärmen"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Ändra storlek"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Det går inte att flytta appen hit"</string>
diff --git a/libs/WindowManager/Shell/res/values-sw/strings.xml b/libs/WindowManager/Shell/res/values-sw/strings.xml
index 20429e17d0d6..9c3c10734a00 100644
--- a/libs/WindowManager/Shell/res/values-sw/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sw/strings.xml
@@ -100,8 +100,7 @@
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Umeshindwa kurekebisha?\nGusa ili urejeshe nakala ya awali"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Je, hakuna hitilafu za kamera? Gusa ili uondoe."</string>
<string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Unaweza kupata menyu ya programu hapa"</string>
- <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
- <skip />
+ <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"Tumia hali ya kupanga madirisha ya kompyuta ya mezani ili ufungue programu nyingi pamoja"</string>
<string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Rudi kwenye skrini nzima wakati wowote ukitumia menyu ya programu"</string>
<string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Angalia na ufanye zaidi"</string>
<string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Buruta katika programu nyingine ili utumie skrini iliyogawanywa"</string>
@@ -122,8 +121,7 @@
<string name="handle_text" msgid="4419667835599523257">"Utambulisho wa programu"</string>
<string name="app_icon_text" msgid="2823268023931811747">"Aikoni ya Programu"</string>
<string name="fullscreen_text" msgid="1162316685217676079">"Skrini nzima"</string>
- <!-- no translation found for desktop_text (9058641752519570266) -->
- <skip />
+ <string name="desktop_text" msgid="9058641752519570266">"Kupanga madirisha ya kompyuta ya mezani"</string>
<string name="split_screen_text" msgid="1396336058129570886">"Gawa Skrini"</string>
<string name="more_button_text" msgid="3655388105592893530">"Zaidi"</string>
<string name="float_button_text" msgid="9221657008391364581">"Inayoelea"</string>
@@ -134,10 +132,11 @@
<string name="new_window_text" msgid="6318648868380652280">"Dirisha Jipya"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"Dhibiti Windows"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Badilisha uwiano"</string>
+ <!-- no translation found for handle_menu_restart_text (3907767216238298098) -->
+ <skip />
<string name="close_text" msgid="4986518933445178928">"Funga"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Funga Menyu"</string>
- <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
- <skip />
+ <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (Kupanga madirisha ya kompyuta ya mezani)"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Panua Dirisha kwenye Skrini"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Badilisha ukubwa"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Imeshindwa kuhamishia programu hapa"</string>
diff --git a/libs/WindowManager/Shell/res/values-ta/strings.xml b/libs/WindowManager/Shell/res/values-ta/strings.xml
index 74ecfdcf73a9..811fce3b2cb1 100644
--- a/libs/WindowManager/Shell/res/values-ta/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ta/strings.xml
@@ -100,8 +100,7 @@
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"சிக்கல்கள் சரிசெய்யப்படவில்லையா?\nமாற்றியமைக்க தட்டவும்"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"கேமரா தொடர்பான சிக்கல்கள் எதுவும் இல்லையா? நிராகரிக்க தட்டவும்."</string>
<string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"ஆப்ஸ் மெனுவை இங்கே பார்க்கலாம்"</string>
- <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
- <skip />
+ <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"பல ஆப்ஸை ஒன்றாகத் திறக்க டெஸ்க்டாப் சாளரமாக்குதலுக்குச் செல்லலாம்"</string>
<string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"ஆப்ஸ் மெனுவிலிருந்து எப்போது வேண்டுமானாலும் முழுத்திரைக்குத் திரும்பலாம்"</string>
<string name="letterbox_education_dialog_title" msgid="7739895354143295358">"பலவற்றைப் பார்த்தல் மற்றும் செய்தல்"</string>
<string name="letterbox_education_split_screen_text" msgid="449233070804658627">"திரைப் பிரிப்புக்கு மற்றொரு ஆப்ஸை இழுக்கலாம்"</string>
@@ -122,8 +121,7 @@
<string name="handle_text" msgid="4419667835599523257">"ஆப்ஸ் ஹேண்டில்"</string>
<string name="app_icon_text" msgid="2823268023931811747">"ஆப்ஸ் ஐகான்"</string>
<string name="fullscreen_text" msgid="1162316685217676079">"முழுத்திரை"</string>
- <!-- no translation found for desktop_text (9058641752519570266) -->
- <skip />
+ <string name="desktop_text" msgid="9058641752519570266">"டெஸ்க்டாப் சாளரமாக்குதல்"</string>
<string name="split_screen_text" msgid="1396336058129570886">"திரையைப் பிரிக்கும்"</string>
<string name="more_button_text" msgid="3655388105592893530">"கூடுதல் விருப்பத்தேர்வுகள்"</string>
<string name="float_button_text" msgid="9221657008391364581">"மிதக்கும் சாளரம்"</string>
@@ -134,10 +132,11 @@
<string name="new_window_text" msgid="6318648868380652280">"புதிய சாளரம்"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"சாளரங்களை நிர்வகிக்கலாம்"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"தோற்ற விகிதத்தை மாற்று"</string>
+ <!-- no translation found for handle_menu_restart_text (3907767216238298098) -->
+ <skip />
<string name="close_text" msgid="4986518933445178928">"மூடும்"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"மெனுவை மூடும்"</string>
- <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
- <skip />
+ <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (டெஸ்க்டாப் சாளரமாக்குதல்)"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"திரையைப் பெரிதாக்கு"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"அளவை மாற்று"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"ஆப்ஸை இங்கே நகர்த்த முடியாது"</string>
diff --git a/libs/WindowManager/Shell/res/values-te/strings.xml b/libs/WindowManager/Shell/res/values-te/strings.xml
index f46c9b628d1a..7a809f4d4684 100644
--- a/libs/WindowManager/Shell/res/values-te/strings.xml
+++ b/libs/WindowManager/Shell/res/values-te/strings.xml
@@ -100,8 +100,7 @@
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"దాని సమస్యను పరిష్కరించలేదా?\nపూర్వస్థితికి మార్చడానికి ట్యాప్ చేయండి"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"కెమెరా సమస్యలు లేవా? తీసివేయడానికి ట్యాప్ చేయండి."</string>
<string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"యాప్ మెనూను ఇక్కడ పొందవచ్చు"</string>
- <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
- <skip />
+ <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"పలు యాప్‌లను ఒకేసారి తెరవడానికి డెస్క్‌టాప్ వీక్షణకు ఎంటర్ అవ్వండి"</string>
<string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"యాప్ మెనూ నుండి ఏ సమయంలోనైనా ఫుల్ స్క్రీన్‌కు తిరిగి రండి"</string>
<string name="letterbox_education_dialog_title" msgid="7739895354143295358">"చూసి, మరిన్ని చేయండి"</string>
<string name="letterbox_education_split_screen_text" msgid="449233070804658627">"స్ప్లిట్ స్క్రీన్ కోసం మరొక యాప్‌లోకి లాగండి"</string>
@@ -122,8 +121,7 @@
<string name="handle_text" msgid="4419667835599523257">"యాప్ హ్యాండిల్"</string>
<string name="app_icon_text" msgid="2823268023931811747">"యాప్ చిహ్నం"</string>
<string name="fullscreen_text" msgid="1162316685217676079">"ఫుల్-స్క్రీన్"</string>
- <!-- no translation found for desktop_text (9058641752519570266) -->
- <skip />
+ <string name="desktop_text" msgid="9058641752519570266">"డెస్క్‌టాప్ వీక్షణ"</string>
<string name="split_screen_text" msgid="1396336058129570886">"స్ప్లిట్ స్క్రీన్"</string>
<string name="more_button_text" msgid="3655388105592893530">"మరిన్ని"</string>
<string name="float_button_text" msgid="9221657008391364581">"ఫ్లోట్"</string>
@@ -134,10 +132,11 @@
<string name="new_window_text" msgid="6318648868380652280">"కొత్త విండో"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"విండోలను మేనేజ్ చేయండి"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"ఆకార నిష్పత్తిని మార్చండి"</string>
+ <!-- no translation found for handle_menu_restart_text (3907767216238298098) -->
+ <skip />
<string name="close_text" msgid="4986518933445178928">"మూసివేయండి"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"మెనూను మూసివేయండి"</string>
- <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
- <skip />
+ <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (డెస్క్‌టాప్ వీక్షణ)"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"స్క్రీన్ సైజ్‌ను పెంచండి"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"సైజ్ మార్చండి"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"యాప్‌ను ఇక్కడకి తరలించడం సాధ్యం కాదు"</string>
diff --git a/libs/WindowManager/Shell/res/values-th/strings.xml b/libs/WindowManager/Shell/res/values-th/strings.xml
index 60632ada32bb..66996356587e 100644
--- a/libs/WindowManager/Shell/res/values-th/strings.xml
+++ b/libs/WindowManager/Shell/res/values-th/strings.xml
@@ -100,8 +100,7 @@
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"หากไม่ได้แก้ไข\nแตะเพื่อเปลี่ยนกลับ"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"หากไม่พบปัญหากับกล้อง แตะเพื่อปิด"</string>
<string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"ดูเมนูแอปที่นี่ได้"</string>
- <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
- <skip />
+ <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"เข้าสู่หน้าต่างเดสก์ท็อปเพื่อเปิดหลายแอปพร้อมกัน"</string>
<string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"กลับไปที่โหมดเต็มหน้าจอได้ทุกเมื่อจากเมนูแอป"</string>
<string name="letterbox_education_dialog_title" msgid="7739895354143295358">"รับชมและทำสิ่งต่างๆ ได้มากขึ้น"</string>
<string name="letterbox_education_split_screen_text" msgid="449233070804658627">"ลากไปไว้ในแอปอื่นเพื่อแยกหน้าจอ"</string>
@@ -122,8 +121,7 @@
<string name="handle_text" msgid="4419667835599523257">"แฮนเดิลแอป"</string>
<string name="app_icon_text" msgid="2823268023931811747">"ไอคอนแอป"</string>
<string name="fullscreen_text" msgid="1162316685217676079">"เต็มหน้าจอ"</string>
- <!-- no translation found for desktop_text (9058641752519570266) -->
- <skip />
+ <string name="desktop_text" msgid="9058641752519570266">"หน้าต่างเดสก์ท็อป"</string>
<string name="split_screen_text" msgid="1396336058129570886">"แยกหน้าจอ"</string>
<string name="more_button_text" msgid="3655388105592893530">"เพิ่มเติม"</string>
<string name="float_button_text" msgid="9221657008391364581">"ล่องลอย"</string>
@@ -134,10 +132,11 @@
<string name="new_window_text" msgid="6318648868380652280">"หน้าต่างใหม่"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"จัดการหน้าต่าง"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"เปลี่ยนสัดส่วนการแสดงผล"</string>
+ <!-- no translation found for handle_menu_restart_text (3907767216238298098) -->
+ <skip />
<string name="close_text" msgid="4986518933445178928">"ปิด"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"ปิดเมนู"</string>
- <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
- <skip />
+ <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (หน้าต่างเดสก์ท็อป)"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"ขยายหน้าจอให้ใหญ่สุด"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"ปรับขนาด"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"ย้ายแอปมาที่นี่ไม่ได้"</string>
diff --git a/libs/WindowManager/Shell/res/values-tl/strings.xml b/libs/WindowManager/Shell/res/values-tl/strings.xml
index ae6df04c255c..82c085bcdbe7 100644
--- a/libs/WindowManager/Shell/res/values-tl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-tl/strings.xml
@@ -100,8 +100,7 @@
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Hindi ito naayos?\nI-tap para i-revert"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Walang isyu sa camera? I-tap para i-dismiss."</string>
<string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Makikita rito ang menu ng app"</string>
- <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
- <skip />
+ <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"Pumunta sa desktop windowing para magbukas ng maraming app nang sabay-sabay"</string>
<string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Bumalik sa full screen anumang oras mula sa menu ng app"</string>
<string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Tumingin at gumawa ng higit pa"</string>
<string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Mag-drag ng isa pang app para sa split screen"</string>
@@ -122,8 +121,7 @@
<string name="handle_text" msgid="4419667835599523257">"Handle ng app"</string>
<string name="app_icon_text" msgid="2823268023931811747">"Icon ng App"</string>
<string name="fullscreen_text" msgid="1162316685217676079">"Fullscreen"</string>
- <!-- no translation found for desktop_text (9058641752519570266) -->
- <skip />
+ <string name="desktop_text" msgid="9058641752519570266">"Desktop windowing"</string>
<string name="split_screen_text" msgid="1396336058129570886">"Split Screen"</string>
<string name="more_button_text" msgid="3655388105592893530">"Higit pa"</string>
<string name="float_button_text" msgid="9221657008391364581">"Float"</string>
@@ -134,10 +132,11 @@
<string name="new_window_text" msgid="6318648868380652280">"Bagong Window"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"Pamahalaan ang Mga Window"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Baguhin ang aspect ratio"</string>
+ <!-- no translation found for handle_menu_restart_text (3907767216238298098) -->
+ <skip />
<string name="close_text" msgid="4986518933445178928">"Isara"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Isara ang Menu"</string>
- <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
- <skip />
+ <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (Desktop windowing)"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"I-maximize ang Screen"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"I-resize"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Hindi mailipat dito ang app"</string>
diff --git a/libs/WindowManager/Shell/res/values-tr/strings.xml b/libs/WindowManager/Shell/res/values-tr/strings.xml
index 116740ef93f7..4d6775a9102d 100644
--- a/libs/WindowManager/Shell/res/values-tr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-tr/strings.xml
@@ -100,8 +100,7 @@
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Bu işlem sorunu düzeltmedi mi?\nİşlemi geri almak için dokunun"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Kameranızda sorun yok mu? Kapatmak için dokunun."</string>
<string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Uygulama menüsünü burada bulabilirsiniz"</string>
- <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
- <skip />
+ <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"Birden fazla uygulamayı birlikte açmak için masaüstü pencerelemeye geçin"</string>
<string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Uygulama menüsünden dilediğiniz zaman tam ekrana dönebilirsiniz"</string>
<string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Daha fazlasını görün ve yapın"</string>
<string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Bölünmüş ekran için başka bir uygulamayı sürükleyin"</string>
@@ -122,8 +121,7 @@
<string name="handle_text" msgid="4419667835599523257">"Uygulama tanıtıcısı"</string>
<string name="app_icon_text" msgid="2823268023931811747">"Uygulama Simgesi"</string>
<string name="fullscreen_text" msgid="1162316685217676079">"Tam Ekran"</string>
- <!-- no translation found for desktop_text (9058641752519570266) -->
- <skip />
+ <string name="desktop_text" msgid="9058641752519570266">"Masaüstü pencereleme"</string>
<string name="split_screen_text" msgid="1396336058129570886">"Bölünmüş Ekran"</string>
<string name="more_button_text" msgid="3655388105592893530">"Daha Fazla"</string>
<string name="float_button_text" msgid="9221657008391364581">"Havada Süzülen"</string>
@@ -134,10 +132,11 @@
<string name="new_window_text" msgid="6318648868380652280">"Yeni Pencere"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"Pencereleri yönet"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"En boy oranını değiştir"</string>
+ <!-- no translation found for handle_menu_restart_text (3907767216238298098) -->
+ <skip />
<string name="close_text" msgid="4986518933445178928">"Kapat"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Menüyü kapat"</string>
- <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
- <skip />
+ <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (masaüstü pencereleme)"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Ekranı Büyüt"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Yeniden boyutlandır"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Uygulama buraya taşınamıyor"</string>
diff --git a/libs/WindowManager/Shell/res/values-uk/strings.xml b/libs/WindowManager/Shell/res/values-uk/strings.xml
index 54af06762029..4b2aad06cec6 100644
--- a/libs/WindowManager/Shell/res/values-uk/strings.xml
+++ b/libs/WindowManager/Shell/res/values-uk/strings.xml
@@ -100,8 +100,7 @@
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Проблему не вирішено?\nНатисніть, щоб скасувати зміни"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Немає проблем із камерою? Торкніться, щоб закрити."</string>
<string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Тут ви знайдете меню додатка"</string>
- <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
- <skip />
+ <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"Щоб відкрити кілька додатків одночасно, перейдіть у режим вікон робочого стола"</string>
<string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"З меню додатка можна будь-коли повернутися в повноекранний режим"</string>
<string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Більше простору та можливостей"</string>
<string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Щоб перейти в режим розділення екрана, перетягніть сюди інший додаток"</string>
@@ -122,8 +121,7 @@
<string name="handle_text" msgid="4419667835599523257">"Дескриптор додатка"</string>
<string name="app_icon_text" msgid="2823268023931811747">"Значок додатка"</string>
<string name="fullscreen_text" msgid="1162316685217676079">"На весь екран"</string>
- <!-- no translation found for desktop_text (9058641752519570266) -->
- <skip />
+ <string name="desktop_text" msgid="9058641752519570266">"Режим вікон робочого стола"</string>
<string name="split_screen_text" msgid="1396336058129570886">"Розділити екран"</string>
<string name="more_button_text" msgid="3655388105592893530">"Більше"</string>
<string name="float_button_text" msgid="9221657008391364581">"Плаваюче вікно"</string>
@@ -134,10 +132,11 @@
<string name="new_window_text" msgid="6318648868380652280">"Нове вікно"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"Керувати вікнами"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Змінити формат"</string>
+ <!-- no translation found for handle_menu_restart_text (3907767216238298098) -->
+ <skip />
<string name="close_text" msgid="4986518933445178928">"Закрити"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Закрити меню"</string>
- <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
- <skip />
+ <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (режим вікон робочого стола)"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Розгорнути екран"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Змінити розмір"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Сюди не можна перемістити додаток"</string>
diff --git a/libs/WindowManager/Shell/res/values-ur/strings.xml b/libs/WindowManager/Shell/res/values-ur/strings.xml
index 635bc4086c0d..f9972d229062 100644
--- a/libs/WindowManager/Shell/res/values-ur/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ur/strings.xml
@@ -100,8 +100,7 @@
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"یہ حل نہیں ہوا؟\nلوٹانے کیلئے تھپتھپائیں"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"کوئی کیمرے کا مسئلہ نہیں ہے؟ برخاست کرنے کیلئے تھپتھپائیں۔"</string>
<string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"ایپ کا مینو یہاں پایا جا سکتا ہے"</string>
- <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
- <skip />
+ <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"متعدد ایپس کو ایک ساتھ کھولنے کے لیے ڈیسک ٹاپ ونڈوئنگ میں داخل ہوں"</string>
<string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"ایپ مینو سے کسی بھی وقت فُل اسکرین پر واپس جائیں"</string>
<string name="letterbox_education_dialog_title" msgid="7739895354143295358">"دیکھیں اور بہت کچھ کریں"</string>
<string name="letterbox_education_split_screen_text" msgid="449233070804658627">"اسپلٹ اسکرین کے ليے دوسری ایپ میں گھسیٹیں"</string>
@@ -122,8 +121,7 @@
<string name="handle_text" msgid="4419667835599523257">"ایپ ہینڈل"</string>
<string name="app_icon_text" msgid="2823268023931811747">"ایپ کا آئیکن"</string>
<string name="fullscreen_text" msgid="1162316685217676079">"مکمل اسکرین"</string>
- <!-- no translation found for desktop_text (9058641752519570266) -->
- <skip />
+ <string name="desktop_text" msgid="9058641752519570266">"ڈیسک ٹاپ ونڈوئنگ"</string>
<string name="split_screen_text" msgid="1396336058129570886">"اسپلٹ اسکرین"</string>
<string name="more_button_text" msgid="3655388105592893530">"مزید"</string>
<string name="float_button_text" msgid="9221657008391364581">"فلوٹ"</string>
@@ -134,10 +132,11 @@
<string name="new_window_text" msgid="6318648868380652280">"نئی ونڈو"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"ونڈوز کا نظم کریں"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"تناسبی شرح کو تبدیل کریں"</string>
+ <!-- no translation found for handle_menu_restart_text (3907767216238298098) -->
+ <skip />
<string name="close_text" msgid="4986518933445178928">"بند کریں"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"مینیو بند کریں"</string>
- <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
- <skip />
+ <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (ڈیسک ٹاپ ونڈوئنگ)"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"اسکرین کو بڑا کریں"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"سائز تبدیل کریں"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"ایپ کو یہاں منتقل نہیں کیا جا سکتا"</string>
diff --git a/libs/WindowManager/Shell/res/values-uz/strings.xml b/libs/WindowManager/Shell/res/values-uz/strings.xml
index 6ce2bd859bea..231cb738940b 100644
--- a/libs/WindowManager/Shell/res/values-uz/strings.xml
+++ b/libs/WindowManager/Shell/res/values-uz/strings.xml
@@ -100,8 +100,7 @@
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Tuzatilmadimi?\nQaytarish uchun bosing"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Kamera muammosizmi? Yopish uchun bosing."</string>
<string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Ilova menyusi shu yerda chiqadi"</string>
- <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
- <skip />
+ <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"Bir nechta ilovani birga ochish uchun oynalarni desktop rejimida chiqarish"</string>
<string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Ilova menyusi orqali istalganda butun ekranga qaytish mumkin"</string>
<string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Yana boshqa amallar"</string>
<string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Ekranni ikkiga ajratish uchun boshqa ilovani bu yerga torting"</string>
@@ -122,8 +121,7 @@
<string name="handle_text" msgid="4419667835599523257">"Ilova identifikatori"</string>
<string name="app_icon_text" msgid="2823268023931811747">"Ilova belgisi"</string>
<string name="fullscreen_text" msgid="1162316685217676079">"Butun ekran"</string>
- <!-- no translation found for desktop_text (9058641752519570266) -->
- <skip />
+ <string name="desktop_text" msgid="9058641752519570266">"Desktop rejimidagi oynalar"</string>
<string name="split_screen_text" msgid="1396336058129570886">"Ekranni ikkiga ajratish"</string>
<string name="more_button_text" msgid="3655388105592893530">"Yana"</string>
<string name="float_button_text" msgid="9221657008391364581">"Pufakli"</string>
@@ -134,10 +132,11 @@
<string name="new_window_text" msgid="6318648868380652280">"Yangi oyna"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"Oynalarni boshqarish"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Tomonlar nisbatini oʻzgartirish"</string>
+ <!-- no translation found for handle_menu_restart_text (3907767216238298098) -->
+ <skip />
<string name="close_text" msgid="4986518933445178928">"Yopish"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Menyuni yopish"</string>
- <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
- <skip />
+ <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (Desktop rejimidagi oynalar)"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Ekranni yoyish"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Oʻlchamini oʻzgartirish"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Ilova bu yerga surilmaydi"</string>
diff --git a/libs/WindowManager/Shell/res/values-vi/strings.xml b/libs/WindowManager/Shell/res/values-vi/strings.xml
index 67f80c142ea3..88ef07186dab 100644
--- a/libs/WindowManager/Shell/res/values-vi/strings.xml
+++ b/libs/WindowManager/Shell/res/values-vi/strings.xml
@@ -100,8 +100,7 @@
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Bạn chưa khắc phục vấn đề?\nHãy nhấn để hủy bỏ"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Không có vấn đề với máy ảnh? Hãy nhấn để đóng."</string>
<string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Bạn có thể tìm thấy trình đơn ứng dụng tại đây"</string>
- <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
- <skip />
+ <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"Chuyển sang chế độ cửa sổ trên máy tính để mở nhiều ứng dụng cùng lúc"</string>
<string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Trở về chế độ toàn màn hình bất cứ lúc nào từ trình đơn ứng dụng"</string>
<string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Xem và làm được nhiều việc hơn"</string>
<string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Kéo một ứng dụng khác vào để chia đôi màn hình"</string>
@@ -122,8 +121,7 @@
<string name="handle_text" msgid="4419667835599523257">"Ô điều khiển ứng dụng"</string>
<string name="app_icon_text" msgid="2823268023931811747">"Biểu tượng ứng dụng"</string>
<string name="fullscreen_text" msgid="1162316685217676079">"Toàn màn hình"</string>
- <!-- no translation found for desktop_text (9058641752519570266) -->
- <skip />
+ <string name="desktop_text" msgid="9058641752519570266">"Chế độ cửa sổ trên máy tính"</string>
<string name="split_screen_text" msgid="1396336058129570886">"Chia đôi màn hình"</string>
<string name="more_button_text" msgid="3655388105592893530">"Tuỳ chọn khác"</string>
<string name="float_button_text" msgid="9221657008391364581">"Nổi"</string>
@@ -134,10 +132,11 @@
<string name="new_window_text" msgid="6318648868380652280">"Cửa sổ mới"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"Quản lý cửa sổ"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Thay đổi tỷ lệ khung hình"</string>
+ <!-- no translation found for handle_menu_restart_text (3907767216238298098) -->
+ <skip />
<string name="close_text" msgid="4986518933445178928">"Đóng"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Đóng trình đơn"</string>
- <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
- <skip />
+ <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (Chế độ cửa sổ trên máy tính)"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Mở rộng màn hình"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Đổi kích thước"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Không di chuyển được ứng dụng đến đây"</string>
diff --git a/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml b/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml
index b29711df14b9..a07767d0d09a 100644
--- a/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml
+++ b/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml
@@ -100,8 +100,7 @@
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"没有解决此问题?\n点按即可恢复"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"相机没有问题?点按即可忽略。"</string>
<string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"您可以在此处找到应用菜单"</string>
- <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
- <skip />
+ <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"进入桌面设备窗口化模式可同时打开多个应用"</string>
<string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"随时从应用菜单返回全屏模式"</string>
<string name="letterbox_education_dialog_title" msgid="7739895354143295358">"查看和处理更多任务"</string>
<string name="letterbox_education_split_screen_text" msgid="449233070804658627">"拖入另一个应用,即可使用分屏模式"</string>
@@ -122,8 +121,7 @@
<string name="handle_text" msgid="4419667835599523257">"应用手柄"</string>
<string name="app_icon_text" msgid="2823268023931811747">"应用图标"</string>
<string name="fullscreen_text" msgid="1162316685217676079">"全屏"</string>
- <!-- no translation found for desktop_text (9058641752519570266) -->
- <skip />
+ <string name="desktop_text" msgid="9058641752519570266">"桌面设备窗口化"</string>
<string name="split_screen_text" msgid="1396336058129570886">"分屏"</string>
<string name="more_button_text" msgid="3655388105592893530">"更多"</string>
<string name="float_button_text" msgid="9221657008391364581">"悬浮"</string>
@@ -134,10 +132,11 @@
<string name="new_window_text" msgid="6318648868380652280">"新窗口"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"管理窗口"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"更改宽高比"</string>
+ <!-- no translation found for handle_menu_restart_text (3907767216238298098) -->
+ <skip />
<string name="close_text" msgid="4986518933445178928">"关闭"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"关闭菜单"</string>
- <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
- <skip />
+ <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g>(桌面设备窗口化)"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"最大化屏幕"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"调整大小"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"无法将应用移至此处"</string>
diff --git a/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml b/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml
index 74e6b5c1e491..103ee600ff60 100644
--- a/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml
+++ b/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml
@@ -100,8 +100,7 @@
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"未能修正問題?\n輕按即可還原"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"相機冇問題?㩒一下就可以即可閂咗佢。"</string>
<string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"你可在這裡找到應用程式選單"</string>
- <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
- <skip />
+ <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"進入桌面電腦視窗模式以同時開啟多個應用程式"</string>
<string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"你可隨時從應用程式選單返回全螢幕"</string>
<string name="letterbox_education_dialog_title" msgid="7739895354143295358">"瀏覽更多內容及執行更多操作"</string>
<string name="letterbox_education_split_screen_text" msgid="449233070804658627">"拖入另一個應用程式即可分割螢幕"</string>
@@ -122,8 +121,7 @@
<string name="handle_text" msgid="4419667835599523257">"應用程式控點"</string>
<string name="app_icon_text" msgid="2823268023931811747">"應用程式圖示"</string>
<string name="fullscreen_text" msgid="1162316685217676079">"全螢幕"</string>
- <!-- no translation found for desktop_text (9058641752519570266) -->
- <skip />
+ <string name="desktop_text" msgid="9058641752519570266">"桌面電腦視窗模式"</string>
<string name="split_screen_text" msgid="1396336058129570886">"分割螢幕"</string>
<string name="more_button_text" msgid="3655388105592893530">"更多"</string>
<string name="float_button_text" msgid="9221657008391364581">"浮動"</string>
@@ -134,10 +132,11 @@
<string name="new_window_text" msgid="6318648868380652280">"新視窗"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"管理視窗"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"變更長寬比"</string>
+ <!-- no translation found for handle_menu_restart_text (3907767216238298098) -->
+ <skip />
<string name="close_text" msgid="4986518933445178928">"關閉"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"關閉選單"</string>
- <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
- <skip />
+ <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (桌面電腦視窗模式)"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"畫面最大化"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"調整大小"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"應用程式無法移至這裡"</string>
diff --git a/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml b/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml
index 575b217229bd..a3d81fc6f4f0 100644
--- a/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml
+++ b/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml
@@ -100,8 +100,7 @@
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"未修正問題嗎?\n輕觸即可還原"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"相機沒問題嗎?輕觸即可關閉。"</string>
<string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"你可以在這裡查看應用程式選單"</string>
- <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
- <skip />
+ <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"進入電腦分割視窗模式後,可同時開啟多個應用程式"</string>
<string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"你隨時可以從應用程式選單返回全螢幕模式"</string>
<string name="letterbox_education_dialog_title" msgid="7739895354143295358">"瀏覽更多內容及執行更多操作"</string>
<string name="letterbox_education_split_screen_text" msgid="449233070804658627">"拖進另一個應用程式即可使用分割畫面模式"</string>
@@ -122,8 +121,7 @@
<string name="handle_text" msgid="4419667835599523257">"應用程式控制代碼"</string>
<string name="app_icon_text" msgid="2823268023931811747">"應用程式圖示"</string>
<string name="fullscreen_text" msgid="1162316685217676079">"全螢幕"</string>
- <!-- no translation found for desktop_text (9058641752519570266) -->
- <skip />
+ <string name="desktop_text" msgid="9058641752519570266">"電腦分割視窗"</string>
<string name="split_screen_text" msgid="1396336058129570886">"分割畫面"</string>
<string name="more_button_text" msgid="3655388105592893530">"更多"</string>
<string name="float_button_text" msgid="9221657008391364581">"浮動"</string>
@@ -134,10 +132,11 @@
<string name="new_window_text" msgid="6318648868380652280">"新視窗"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"管理視窗"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"變更顯示比例"</string>
+ <!-- no translation found for handle_menu_restart_text (3907767216238298098) -->
+ <skip />
<string name="close_text" msgid="4986518933445178928">"關閉"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"關閉選單"</string>
- <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
- <skip />
+ <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (電腦分割視窗)"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"畫面最大化"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"調整大小"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"應用程式無法移至此處"</string>
diff --git a/libs/WindowManager/Shell/res/values-zu/strings.xml b/libs/WindowManager/Shell/res/values-zu/strings.xml
index 30403cd21862..81d9200d6938 100644
--- a/libs/WindowManager/Shell/res/values-zu/strings.xml
+++ b/libs/WindowManager/Shell/res/values-zu/strings.xml
@@ -100,8 +100,7 @@
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Akuyilungisanga?\nThepha ukuze ubuyele"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Azikho izinkinga zekhamera? Thepha ukuze ucashise."</string>
<string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Imenyu ye-app ingatholakala lapha"</string>
- <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) -->
- <skip />
+ <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"Faka ukwenziwa kwamawindi amaningi kwedeskithophu ukuze uvule ama-app amaningi ndawonye"</string>
<string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Buyela esikrinini esigcwele noma nini ukusuka kumenyu ye-app"</string>
<string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Bona futhi wenze okuningi"</string>
<string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Hudula kwenye i-app mayelana nokuhlukanisa isikrini"</string>
@@ -122,8 +121,7 @@
<string name="handle_text" msgid="4419667835599523257">"Inkomba ye-App"</string>
<string name="app_icon_text" msgid="2823268023931811747">"Isithonjana Se-app"</string>
<string name="fullscreen_text" msgid="1162316685217676079">"Isikrini esigcwele"</string>
- <!-- no translation found for desktop_text (9058641752519570266) -->
- <skip />
+ <string name="desktop_text" msgid="9058641752519570266">"Ukwenziwa kwamawindi amaningi kwedeskithophu"</string>
<string name="split_screen_text" msgid="1396336058129570886">"Hlukanisa isikrini"</string>
<string name="more_button_text" msgid="3655388105592893530">"Okwengeziwe"</string>
<string name="float_button_text" msgid="9221657008391364581">"Iflowuthi"</string>
@@ -134,10 +132,11 @@
<string name="new_window_text" msgid="6318648868380652280">"Iwindi Elisha"</string>
<string name="manage_windows_text" msgid="5567366688493093920">"Phatha Amawindi"</string>
<string name="change_aspect_ratio_text" msgid="9104456064548212806">"Shintsha ukubukeka kwesilinganiselo"</string>
+ <!-- no translation found for handle_menu_restart_text (3907767216238298098) -->
+ <skip />
<string name="close_text" msgid="4986518933445178928">"Vala"</string>
<string name="collapse_menu_text" msgid="7515008122450342029">"Vala Imenyu"</string>
- <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) -->
- <skip />
+ <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (Ukwenziwa kwamawindi amaningi kwedeskithophu)"</string>
<string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Khulisa Isikrini Sifike Ekugcineni"</string>
<string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Shintsha usayizi"</string>
<string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"I-app ayikwazi ukuhanjiswa lapha"</string>
diff --git a/libs/WindowManager/Shell/res/values/colors.xml b/libs/WindowManager/Shell/res/values/colors.xml
index 8d18f959951b..5732fc936b47 100644
--- a/libs/WindowManager/Shell/res/values/colors.xml
+++ b/libs/WindowManager/Shell/res/values/colors.xml
@@ -68,8 +68,6 @@
<color name="desktop_mode_caption_button_on_hover_light">#11000000</color>
<color name="desktop_mode_caption_button_on_hover_dark">#11FFFFFF</color>
<color name="desktop_mode_caption_button">#00000000</color>
- <color name="tiling_divider_background_light">#C9C7B6</color>
- <color name="tiling_divider_background_dark">#4A4739</color>
<color name="tiling_handle_background_light">#000000</color>
<color name="tiling_handle_background_dark">#FFFFFF</color>
</resources>
diff --git a/libs/WindowManager/Shell/res/values/dimen.xml b/libs/WindowManager/Shell/res/values/dimen.xml
index e1bf6638a9b2..ca18c97f9127 100644
--- a/libs/WindowManager/Shell/res/values/dimen.xml
+++ b/libs/WindowManager/Shell/res/values/dimen.xml
@@ -294,6 +294,8 @@
<dimen name="bubble_bar_expanded_view_drop_target_padding_top">60dp</dimen>
<dimen name="bubble_bar_expanded_view_drop_target_padding_bottom">24dp</dimen>
<dimen name="bubble_bar_expanded_view_drop_target_padding_horizontal">48dp</dimen>
+ <dimen name="bubble_bar_drop_target_width">84dp</dimen>
+ <dimen name="bubble_bar_drop_target_height">48dp</dimen>
<!-- Width of the box around bottom center of the screen where drag only leads to dismiss -->
<dimen name="bubble_bar_dismiss_zone_width">192dp</dimen>
<!-- Height of the box around bottom center of the screen where drag only leads to dismiss -->
@@ -302,6 +304,8 @@
<dimen name="bubble_transform_area_width">140dp</dimen>
<!-- Width of the box at the corner of the screen where drag leads to app moving to bubble -->
<dimen name="bubble_transform_area_height">140dp</dimen>
+ <!-- How much elevation a bubble ui needs when dragged, must be above drop target & dismiss. -->
+ <dimen name="dragged_bubble_elevation">3dp</dimen>
<!-- Bottom and end margin for compat buttons. -->
<dimen name="compat_button_margin">24dp</dimen>
@@ -532,10 +536,10 @@
pill elevation. -->
<dimen name="desktop_mode_handle_menu_width">218dp</dimen>
- <!-- The maximum height of the handle menu in desktop mode. Three pills at 52dp each plus
- additional actions pill 208dp plus 2dp spacing between them plus 4dp top padding
- plus 2dp bottom padding: 52*3 + 52*4 + (4-1)*2 + 4 + 2 = 376 -->
- <dimen name="desktop_mode_handle_menu_height">376dp</dimen>
+ <!-- The maximum height of the handle menu in desktop mode. Three pills at 52dp each,
+ additional actions pill 260dp, plus 2dp spacing between them plus 4dp top padding.
+ 52*3 + 52*5 + (5-1)*2 + 4 = 428 -->
+ <dimen name="desktop_mode_handle_menu_height">428dp</dimen>
<!-- The elevation set on the handle menu pills. -->
<dimen name="desktop_mode_handle_menu_pill_elevation">1dp</dimen>
@@ -564,6 +568,9 @@
<!-- The height of the handle menu's "Change aspect ratio" pill in desktop mode. -->
<dimen name="desktop_mode_handle_menu_change_aspect_ratio_height">52dp</dimen>
+ <!-- The height of the handle menu's "Optimize View" pill in desktop mode. -->
+ <dimen name="desktop_mode_handle_menu_restart_button_height">52dp</dimen>
+
<!-- The margin between pills of the handle menu in desktop mode. -->
<dimen name="desktop_mode_handle_menu_pill_spacing_margin">2dp</dimen>
diff --git a/libs/WindowManager/Shell/res/values/strings.xml b/libs/WindowManager/Shell/res/values/strings.xml
index 5ef83826840b..1fd4704f7814 100644
--- a/libs/WindowManager/Shell/res/values/strings.xml
+++ b/libs/WindowManager/Shell/res/values/strings.xml
@@ -314,6 +314,8 @@
<string name="manage_windows_text">Manage Windows</string>
<!-- Accessibility text for the handle menu change aspect ratio button [CHAR LIMIT=NONE] -->
<string name="change_aspect_ratio_text">Change aspect ratio</string>
+ <!-- Accessibility text for the handle menu restart button [CHAR LIMIT=NONE] -->
+ <string name="handle_menu_restart_text">Optimize View</string>
<!-- Accessibility text for the handle menu close button [CHAR LIMIT=NONE] -->
<string name="close_text">Close</string>
<!-- Accessibility text for the handle menu close menu button [CHAR LIMIT=NONE] -->
diff --git a/libs/WindowManager/Shell/res/values/styles.xml b/libs/WindowManager/Shell/res/values/styles.xml
index 5f1db83d7acb..08cda7b94a78 100644
--- a/libs/WindowManager/Shell/res/values/styles.xml
+++ b/libs/WindowManager/Shell/res/values/styles.xml
@@ -45,7 +45,13 @@
<item name="android:layout_height">52dp</item>
<item name="android:textColor">@androidprv:color/materialColorOnSurface</item>
<item name="android:drawableTint">@androidprv:color/materialColorOnSurface</item>
- <item name="android:importantForAccessibility">no</item>
+ <item name="android:importantForAccessibility">yes</item>
+ <item name="android:gravity">start|center_vertical</item>
+ <item name="android:paddingHorizontal">16dp</item>
+ <item name="android:clickable">true</item>
+ <item name="android:focusable">true</item>
+ <item name="android:orientation">horizontal</item>
+ <item name="android:background">?android:attr/selectableItemBackground</item>
</style>
<style name="DesktopModeHandleMenuActionButtonImage">
diff --git a/libs/WindowManager/Shell/shared/res/values/dimen.xml b/libs/WindowManager/Shell/shared/res/values/dimen.xml
index 3b504cf713f1..74b6023bde36 100644
--- a/libs/WindowManager/Shell/shared/res/values/dimen.xml
+++ b/libs/WindowManager/Shell/shared/res/values/dimen.xml
@@ -39,9 +39,9 @@
<dimen name="drag_zone_v_split_from_expanded_view_height_fold_short">100dp</dimen>
<!-- Bubble drop target dimensions -->
- <dimen name="drop_target_elevation">1dp</dimen>
+ <dimen name="drop_target_elevation">2dp</dimen>
<dimen name="drop_target_radius">28dp</dimen>
- <dimen name="drop_target_stroke">1dp</dimen>
+ <dimen name="drop_target_stroke">2dp</dimen>
<dimen name="drop_target_full_screen_padding">20dp</dimen>
<dimen name="drop_target_desktop_window_padding_small">100dp</dimen>
<dimen name="drop_target_desktop_window_padding_large">130dp</dimen>
diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/GroupedTaskInfo.java b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/GroupedTaskInfo.java
index 25b9f8ccc6ae..f68afea92850 100644
--- a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/GroupedTaskInfo.java
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/GroupedTaskInfo.java
@@ -18,6 +18,7 @@ package com.android.wm.shell.shared;
import static android.app.WindowConfiguration.windowingModeToString;
import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
+import static android.view.Display.INVALID_DISPLAY;
import android.annotation.IntDef;
import android.app.ActivityManager.RecentTaskInfo;
@@ -65,6 +66,11 @@ public class GroupedTaskInfo implements Parcelable {
private final int mDeskId;
/**
+ * The ID of the display that desk with [mDeskId] is in.
+ */
+ private final int mDeskDisplayId;
+
+ /**
* The type of this particular task info, can be one of TYPE_FULLSCREEN, TYPE_SPLIT or
* TYPE_DESK.
*/
@@ -109,17 +115,19 @@ public class GroupedTaskInfo implements Parcelable {
* Create new for a stack of fullscreen tasks
*/
public static GroupedTaskInfo forFullscreenTasks(@NonNull TaskInfo task) {
- return new GroupedTaskInfo(/* deskId = */ -1, List.of(task), null, TYPE_FULLSCREEN,
- /* minimizedFreeformTaskIds = */ null);
+ return new GroupedTaskInfo(/* deskId = */ -1, /* displayId = */ INVALID_DISPLAY,
+ List.of(task), null,
+ TYPE_FULLSCREEN, /* minimizedFreeformTaskIds = */ null);
}
/**
* Create new for a pair of tasks in split screen
*/
public static GroupedTaskInfo forSplitTasks(@NonNull TaskInfo task1,
- @NonNull TaskInfo task2, @NonNull SplitBounds splitBounds) {
- return new GroupedTaskInfo(/* deskId = */ -1, List.of(task1, task2), splitBounds,
- TYPE_SPLIT, /* minimizedFreeformTaskIds = */ null);
+ @NonNull TaskInfo task2, @NonNull SplitBounds splitBounds) {
+ return new GroupedTaskInfo(/* deskId = */ -1, /* displayId = */ INVALID_DISPLAY,
+ List.of(task1, task2),
+ splitBounds, TYPE_SPLIT, /* minimizedFreeformTaskIds = */ null);
}
/**
@@ -127,9 +135,11 @@ public class GroupedTaskInfo implements Parcelable {
*/
public static GroupedTaskInfo forDeskTasks(
int deskId,
+ int deskDisplayId,
@NonNull List<TaskInfo> tasks,
@NonNull Set<Integer> minimizedFreeformTaskIds) {
- return new GroupedTaskInfo(deskId, tasks, /* splitBounds = */ null, TYPE_DESK,
+ return new GroupedTaskInfo(deskId, deskDisplayId, tasks, /* splitBounds = */ null,
+ TYPE_DESK,
minimizedFreeformTaskIds.stream().mapToInt(i -> i).toArray());
}
@@ -149,11 +159,13 @@ public class GroupedTaskInfo implements Parcelable {
private GroupedTaskInfo(
int deskId,
+ int deskDisplayId,
@NonNull List<TaskInfo> tasks,
@Nullable SplitBounds splitBounds,
@GroupType int type,
@Nullable int[] minimizedFreeformTaskIds) {
mDeskId = deskId;
+ mDeskDisplayId = deskDisplayId;
mTasks = tasks;
mGroupedTasks = null;
mSplitBounds = splitBounds;
@@ -164,6 +176,7 @@ public class GroupedTaskInfo implements Parcelable {
private GroupedTaskInfo(@NonNull List<GroupedTaskInfo> groupedTasks) {
mDeskId = -1;
+ mDeskDisplayId = INVALID_DISPLAY;
mTasks = null;
mGroupedTasks = groupedTasks;
mSplitBounds = null;
@@ -185,6 +198,7 @@ public class GroupedTaskInfo implements Parcelable {
protected GroupedTaskInfo(@NonNull Parcel parcel) {
mDeskId = parcel.readInt();
+ mDeskDisplayId = parcel.readInt();
mTasks = new ArrayList();
final int numTasks = parcel.readInt();
for (int i = 0; i < numTasks; i++) {
@@ -295,6 +309,16 @@ public class GroupedTaskInfo implements Parcelable {
}
/**
+ * Returns the ID of the display that hosts the desk represented by [mDeskId].
+ */
+ public int getDeskDisplayId() {
+ if (mType != TYPE_DESK) {
+ throw new IllegalStateException("No display ID for non desktop task");
+ }
+ return mDeskDisplayId;
+ }
+
+ /**
* Get type of this recents entry. One of {@link GroupType}.
* Note: This is deprecated, callers should use `isBaseType()` and not make assumptions about
* specific group types
@@ -323,6 +347,7 @@ public class GroupedTaskInfo implements Parcelable {
}
GroupedTaskInfo other = (GroupedTaskInfo) obj;
return mDeskId == other.mDeskId
+ && mDeskDisplayId == other.mDeskDisplayId
&& mType == other.mType
&& Objects.equals(mTasks, other.mTasks)
&& Objects.equals(mGroupedTasks, other.mGroupedTasks)
@@ -332,7 +357,7 @@ public class GroupedTaskInfo implements Parcelable {
@Override
public int hashCode() {
- return Objects.hash(mDeskId, mType, mTasks, mGroupedTasks, mSplitBounds,
+ return Objects.hash(mDeskId, mDeskDisplayId, mType, mTasks, mGroupedTasks, mSplitBounds,
Arrays.hashCode(mMinimizedTaskIds));
}
@@ -345,6 +370,7 @@ public class GroupedTaskInfo implements Parcelable {
.collect(Collectors.joining(",\n\t", "[\n\t", "\n]")));
} else {
taskString.append("Desk ID= ").append(mDeskId).append(", ");
+ taskString.append("Desk Display ID=").append(mDeskDisplayId).append(", ");
taskString.append("Tasks=" + mTasks.stream()
.map(taskInfo -> getTaskInfoDumpString(taskInfo))
.collect(Collectors.joining(", ", "[", "]")));
@@ -377,6 +403,7 @@ public class GroupedTaskInfo implements Parcelable {
@Override
public void writeToParcel(Parcel parcel, int flags) {
parcel.writeInt(mDeskId);
+ parcel.writeInt(mDeskDisplayId);
// We don't use the parcel list methods because we want to only write the TaskInfo state
// and not the subclasses (Recents/RunningTaskInfo) whose fields are all deprecated
final int tasksSize = mTasks != null ? mTasks.size() : 0;
diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/IHomeTransitionListener.aidl b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/IHomeTransitionListener.aidl
index 8481c446c6aa..8dcda53b602b 100644
--- a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/IHomeTransitionListener.aidl
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/IHomeTransitionListener.aidl
@@ -18,6 +18,7 @@ package com.android.wm.shell.shared;
import android.window.RemoteTransition;
import android.window.TransitionFilter;
+import android.view.InsetsState;
/**
* Listener interface that Launcher attaches to SystemUI to get home activity transition callbacks
@@ -29,5 +30,10 @@ oneway interface IHomeTransitionListener {
* Called when a transition changes the visibility of the home activity on the default display.
*/
void onHomeVisibilityChanged(in boolean isVisible);
+
+ /**
+ * Called when the insets at display-level change.
+ */
+ void onDisplayInsetsChanged(in InsetsState insets);
}
diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/BubbleDropTargetBoundsProvider.kt b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/BubbleDropTargetBoundsProvider.kt
index 9bee11a92430..84e0fbe96de2 100644
--- a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/BubbleDropTargetBoundsProvider.kt
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/BubbleDropTargetBoundsProvider.kt
@@ -26,4 +26,9 @@ interface BubbleDropTargetBoundsProvider {
* Get bubble bar expanded view visual drop target bounds on screen
*/
fun getBubbleBarExpandedViewDropTargetBounds(onLeft: Boolean): Rect
+
+ /**
+ * Get the bar visual drop target bounds on screen
+ */
+ fun getBarDropTargetBounds(onLeft: Boolean): Rect
} \ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/ContextUtils.kt b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/ContextUtils.kt
index 0b36f452348a..27db5297b758 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/ContextUtils.kt
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/ContextUtils.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.wm.shell.bubbles
+package com.android.wm.shell.shared.bubbles
import android.content.Context
import android.view.View
diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/DropTargetView.kt b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/DropTargetView.kt
index 73277310ffe4..df101fe44b75 100644
--- a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/DropTargetView.kt
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/DropTargetView.kt
@@ -30,15 +30,15 @@ import com.android.wm.shell.shared.R
class DropTargetView(context: Context) : View(context) {
private val rectPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
- color = context.getColor(com.android.internal.R.color.materialColorPrimaryContainer)
+ color = context.getColor(com.android.internal.R.color.materialColorPrimaryFixed)
style = Paint.Style.FILL
alpha = (0.35f * 255).toInt()
}
private val strokePaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
- color = context.getColor(com.android.internal.R.color.materialColorPrimaryContainer)
+ color = context.getColor(com.android.internal.R.color.materialColorPrimaryFixed)
style = Paint.Style.STROKE
- strokeWidth = 1.dpToPx()
+ strokeWidth = 2.dpToPx()
}
private val cornerRadius = 28.dpToPx()
diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeStatus.java b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeStatus.java
index ed5e0c608675..a9224b02ad31 100644
--- a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeStatus.java
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeStatus.java
@@ -278,6 +278,9 @@ public class DesktopModeStatus {
if (!canEnterDesktopMode(context)) {
return false;
}
+ if (!enforceDeviceRestrictions()) {
+ return true;
+ }
if (display.getType() == Display.TYPE_INTERNAL) {
return canInternalDisplayHostDesktops(context);
}
@@ -448,6 +451,6 @@ public class DesktopModeStatus {
pw.println(maxTaskLimitHandle == null ? "null" : maxTaskLimitHandle.getInt(/* def= */ -1));
pw.print(innerPrefix); pw.print("showAppHandle config override=");
- pw.print(overridesShowAppHandle(context));
+ pw.println(overridesShowAppHandle(context));
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/animation/SizeChangeAnimation.java b/libs/WindowManager/Shell/src/com/android/wm/shell/animation/SizeChangeAnimation.java
index 8e78686ac13d..8e3dc4c36c1d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/animation/SizeChangeAnimation.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/animation/SizeChangeAnimation.java
@@ -66,7 +66,7 @@ public class SizeChangeAnimation {
* The maximum of stretching applied to any surface during interpolation (since the animation
* is a combination of stretching/cropping/fading).
*/
- private static final float SCALE_FACTOR = 0.7f;
+ private static final float DEFAULT_SCALE_FACTOR = 0.7f;
/**
* Since this animation is made of several sub-animations, we want to pre-arrange the
@@ -82,13 +82,27 @@ public class SizeChangeAnimation {
*/
private static final int ANIMATION_RESOLUTION = 1000;
+ /**
+ * Initialize a size-change animation from start to end bounds
+ */
public SizeChangeAnimation(Rect startBounds, Rect endBounds) {
- this(startBounds, endBounds, 1f);
+ this(startBounds, endBounds, 1f, DEFAULT_SCALE_FACTOR);
}
- public SizeChangeAnimation(Rect startBounds, Rect endBounds, float initialScale) {
- mAnimation = buildContainerAnimation(startBounds, endBounds, initialScale);
- mSnapshotAnim = buildSnapshotAnimation(startBounds, endBounds);
+ /**
+ * Initialize a size-change animation from start to end bounds.
+ * <p>
+ * Allows specifying the initial scale factor, {@code initialScale}, that is applied to the
+ * start bounds. This can be useful for example when a task is scaled down when the size change
+ * animation starts.
+ * <p>
+ * By default the max scale applied to any surface is {@link #DEFAULT_SCALE_FACTOR}. Use
+ * {@code scaleFactor} to override it.
+ */
+ public SizeChangeAnimation(Rect startBounds, Rect endBounds, float initialScale,
+ float scaleFactor) {
+ mAnimation = buildContainerAnimation(startBounds, endBounds, initialScale, scaleFactor);
+ mSnapshotAnim = buildSnapshotAnimation(startBounds, endBounds, scaleFactor);
}
/**
@@ -172,15 +186,15 @@ public class SizeChangeAnimation {
/** Animation for the whole container (snapshot is inside this container). */
private static AnimationSet buildContainerAnimation(Rect startBounds, Rect endBounds,
- float initialScale) {
+ float initialScale, float scaleFactor) {
final long duration = ANIMATION_RESOLUTION;
boolean growing = endBounds.width() - startBounds.width()
+ endBounds.height() - startBounds.height() >= 0;
- long scalePeriod = (long) (duration * SCALE_FACTOR);
- float startScaleX = SCALE_FACTOR * ((float) startBounds.width()) / endBounds.width()
- + (1.f - SCALE_FACTOR);
- float startScaleY = SCALE_FACTOR * ((float) startBounds.height()) / endBounds.height()
- + (1.f - SCALE_FACTOR);
+ long scalePeriod = (long) (duration * scaleFactor);
+ float startScaleX = scaleFactor * ((float) startBounds.width()) / endBounds.width()
+ + (1.f - scaleFactor);
+ float startScaleY = scaleFactor * ((float) startBounds.height()) / endBounds.height()
+ + (1.f - scaleFactor);
final AnimationSet animSet = new AnimationSet(true);
final Animation scaleAnim = new ScaleAnimation(startScaleX, 1, startScaleY, 1);
@@ -218,15 +232,16 @@ public class SizeChangeAnimation {
}
/** The snapshot surface is assumed to be a child of the container surface. */
- private static AnimationSet buildSnapshotAnimation(Rect startBounds, Rect endBounds) {
+ private static AnimationSet buildSnapshotAnimation(Rect startBounds, Rect endBounds,
+ float scaleFactor) {
final long duration = ANIMATION_RESOLUTION;
boolean growing = endBounds.width() - startBounds.width()
+ endBounds.height() - startBounds.height() >= 0;
- long scalePeriod = (long) (duration * SCALE_FACTOR);
- float endScaleX = 1.f / (SCALE_FACTOR * ((float) startBounds.width()) / endBounds.width()
- + (1.f - SCALE_FACTOR));
- float endScaleY = 1.f / (SCALE_FACTOR * ((float) startBounds.height()) / endBounds.height()
- + (1.f - SCALE_FACTOR));
+ long scalePeriod = (long) (duration * scaleFactor);
+ float endScaleX = 1.f / (scaleFactor * ((float) startBounds.width()) / endBounds.width()
+ + (1.f - scaleFactor));
+ float endScaleY = 1.f / (scaleFactor * ((float) startBounds.height()) / endBounds.height()
+ + (1.f - scaleFactor));
AnimationSet snapAnimSet = new AnimationSet(true);
// Animation for the "old-state" snapshot that is atop the task.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
index 7f8cfaeb9c03..746632f67725 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
@@ -286,6 +286,7 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
this::createExternalInterface, this);
mShellCommandHandler.addDumpCallback(this::dump, this);
mShellController.addConfigurationChangeListener(this);
+ registerBackGestureDelegate();
}
public BackAnimation getBackAnimationImpl() {
@@ -332,7 +333,11 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
@Override
public void onThresholdCrossed() {
- BackAnimationController.this.onThresholdCrossed();
+ if (predictiveBackDelayWmTransition()) {
+ mShellExecutor.execute(BackAnimationController.this::onThresholdCrossed);
+ } else {
+ BackAnimationController.this.onThresholdCrossed();
+ }
}
@Override
@@ -448,7 +453,7 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
final boolean shouldDispatchToAnimator = shouldDispatchToAnimator();
if (!shouldDispatchToAnimator && mActiveCallback != null) {
mCurrentTracker.updateStartLocation();
- tryDispatchOnBackStarted(mActiveCallback, mCurrentTracker.createStartEvent());
+ tryDispatchOnBackStarted(mActiveCallback, mCurrentTracker.createStartEvent(null));
if (mBackNavigationInfo != null && !isAppProgressGenerationAllowed()) {
tryPilferPointers();
}
@@ -504,7 +509,11 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
}
mShouldStartOnNextMoveEvent = false;
} else {
- mShouldStartOnNextMoveEvent = true;
+ if (predictiveBackDelayWmTransition()) {
+ onGestureStarted(touchX, touchY, swipeEdge);
+ } else {
+ mShouldStartOnNextMoveEvent = true;
+ }
}
}
} else if (keyAction == MotionEvent.ACTION_MOVE) {
@@ -604,7 +613,7 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
mActiveCallback = mBackNavigationInfo.getOnBackInvokedCallback();
// App is handling back animation. Cancel system animation latency tracking.
cancelLatencyTracking();
- tryDispatchOnBackStarted(mActiveCallback, touchTracker.createStartEvent());
+ tryDispatchOnBackStarted(mActiveCallback, touchTracker.createStartEvent(null));
if (!isAppProgressGenerationAllowed()) {
tryPilferPointers();
}
@@ -1041,7 +1050,7 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
() -> mShellExecutor.execute(this::onBackAnimationFinished));
if (mApps.length >= 1) {
- BackMotionEvent startEvent = mCurrentTracker.createStartEvent();
+ BackMotionEvent startEvent = mCurrentTracker.createStartEvent(mApps[0]);
dispatchOnBackStarted(mActiveCallback, startEvent);
if (startEvent.getSwipeEdge() == EDGE_NONE) {
// TODO(b/373544911): onBackStarted is dispatched here so that
@@ -1136,6 +1145,32 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
mBackAnimationAdapter = new BackAnimationAdapter(runner);
}
+ private void registerBackGestureDelegate() {
+ if (!Flags.delegateBackGestureToShell()) {
+ return;
+ }
+ final RemoteCallback requestBackMonitor = new RemoteCallback(
+ new RemoteCallback.OnResultListener() {
+ @Override
+ public void onResult(@Nullable Bundle result) {
+ mShellExecutor.execute(() -> {
+ if (mBackGestureStarted) {
+ Log.w(TAG, "Back gesture is running, ignore request");
+ return;
+ }
+ onMotionEvent(0, 0, KeyEvent.ACTION_DOWN, EDGE_NONE);
+ setTriggerBack(true);
+ onMotionEvent(0, 0, KeyEvent.ACTION_UP, EDGE_NONE);
+ });
+ }
+ });
+ try {
+ mActivityTaskManager.registerBackGestureDelegate(requestBackMonitor);
+ } catch (RemoteException remoteException) {
+ Log.w(TAG, "Failed register back gesture request ", remoteException);
+ }
+ }
+
/**
* Description of current BackAnimationController state.
*/
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
index 81eff6f7399a..70fa48cca0b0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java
@@ -120,6 +120,7 @@ import com.android.wm.shell.shared.bubbles.BubbleBarLocation;
import com.android.wm.shell.shared.bubbles.BubbleBarLocation.UpdateSource;
import com.android.wm.shell.shared.bubbles.BubbleBarUpdate;
import com.android.wm.shell.shared.bubbles.BubbleDropTargetBoundsProvider;
+import com.android.wm.shell.shared.bubbles.ContextUtils;
import com.android.wm.shell.shared.bubbles.DeviceConfig;
import com.android.wm.shell.shared.draganddrop.DragAndDropConstants;
import com.android.wm.shell.sysui.ConfigurationChangeListener;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java
index 426c3ee5b853..290ef1633819 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java
@@ -27,6 +27,7 @@ import static com.android.wm.shell.bubbles.BubbleDebugConfig.TAG_BUBBLES;
import static com.android.wm.shell.bubbles.BubbleDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.wm.shell.bubbles.BubblePositioner.MAX_HEIGHT;
import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_BUBBLES;
+import static com.android.wm.shell.shared.TypefaceUtils.setTypeface;
import android.annotation.NonNull;
import android.annotation.SuppressLint;
@@ -71,6 +72,7 @@ import com.android.wm.shell.Flags;
import com.android.wm.shell.R;
import com.android.wm.shell.common.AlphaOptimizedButton;
import com.android.wm.shell.shared.TriangleShape;
+import com.android.wm.shell.shared.TypefaceUtils;
import com.android.wm.shell.shared.bubbles.BubbleAnythingFlagHelper;
import com.android.wm.shell.taskview.TaskView;
@@ -551,6 +553,7 @@ public class BubbleExpandedView extends LinearLayout {
mManageButton = (AlphaOptimizedButton) LayoutInflater.from(ctw).inflate(
R.layout.bubble_manage_button, this /* parent */, false /* attach */);
addView(mManageButton);
+ setTypeface(mManageButton, TypefaceUtils.FontFamily.GSF_LABEL_LARGE);
mManageButton.setVisibility(visibility);
setManageClickListener();
post(() -> {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleFlyoutView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleFlyoutView.java
index da6948d947d8..92007a4df71e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleFlyoutView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleFlyoutView.java
@@ -50,6 +50,7 @@ import androidx.annotation.Nullable;
import com.android.wm.shell.R;
import com.android.wm.shell.shared.TriangleShape;
+import com.android.wm.shell.shared.TypefaceUtils;
/**
* Flyout view that appears as a 'chat bubble' alongside the bubble stack. The flyout can visually
@@ -165,8 +166,10 @@ public class BubbleFlyoutView extends FrameLayout {
LayoutInflater.from(context).inflate(R.layout.bubble_flyout, this, true);
mFlyoutTextContainer = findViewById(R.id.bubble_flyout_text_container);
mSenderText = findViewById(R.id.bubble_flyout_name);
+ TypefaceUtils.setTypeface(mSenderText, TypefaceUtils.FontFamily.GSF_LABEL_LARGE);
mSenderAvatar = findViewById(R.id.bubble_flyout_avatar);
mMessageText = mFlyoutTextContainer.findViewById(R.id.bubble_flyout_text);
+ TypefaceUtils.setTypeface(mMessageText, TypefaceUtils.FontFamily.GSF_BODY_MEDIUM);
final Resources res = getResources();
mFlyoutPadding = res.getDimensionPixelSize(R.dimen.bubble_flyout_padding_x);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflowContainerView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflowContainerView.java
index 64f54b8ab5be..e901e0c07fe4 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflowContainerView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflowContainerView.java
@@ -46,6 +46,7 @@ import com.android.internal.protolog.ProtoLog;
import com.android.internal.util.ContrastColorUtil;
import com.android.wm.shell.Flags;
import com.android.wm.shell.R;
+import com.android.wm.shell.shared.TypefaceUtils;
import java.util.ArrayList;
import java.util.List;
@@ -234,6 +235,10 @@ public class BubbleOverflowContainerView extends LinearLayout {
setBackgroundColor(bgColor);
mEmptyStateTitle.setTextColor(textColor);
mEmptyStateSubtitle.setTextColor(textColor);
+ TypefaceUtils.setTypeface(mEmptyStateTitle,
+ TypefaceUtils.FontFamily.GSF_BODY_MEDIUM_EMPHASIZED);
+ TypefaceUtils.setTypeface(mEmptyStateSubtitle, TypefaceUtils.FontFamily.GSF_BODY_MEDIUM);
+
}
public void updateFontSize() {
@@ -322,6 +327,7 @@ class BubbleOverflowAdapter extends RecyclerView.Adapter<BubbleOverflowAdapter.V
TextView viewName = overflowView.findViewById(R.id.bubble_view_name);
viewName.setTextColor(textColor);
+ TypefaceUtils.setTypeface(viewName, TypefaceUtils.FontFamily.GSF_LABEL_LARGE);
return new ViewHolder(overflowView, mPositioner);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java
index 03d6b0a8075d..0b45b086e13c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java
@@ -106,6 +106,8 @@ public class BubblePositioner implements BubbleDropTargetBoundsProvider {
private int mBarExpViewDropTargetPaddingTop;
private int mBarExpViewDropTargetPaddingBottom;
private int mBarExpViewDropTargetPaddingHorizontal;
+ private int mBarDropTargetWidth;
+ private int mBarDropTargetHeight;
private PointF mRestingStackPosition;
@@ -181,6 +183,8 @@ public class BubblePositioner implements BubbleDropTargetBoundsProvider {
R.dimen.bubble_bar_expanded_view_drop_target_padding_bottom);
mBarExpViewDropTargetPaddingHorizontal = res.getDimensionPixelSize(
R.dimen.bubble_bar_expanded_view_drop_target_padding_horizontal);
+ mBarDropTargetWidth = res.getDimensionPixelSize(R.dimen.bubble_bar_drop_target_width);
+ mBarDropTargetHeight = res.getDimensionPixelSize(R.dimen.bubble_bar_drop_target_height);
if (mShowingInBubbleBar) {
mExpandedViewLargeScreenWidth = mExpandedViewBubbleBarWidth;
@@ -1003,4 +1007,20 @@ public class BubblePositioner implements BubbleDropTargetBoundsProvider {
);
return bounds;
}
+
+ @NonNull
+ @Override
+ public Rect getBarDropTargetBounds(boolean onLeft) {
+ Rect bounds = getBubbleBarExpandedViewDropTargetBounds(onLeft);
+ bounds.top = getBubbleBarTopOnScreen();
+ bounds.bottom = bounds.top + mBarDropTargetHeight;
+ if (onLeft) {
+ // Keep the left edge from expanded view
+ bounds.right = bounds.left + mBarDropTargetWidth;
+ } else {
+ // Keep the right edge from expanded view
+ bounds.left = bounds.right - mBarDropTargetWidth;
+ }
+ return bounds;
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
index dd5a23aae7f9..4900b6fc77ea 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
@@ -89,9 +89,12 @@ import com.android.wm.shell.bubbles.animation.PhysicsAnimationLayout;
import com.android.wm.shell.bubbles.animation.StackAnimationController;
import com.android.wm.shell.common.FloatingContentCoordinator;
import com.android.wm.shell.common.ShellExecutor;
+import com.android.wm.shell.shared.TypefaceUtils;
+import com.android.wm.shell.shared.TypefaceUtils.FontFamily;
import com.android.wm.shell.shared.animation.Interpolators;
import com.android.wm.shell.shared.animation.PhysicsAnimator;
import com.android.wm.shell.shared.bubbles.BubbleAnythingFlagHelper;
+import com.android.wm.shell.shared.bubbles.ContextUtils;
import com.android.wm.shell.shared.bubbles.DeviceConfig;
import com.android.wm.shell.shared.bubbles.DismissView;
import com.android.wm.shell.shared.bubbles.RelativeTouchListener;
@@ -1397,6 +1400,14 @@ public class BubbleStackView extends FrameLayout
// The menu itself should respect locale direction so the icons are on the correct side.
mManageMenu.setLayoutDirection(LAYOUT_DIRECTION_LOCALE);
addView(mManageMenu);
+
+ // Doesn't seem to work unless view is added; so set font after.
+ TypefaceUtils.setTypeface(findViewById(R.id.manage_dismiss), FontFamily.GSF_LABEL_LARGE);
+ TypefaceUtils.setTypeface(findViewById(R.id.manage_dont_bubble),
+ FontFamily.GSF_LABEL_LARGE);
+ TypefaceUtils.setTypeface(mManageSettingsText, FontFamily.GSF_LABEL_LARGE);
+ TypefaceUtils.setTypeface(findViewById(R.id.bubble_manage_menu_fullscreen_title),
+ FontFamily.GSF_LABEL_LARGE);
}
/**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTransitions.java
index 728975e8ef9f..6f7d7a486453 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTransitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTransitions.java
@@ -649,7 +649,7 @@ public class BubbleTransitions {
@Override
public void continueCollapse() {
mBubble.cleanupTaskView();
- if (mTaskLeash == null || !mTaskLeash.isValid()) return;
+ if (mTaskLeash == null || !mTaskLeash.isValid() || !mRootLeash.isValid()) return;
SurfaceControl.Transaction t = new SurfaceControl.Transaction();
t.reparent(mTaskLeash, mRootLeash);
t.apply();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/ManageEducationView.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/ManageEducationView.kt
index 39a2a7b868a0..d2ad70886fa1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/ManageEducationView.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/ManageEducationView.kt
@@ -27,6 +27,7 @@ import android.widget.Button
import android.widget.LinearLayout
import com.android.internal.R.color.system_neutral1_900
import com.android.wm.shell.R
+import com.android.wm.shell.shared.TypefaceUtils
import com.android.wm.shell.shared.animation.Interpolators
/**
@@ -53,6 +54,12 @@ class ManageEducationView(
init {
LayoutInflater.from(context).inflate(R.layout.bubbles_manage_button_education, this)
+ TypefaceUtils.setTypeface(findViewById(R.id.user_education_title),
+ TypefaceUtils.FontFamily.GSF_HEADLINE_SMALL_EMPHASIZED)
+ TypefaceUtils.setTypeface(findViewById(R.id.user_education_description),
+ TypefaceUtils.FontFamily.GSF_BODY_MEDIUM)
+ TypefaceUtils.setTypeface(manageButton, TypefaceUtils.FontFamily.GSF_LABEL_LARGE_EMPHASIZED)
+ TypefaceUtils.setTypeface(gotItButton, TypefaceUtils.FontFamily.GSF_LABEL_LARGE_EMPHASIZED)
visibility = View.GONE
elevation = resources.getDimensionPixelSize(R.dimen.bubble_elevation).toFloat()
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/StackEducationView.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/StackEducationView.kt
index 16606198b240..9ac059890dd7 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/StackEducationView.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/StackEducationView.kt
@@ -26,6 +26,7 @@ import android.widget.LinearLayout
import android.widget.TextView
import com.android.internal.util.ContrastColorUtil
import com.android.wm.shell.R
+import com.android.wm.shell.shared.TypefaceUtils
import com.android.wm.shell.shared.animation.Interpolators
/**
@@ -59,6 +60,9 @@ class StackEducationView(
init {
LayoutInflater.from(context).inflate(R.layout.bubble_stack_user_education, this)
+ TypefaceUtils.setTypeface(titleTextView,
+ TypefaceUtils.FontFamily.GSF_HEADLINE_SMALL_EMPHASIZED)
+ TypefaceUtils.setTypeface(descTextView, TypefaceUtils.FontFamily.GSF_BODY_MEDIUM)
visibility = View.GONE
elevation = resources.getDimensionPixelSize(R.dimen.bubble_elevation).toFloat()
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelper.java
index fa22a3961002..b89bfd5c969e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelper.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelper.java
@@ -596,11 +596,11 @@ public class BubbleBarAnimationHelper {
final Size size = getExpandedViewSize();
Point position = getExpandedViewRestPosition(size);
- final SizeChangeAnimation sca =
- new SizeChangeAnimation(
- new Rect(origBounds.left - position.x, origBounds.top - position.y,
- origBounds.right - position.x, origBounds.bottom - position.y),
- new Rect(0, 0, size.getWidth(), size.getHeight()), origScale);
+ Rect startBounds = new Rect(origBounds.left - position.x, origBounds.top - position.y,
+ origBounds.right - position.x, origBounds.bottom - position.y);
+ Rect endBounds = new Rect(0, 0, size.getWidth(), size.getHeight());
+ final SizeChangeAnimation sca = new SizeChangeAnimation(startBounds, endBounds,
+ origScale, /* scaleFactor= */ 1f);
sca.initialize(bbev, taskLeash, snapshot, startT);
Animator a = sca.buildViewAnimator(bbev, tvSf, snapshot, /* onFinish */ (va) -> {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedViewDragController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedViewDragController.kt
index 9d4f904e55d0..44d859dfb9ba 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedViewDragController.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedViewDragController.kt
@@ -17,9 +17,11 @@
package com.android.wm.shell.bubbles.bar
import android.annotation.SuppressLint
+import android.content.Context
import android.view.MotionEvent
import android.view.View
import androidx.annotation.VisibleForTesting
+import com.android.wm.shell.R
import com.android.wm.shell.bubbles.BubblePositioner
import com.android.wm.shell.shared.bubbles.BubbleBarLocation
import com.android.wm.shell.shared.bubbles.DismissView
@@ -32,6 +34,7 @@ import com.android.wm.shell.shared.magnetictarget.MagnetizedObject
/** Controller for handling drag interactions with [BubbleBarExpandedView] */
@SuppressLint("ClickableViewAccessibility")
class BubbleBarExpandedViewDragController(
+ private val context: Context,
private val expandedView: BubbleBarExpandedView,
private val dismissView: DismissView,
private val animationHelper: BubbleBarAnimationHelper,
@@ -54,6 +57,8 @@ class BubbleBarExpandedViewDragController(
MagnetizedObject.magnetizeView(expandedView)
private val magnetizedDismissTarget: MagnetizedObject.MagneticTarget
+ private val draggedBubbleElevation: Float
+
init {
magnetizedExpandedView.magnetListener = MagnetListener()
magnetizedExpandedView.animateStuckToTarget =
@@ -70,6 +75,8 @@ class BubbleBarExpandedViewDragController(
MagnetizedObject.MagneticTarget(dismissView.circle, dismissView.circle.width)
magnetizedExpandedView.addTarget(magnetizedDismissTarget)
+ draggedBubbleElevation = context.resources.getDimension(
+ R.dimen.dragged_bubble_elevation)
val dragMotionEventHandler = HandleDragListener()
expandedView.handleView.setOnTouchListener { view, event ->
@@ -103,6 +110,7 @@ class BubbleBarExpandedViewDragController(
override fun onDown(v: View, ev: MotionEvent): Boolean {
// While animating, don't allow new touch events
if (expandedView.isAnimating) return false
+ expandedView.z = draggedBubbleElevation
if (dropTargetManager != null && dragZoneFactory != null) {
val draggedObject = DraggedObject.ExpandedView(
if (bubblePositioner.isBubbleBarOnLeft) {
@@ -154,11 +162,13 @@ class BubbleBarExpandedViewDragController(
velX: Float,
velY: Float,
) {
+ v.translationZ = 0f
finishDrag()
}
override fun onCancel(v: View, ev: MotionEvent, viewInitialX: Float, viewInitialY: Float) {
isStuckToDismiss = false
+ v.translationZ = 0f
finishDrag()
}
@@ -203,7 +213,11 @@ class BubbleBarExpandedViewDragController(
draggedObject: MagnetizedObject<*>,
) {
dragListener.onReleased(inDismiss = true)
- pinController.onDragEnd()
+ if (dropTargetManager != null) {
+ dropTargetManager.onDragEnded()
+ } else {
+ pinController.onDragEnd()
+ }
dismissView.hide()
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java
index 3997412ab459..dd86725f7944 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java
@@ -147,15 +147,23 @@ public class BubbleBarLayerView extends FrameLayout
Log.w(TAG, "dropped invalid bubble: " + mExpandedBubble);
return;
}
+
+ final boolean isBubbleLeft = zone instanceof DragZone.Bubble.Left;
+ final boolean isBubbleRight = zone instanceof DragZone.Bubble.Right;
+ if (!isBubbleLeft && !isBubbleRight) {
+ // If we didn't finish the "change" animation make sure to animate
+ // it back to the right spot
+ locationChangeListener.onChange(mInitialLocation);
+ }
if (zone instanceof DragZone.FullScreen) {
((Bubble) mExpandedBubble).getTaskView().moveToFullscreen();
// Make sure location change listener is updated with the initial
// location -- even if we "switched sides" during the drag, since
// we've ended up in fullscreen, the location shouldn't change.
locationChangeListener.onRelease(mInitialLocation);
- } else if (zone instanceof DragZone.Bubble.Left) {
+ } else if (isBubbleLeft) {
locationChangeListener.onRelease(BubbleBarLocation.LEFT);
- } else if (zone instanceof DragZone.Bubble.Right) {
+ } else if (isBubbleRight) {
locationChangeListener.onRelease(BubbleBarLocation.RIGHT);
}
}
@@ -189,7 +197,7 @@ public class BubbleBarLayerView extends FrameLayout
@NonNull
@Override
public SplitScreenMode getSplitScreenMode() {
- return SplitScreenMode.NONE;
+ return SplitScreenMode.UNSUPPORTED;
}
},
new DragZoneFactory.DesktopWindowModeChecker() {
@@ -341,6 +349,7 @@ public class BubbleBarLayerView extends FrameLayout
}
};
mDragController = new BubbleBarExpandedViewDragController(
+ mContext,
mExpandedView,
mDismissView,
mAnimationHelper,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarMenuItemView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarMenuItemView.java
index 6c14d83dfafa..bccc6dcd91db 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarMenuItemView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarMenuItemView.java
@@ -25,6 +25,7 @@ import android.widget.LinearLayout;
import android.widget.TextView;
import com.android.wm.shell.R;
+import com.android.wm.shell.shared.TypefaceUtils;
/**
* Bubble bar expanded view menu item view to display menu action details
@@ -55,6 +56,7 @@ public class BubbleBarMenuItemView extends LinearLayout {
super.onFinishInflate();
mImageView = findViewById(R.id.bubble_bar_menu_item_icon);
mTextView = findViewById(R.id.bubble_bar_menu_item_title);
+ TypefaceUtils.setTypeface(mTextView, TypefaceUtils.FontFamily.GSF_TITLE_MEDIUM);
}
/**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarMenuView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarMenuView.java
index dfbf655bb6fc..7c0f8e138aae 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarMenuView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarMenuView.java
@@ -33,6 +33,7 @@ import androidx.core.widget.ImageViewCompat;
import com.android.wm.shell.R;
import com.android.wm.shell.bubbles.Bubble;
+import com.android.wm.shell.shared.TypefaceUtils;
import java.util.ArrayList;
@@ -75,6 +76,7 @@ public class BubbleBarMenuView extends LinearLayout {
mActionsSectionView = findViewById(R.id.bubble_bar_manage_menu_actions_section);
mBubbleIconView = findViewById(R.id.bubble_bar_manage_menu_bubble_icon);
mBubbleTitleView = findViewById(R.id.bubble_bar_manage_menu_bubble_title);
+ TypefaceUtils.setTypeface(mBubbleTitleView, TypefaceUtils.FontFamily.GSF_TITLE_MEDIUM);
mBubbleDismissIconView = findViewById(R.id.bubble_bar_manage_menu_dismiss_icon);
updateThemeColors();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleEducationViewController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleEducationViewController.kt
index 7adec39c9d66..0bd3a54ceeaa 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleEducationViewController.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleEducationViewController.kt
@@ -35,6 +35,7 @@ import com.android.wm.shell.bubbles.BubbleDebugConfig.TAG_WITH_CLASS_NAME
import com.android.wm.shell.bubbles.BubbleEducationController
import com.android.wm.shell.bubbles.BubbleViewProvider
import com.android.wm.shell.bubbles.setup
+import com.android.wm.shell.shared.TypefaceUtils
import com.android.wm.shell.shared.animation.PhysicsAnimator
import com.android.wm.shell.shared.bubbles.BubblePopupDrawable
import com.android.wm.shell.shared.bubbles.BubblePopupView
@@ -108,6 +109,10 @@ class BubbleEducationViewController(private val context: Context, private val li
root.getBoundsOnScreen(rootBounds)
educationView =
createEducationView(R.layout.bubble_bar_stack_education, root).apply {
+ TypefaceUtils.setTypeface(findViewById(R.id.education_title),
+ TypefaceUtils.FontFamily.GSF_HEADLINE_SMALL_EMPHASIZED)
+ TypefaceUtils.setTypeface(findViewById(R.id.education_text),
+ TypefaceUtils.FontFamily.GSF_BODY_MEDIUM)
setArrowDirection(BubblePopupDrawable.ArrowDirection.DOWN)
updateEducationPosition(view = this, position, rootBounds)
val arrowToEdgeOffset = popupDrawable?.config?.cornerRadius ?: 0f
@@ -153,6 +158,10 @@ class BubbleEducationViewController(private val context: Context, private val li
educationView =
createEducationView(R.layout.bubble_bar_manage_education, root).apply {
+ TypefaceUtils.setTypeface(findViewById(R.id.education_manage_title),
+ TypefaceUtils.FontFamily.GSF_HEADLINE_SMALL_EMPHASIZED)
+ TypefaceUtils.setTypeface(findViewById(R.id.education_manage_text),
+ TypefaceUtils.FontFamily.GSF_BODY_MEDIUM)
pivotY = 0f
doOnLayout { it.pivotX = it.width / 2f }
setOnClickListener { hideEducation(animated = true) }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayController.java
index 97184c859d4d..46f6e40ec5e8 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayController.java
@@ -287,7 +287,13 @@ public class DisplayController {
? mContext
: mContext.createDisplayContext(display);
final Context context = perDisplayContext.createConfigurationContext(newConfig);
- dr.setDisplayLayout(context, new DisplayLayout(context, display));
+ final DisplayLayout displayLayout = new DisplayLayout(context, display);
+ if (mDisplayTopology != null) {
+ displayLayout.setGlobalBoundsDp(
+ mDisplayTopology.getAbsoluteBounds().get(
+ displayId, displayLayout.globalBoundsDp()));
+ }
+ dr.setDisplayLayout(context, displayLayout);
for (int i = 0; i < mDisplayChangedListeners.size(); ++i) {
mDisplayChangedListeners.get(i).onDisplayConfigurationChanged(
displayId, newConfig);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipBoundsAlgorithm.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipBoundsAlgorithm.java
index 04e8d8dee520..5d603d6c087d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipBoundsAlgorithm.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipBoundsAlgorithm.java
@@ -221,8 +221,11 @@ public class PipBoundsAlgorithm {
+ " than destination(%s)", sourceRectHint, destinationBounds);
return false;
}
- if (!PictureInPictureParams.isSameAspectRatio(sourceRectHint,
- new Rational(destinationBounds.width(), destinationBounds.height()))) {
+ // We use the aspect ratio of source rect hint to check against destination bounds
+ // here to avoid upscaling error.
+ final Rational srcAspectRatio = new Rational(
+ sourceRectHint.width(), sourceRectHint.height());
+ if (!PictureInPictureParams.isSameAspectRatio(destinationBounds, srcAspectRatio)) {
ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
"isSourceRectHintValidForEnterPip=false, hint(%s) does not match"
+ " destination(%s) aspect ratio", sourceRectHint, destinationBounds);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipDesktopState.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipDesktopState.java
deleted file mode 100644
index 1128fb2259b2..000000000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipDesktopState.java
+++ /dev/null
@@ -1,122 +0,0 @@
-/*
- * Copyright (C) 2025 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.wm.shell.common.pip;
-
-import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
-import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
-
-import android.window.DesktopExperienceFlags;
-import android.window.DesktopModeFlags;
-import android.window.DisplayAreaInfo;
-
-import com.android.wm.shell.Flags;
-import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
-import com.android.wm.shell.desktopmode.DesktopUserRepositories;
-import com.android.wm.shell.desktopmode.DragToDesktopTransitionHandler;
-
-import java.util.Optional;
-
-/** Helper class for PiP on Desktop Mode. */
-public class PipDesktopState {
- private final PipDisplayLayoutState mPipDisplayLayoutState;
- private final Optional<DesktopUserRepositories> mDesktopUserRepositoriesOptional;
- private final Optional<DragToDesktopTransitionHandler> mDragToDesktopTransitionHandlerOptional;
- private final RootTaskDisplayAreaOrganizer mRootTaskDisplayAreaOrganizer;
-
- public PipDesktopState(PipDisplayLayoutState pipDisplayLayoutState,
- Optional<DesktopUserRepositories> desktopUserRepositoriesOptional,
- Optional<DragToDesktopTransitionHandler> dragToDesktopTransitionHandlerOptional,
- RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer) {
- mPipDisplayLayoutState = pipDisplayLayoutState;
- mDesktopUserRepositoriesOptional = desktopUserRepositoriesOptional;
- mDragToDesktopTransitionHandlerOptional = dragToDesktopTransitionHandlerOptional;
- mRootTaskDisplayAreaOrganizer = rootTaskDisplayAreaOrganizer;
- }
-
- /**
- * Returns whether PiP in Desktop Windowing is enabled by checking the following:
- * - PiP in Desktop Windowing flag is enabled
- * - DesktopUserRepositories is injected
- * - DragToDesktopTransitionHandler is injected
- */
- public boolean isDesktopWindowingPipEnabled() {
- return DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_PIP.isTrue()
- && mDesktopUserRepositoriesOptional.isPresent()
- && mDragToDesktopTransitionHandlerOptional.isPresent();
- }
-
- /**
- * Returns whether PiP in Connected Displays is enabled by checking the following:
- * - PiP in Connected Displays flag is enabled
- * - PiP2 flag is enabled
- */
- public boolean isConnectedDisplaysPipEnabled() {
- return DesktopExperienceFlags.ENABLE_CONNECTED_DISPLAYS_PIP.isTrue() && Flags.enablePip2();
- }
-
- /** Returns whether the display with the PiP task is in freeform windowing mode. */
- private boolean isDisplayInFreeform() {
- final DisplayAreaInfo tdaInfo = mRootTaskDisplayAreaOrganizer.getDisplayAreaInfo(
- mPipDisplayLayoutState.getDisplayId());
- if (tdaInfo != null) {
- return tdaInfo.configuration.windowConfiguration.getWindowingMode()
- == WINDOWING_MODE_FREEFORM;
- }
- return false;
- }
-
- /** Returns whether PiP is active in a display that is in active Desktop Mode session. */
- public boolean isPipInDesktopMode() {
- // Early return if PiP in Desktop Windowing is not supported.
- if (!isDesktopWindowingPipEnabled()) {
- return false;
- }
- final int displayId = mPipDisplayLayoutState.getDisplayId();
- return mDesktopUserRepositoriesOptional.get().getCurrent().isAnyDeskActive(displayId);
- }
-
- /**
- * The windowing mode to restore to when resizing out of PIP direction.
- * Defaults to undefined and can be overridden to restore to an alternate windowing mode.
- */
- public int getOutPipWindowingMode() {
- // If we are exiting PiP while the device is in Desktop mode (the task should expand to
- // freeform windowing mode):
- // 1) If the display windowing mode is freeform, set windowing mode to UNDEFINED so it will
- // resolve the windowing mode to the display's windowing mode.
- // 2) If the display windowing mode is not FREEFORM, set windowing mode to FREEFORM.
- if (isPipInDesktopMode()) {
- if (isDisplayInFreeform()) {
- return WINDOWING_MODE_UNDEFINED;
- } else {
- return WINDOWING_MODE_FREEFORM;
- }
- }
-
- // By default, or if the task is going to fullscreen, reset the windowing mode to undefined.
- return WINDOWING_MODE_UNDEFINED;
- }
-
- /** Returns whether there is a drag-to-desktop transition in progress. */
- public boolean isDragToDesktopInProgress() {
- // Early return if PiP in Desktop Windowing is not supported.
- if (!isDesktopWindowingPipEnabled()) {
- return false;
- }
- return mDragToDesktopTransitionHandlerOptional.get().getInProgress();
- }
-}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipDesktopState.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipDesktopState.kt
new file mode 100644
index 000000000000..55bde8906b63
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipDesktopState.kt
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2025 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.wm.shell.common.pip
+
+import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM
+import android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED
+import android.window.DesktopExperienceFlags
+import android.window.DesktopModeFlags
+import com.android.wm.shell.Flags
+import com.android.wm.shell.RootTaskDisplayAreaOrganizer
+import com.android.wm.shell.desktopmode.DesktopUserRepositories
+import com.android.wm.shell.desktopmode.DragToDesktopTransitionHandler
+import java.util.Optional
+
+/** Helper class for PiP on Desktop Mode. */
+class PipDesktopState(
+ private val pipDisplayLayoutState: PipDisplayLayoutState,
+ private val desktopUserRepositoriesOptional: Optional<DesktopUserRepositories>,
+ private val dragToDesktopTransitionHandlerOptional: Optional<DragToDesktopTransitionHandler>,
+ private val rootTaskDisplayAreaOrganizer: RootTaskDisplayAreaOrganizer
+) {
+ /**
+ * Returns whether PiP in Desktop Windowing is enabled by checking the following:
+ * - PiP in Desktop Windowing flag is enabled
+ * - DesktopUserRepositories is present
+ * - DragToDesktopTransitionHandler is present
+ */
+ fun isDesktopWindowingPipEnabled(): Boolean =
+ DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_PIP.isTrue &&
+ desktopUserRepositoriesOptional.isPresent &&
+ dragToDesktopTransitionHandlerOptional.isPresent
+
+ /**
+ * Returns whether PiP in Connected Displays is enabled by checking the following:
+ * - PiP in Connected Displays flag is enabled
+ * - PiP2 flag is enabled
+ */
+ fun isConnectedDisplaysPipEnabled(): Boolean =
+ DesktopExperienceFlags.ENABLE_CONNECTED_DISPLAYS_PIP.isTrue && Flags.enablePip2()
+
+ /** Returns whether the display with the PiP task is in freeform windowing mode. */
+ private fun isDisplayInFreeform(): Boolean {
+ val tdaInfo = rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(
+ pipDisplayLayoutState.displayId
+ )
+
+ return tdaInfo?.configuration?.windowConfiguration?.windowingMode == WINDOWING_MODE_FREEFORM
+ }
+
+ /** Returns whether PiP is active in a display that is in active Desktop Mode session. */
+ fun isPipInDesktopMode(): Boolean {
+ if (!isDesktopWindowingPipEnabled()) {
+ return false
+ }
+
+ val displayId = pipDisplayLayoutState.displayId
+ return desktopUserRepositoriesOptional.get().current.isAnyDeskActive(displayId)
+ }
+
+ /** Returns the windowing mode to restore to when resizing out of PIP direction. */
+ // TODO(b/403345629): Update this for Multi-Desktop.
+ fun getOutPipWindowingMode(): Int {
+ // If we are exiting PiP while the device is in Desktop mode, the task should expand to
+ // freeform windowing mode.
+ // 1) If the display windowing mode is freeform, set windowing mode to UNDEFINED so it will
+ // resolve the windowing mode to the display's windowing mode.
+ // 2) If the display windowing mode is not FREEFORM, set windowing mode to FREEFORM.
+ if (isPipInDesktopMode()) {
+ return if (isDisplayInFreeform()) {
+ WINDOWING_MODE_UNDEFINED
+ } else {
+ WINDOWING_MODE_FREEFORM
+ }
+ }
+
+ // By default, or if the task is going to fullscreen, reset the windowing mode to undefined.
+ return WINDOWING_MODE_UNDEFINED
+ }
+
+ /** Returns whether there is a drag-to-desktop transition in progress. */
+ fun isDragToDesktopInProgress(): Boolean =
+ isDesktopWindowingPipEnabled() && dragToDesktopTransitionHandlerOptional.get().inProgress
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerRoundedCorner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerRoundedCorner.java
index cf0ecae7c815..a1e7ff04347f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerRoundedCorner.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerRoundedCorner.java
@@ -106,31 +106,37 @@ public class DividerRoundedCorner extends View {
* of non split screen.
*
* @param isSplitScreen Whether the divider is used by split screen or tiling.
- * @param isDarkMode Whether the mode is ui dark mode.
+ * @param color Rounded corner color.
*/
- public void setup(boolean isSplitScreen, boolean isDarkMode) {
+ public void setup(boolean isSplitScreen, int color) {
mIsSplitScreen = isSplitScreen;
if (!isSplitScreen) {
- mDividerBarBackground.setColor(getTilingHandleColor(isDarkMode));
+ mDividerBarBackground.setColor(color);
}
}
/**
- * Notifies the divider of ui mode change.
+ * Notifies the divider of ui mode change and provides a new color.
*
- * @param isDarkMode Whether the mode is ui dark mode.
+ * @param color The new divider rounded corner color.
*/
- public void onUiModeChange(boolean isDarkMode) {
+ public void onUiModeChange(int color) {
if (!mIsSplitScreen) {
- mDividerBarBackground.setColor(getTilingHandleColor(isDarkMode));
+ mDividerBarBackground.setColor(color);
invalidate();
}
}
- private int getTilingHandleColor(boolean isDarkMode) {
- return isDarkMode ? getResources().getColor(
- R.color.tiling_divider_background_dark, null /* theme */) : getResources().getColor(
- R.color.tiling_divider_background_light, null /* theme */);
+ /**
+ * Notifies rounded corner view of color change.
+ *
+ * @param color The new divider rounded corner color.
+ */
+ public void onCornerColorChange(int color) {
+ if (!mIsSplitScreen) {
+ mDividerBarBackground.setColor(color);
+ invalidate();
+ }
}
/**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/FlexParallaxSpec.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/FlexParallaxSpec.java
index 9fa162164e0e..d9a66e1d64b2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/FlexParallaxSpec.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/FlexParallaxSpec.java
@@ -57,6 +57,11 @@ public class FlexParallaxSpec implements ParallaxSpec {
* @return 0f = no dim applied. 1f = full black.
*/
public float getDimValue(int position, DividerSnapAlgorithm snapAlgorithm) {
+ // On tablets, apps don't go offscreen, so only dim for dismissal.
+ if (!snapAlgorithm.areOffscreenRatiosSupported()) {
+ return ParallaxSpec.super.getDimValue(position, snapAlgorithm);
+ }
+
int startDismissPos = snapAlgorithm.getDismissStartTarget().getPosition();
int firstTargetPos = snapAlgorithm.getFirstSplitTarget().getPosition();
int middleTargetPos = snapAlgorithm.getMiddleTarget().getPosition();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
index fec1f56c76bb..2b0885ed65a1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
@@ -559,7 +559,12 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
true /* setEffectBounds */);
}
- /** Updates recording bounds of divider window and both of the splits. */
+ /**
+ * Updates the bounds of the divider window and both split apps.
+ * @param position The left/top edge of the visual divider, where the edge of app A meets the
+ * divider. Not to be confused with the actual divider surface, which is larger
+ * and overlaps the apps a bit.
+ */
private void updateBounds(int position, Rect bounds1, Rect bounds2, Rect dividerBounds,
boolean setEffectBounds) {
dividerBounds.set(mRootBounds);
@@ -574,10 +579,11 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
// For flexible split, expand app offscreen as well
if (mDividerSnapAlgorithm.areOffscreenRatiosSupported()) {
- if (position <= mDividerSnapAlgorithm.getMiddleTarget().position) {
- bounds1.left = bounds1.right - bounds2.width();
+ int distanceToCenter = position - mDividerSnapAlgorithm.getMiddleTarget().position;
+ if (position < mDividerSnapAlgorithm.getMiddleTarget().position) {
+ bounds1.left += distanceToCenter * 2;
} else {
- bounds2.right = bounds2.left + bounds1.width();
+ bounds2.right += distanceToCenter * 2;
}
}
@@ -590,10 +596,11 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
// For flexible split, expand app offscreen as well
if (mDividerSnapAlgorithm.areOffscreenRatiosSupported()) {
- if (position <= mDividerSnapAlgorithm.getMiddleTarget().position) {
- bounds1.top = bounds1.bottom - bounds2.height();
+ int distanceToCenter = position - mDividerSnapAlgorithm.getMiddleTarget().position;
+ if (position < mDividerSnapAlgorithm.getMiddleTarget().position) {
+ bounds1.top += distanceToCenter * 2;
} else {
- bounds2.bottom = bounds2.top + bounds1.height();
+ bounds2.bottom += distanceToCenter * 2;
}
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java
index 4413c8715c0d..d5f4a3885dbb 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java
@@ -112,6 +112,12 @@ public class CompatUIController implements OnDisplaysChangedListener,
new SparseArray<>(0);
/**
+ * {@link SparseArray} that maps task ids to {@link CompatUIInfo}.
+ */
+ private final SparseArray<CompatUIInfo> mTaskIdToCompatUIInfoMap =
+ new SparseArray<>(0);
+
+ /**
* {@link Set} of task ids for which we need to display a restart confirmation dialog
*/
private Set<Integer> mSetOfTaskIdsShowingRestartDialog = new HashSet<>();
@@ -261,7 +267,11 @@ public class CompatUIController implements OnDisplaysChangedListener,
private void handleDisplayCompatShowRestartDialog(
CompatUIRequests.DisplayCompatShowRestartDialog request) {
- onRestartButtonClicked(new Pair<>(request.getTaskInfo(), request.getTaskListener()));
+ final CompatUIInfo compatUIInfo = mTaskIdToCompatUIInfoMap.get(request.getTaskId());
+ if (compatUIInfo == null) {
+ return;
+ }
+ onRestartButtonClicked(new Pair<>(compatUIInfo.getTaskInfo(), compatUIInfo.getListener()));
}
/**
@@ -273,6 +283,11 @@ public class CompatUIController implements OnDisplaysChangedListener,
public void onCompatInfoChanged(@NonNull CompatUIInfo compatUIInfo) {
final TaskInfo taskInfo = compatUIInfo.getTaskInfo();
final ShellTaskOrganizer.TaskListener taskListener = compatUIInfo.getListener();
+ if (taskListener == null) {
+ mTaskIdToCompatUIInfoMap.delete(taskInfo.taskId);
+ } else {
+ mTaskIdToCompatUIInfoMap.put(taskInfo.taskId, compatUIInfo);
+ }
final boolean isInDisplayCompatMode =
taskInfo.appCompatTaskInfo.isRestartMenuEnabledForDisplayMove();
if (taskInfo != null && !taskInfo.appCompatTaskInfo.isTopActivityInSizeCompat()
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/impl/CompatUIRequests.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/impl/CompatUIRequests.kt
index da4fc99491dc..b7af596ee0ae 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/impl/CompatUIRequests.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/impl/CompatUIRequests.kt
@@ -16,8 +16,6 @@
package com.android.wm.shell.compatui.impl
-import android.app.TaskInfo
-import com.android.wm.shell.ShellTaskOrganizer
import com.android.wm.shell.compatui.api.CompatUIRequest
internal const val DISPLAY_COMPAT_SHOW_RESTART_DIALOG = 0
@@ -27,7 +25,6 @@ internal const val DISPLAY_COMPAT_SHOW_RESTART_DIALOG = 0
*/
sealed class CompatUIRequests(override val requestId: Int) : CompatUIRequest {
/** Sent when the restart handle menu is clicked, and a restart dialog is requested. */
- data class DisplayCompatShowRestartDialog(val taskInfo: TaskInfo,
- val taskListener: ShellTaskOrganizer.TaskListener) :
+ data class DisplayCompatShowRestartDialog(val taskId: Int) :
CompatUIRequests(DISPLAY_COMPAT_SHOW_RESTART_DIALOG)
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
index f62fd819319e..b3c25d495002 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
@@ -762,6 +762,7 @@ public abstract class WMShellBaseModule {
ShellTaskOrganizer organizer,
TransactionPool pool,
DisplayController displayController,
+ DisplayInsetsController displayInsetsController,
@ShellMainThread ShellExecutor mainExecutor,
@ShellMainThread Handler mainHandler,
@ShellAnimationThread ShellExecutor animExecutor,
@@ -773,15 +774,19 @@ public abstract class WMShellBaseModule {
shellInit = new ShellInit(mainExecutor);
}
return new Transitions(context, shellInit, shellCommandHandler, shellController, organizer,
- pool, displayController, mainExecutor, mainHandler, animExecutor,
- rootTaskDisplayAreaOrganizer, homeTransitionObserver, focusTransitionObserver);
+ pool, displayController, displayInsetsController, mainExecutor, mainHandler,
+ animExecutor, rootTaskDisplayAreaOrganizer, homeTransitionObserver,
+ focusTransitionObserver);
}
@WMSingleton
@Provides
static HomeTransitionObserver provideHomeTransitionObserver(Context context,
- @ShellMainThread ShellExecutor mainExecutor) {
- return new HomeTransitionObserver(context, mainExecutor);
+ @ShellMainThread ShellExecutor mainExecutor,
+ DisplayInsetsController displayInsetsController,
+ ShellInit shellInit) {
+ return new HomeTransitionObserver(context, mainExecutor, displayInsetsController,
+ shellInit);
}
@WMSingleton
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
index 67a4d6cf89bf..5fbbb0bf1e78 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
@@ -79,6 +79,7 @@ import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.TaskStackListenerImpl;
import com.android.wm.shell.common.UserProfileContexts;
import com.android.wm.shell.common.split.SplitState;
+import com.android.wm.shell.compatui.api.CompatUIHandler;
import com.android.wm.shell.compatui.letterbox.LetterboxCommandHandler;
import com.android.wm.shell.compatui.letterbox.LetterboxTransitionObserver;
import com.android.wm.shell.crashhandling.ShellCrashHandler;
@@ -89,6 +90,7 @@ import com.android.wm.shell.desktopmode.DefaultDragToDesktopTransitionHandler;
import com.android.wm.shell.desktopmode.DesktopActivityOrientationChangeHandler;
import com.android.wm.shell.desktopmode.DesktopDisplayEventHandler;
import com.android.wm.shell.desktopmode.DesktopDisplayModeController;
+import com.android.wm.shell.desktopmode.DesktopImeHandler;
import com.android.wm.shell.desktopmode.DesktopImmersiveController;
import com.android.wm.shell.desktopmode.DesktopMinimizationTransitionHandler;
import com.android.wm.shell.desktopmode.DesktopMixedTransitionHandler;
@@ -969,8 +971,15 @@ public abstract class WMShellModule {
@WMSingleton
@Provides
- static DesktopModeMoveToDisplayTransitionHandler provideMoveToDisplayTransitionHandler() {
- return new DesktopModeMoveToDisplayTransitionHandler(new SurfaceControl.Transaction());
+ static DesktopModeMoveToDisplayTransitionHandler provideMoveToDisplayTransitionHandler(
+ InteractionJankMonitor interactionJankMonitor,
+ @ShellMainThread Handler shellMainHandler,
+ DisplayController displayController) {
+ return new DesktopModeMoveToDisplayTransitionHandler(
+ new SurfaceControl.Transaction(),
+ interactionJankMonitor,
+ shellMainHandler,
+ displayController);
}
@WMSingleton
@@ -1037,7 +1046,8 @@ public abstract class WMShellModule {
RecentsTransitionHandler recentsTransitionHandler,
DesktopModeCompatPolicy desktopModeCompatPolicy,
DesktopTilingDecorViewModel desktopTilingDecorViewModel,
- MultiDisplayDragMoveIndicatorController multiDisplayDragMoveIndicatorController
+ MultiDisplayDragMoveIndicatorController multiDisplayDragMoveIndicatorController,
+ Optional<CompatUIHandler> compatUI
) {
if (!DesktopModeStatus.canEnterDesktopModeOrShowAppHandle(context)) {
return Optional.empty();
@@ -1055,7 +1065,7 @@ public abstract class WMShellModule {
activityOrientationChangeHandler, focusTransitionObserver, desktopModeEventLogger,
desktopModeUiEventLogger, taskResourceLoader, recentsTransitionHandler,
desktopModeCompatPolicy, desktopTilingDecorViewModel,
- multiDisplayDragMoveIndicatorController));
+ multiDisplayDragMoveIndicatorController, compatUI.orElse(null)));
}
@WMSingleton
@@ -1206,7 +1216,8 @@ public abstract class WMShellModule {
ShellTaskOrganizer shellTaskOrganizer,
TaskStackListenerImpl taskStackListener,
ToggleResizeDesktopTaskTransitionHandler toggleResizeDesktopTaskTransitionHandler,
- @DynamicOverride DesktopUserRepositories desktopUserRepositories) {
+ @DynamicOverride DesktopUserRepositories desktopUserRepositories,
+ DisplayController displayController) {
if (DesktopModeStatus.canEnterDesktopMode(context)) {
return Optional.of(
new DesktopActivityOrientationChangeHandler(
@@ -1215,7 +1226,8 @@ public abstract class WMShellModule {
shellTaskOrganizer,
taskStackListener,
toggleResizeDesktopTaskTransitionHandler,
- desktopUserRepositories));
+ desktopUserRepositories,
+ displayController));
}
return Optional.empty();
}
@@ -1334,7 +1346,9 @@ public abstract class WMShellModule {
Context context,
ShellInit shellInit,
@ShellMainThread CoroutineScope mainScope,
+ ShellController shellController,
DisplayController displayController,
+ RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer,
Optional<DesktopUserRepositories> desktopUserRepositories,
Optional<DesktopTasksController> desktopTasksController,
Optional<DesktopDisplayModeController> desktopDisplayModeController,
@@ -1348,7 +1362,9 @@ public abstract class WMShellModule {
context,
shellInit,
mainScope,
+ shellController,
displayController,
+ rootTaskDisplayAreaOrganizer,
desktopRepositoryInitializer,
desktopUserRepositories.get(),
desktopTasksController.get(),
@@ -1511,6 +1527,18 @@ public abstract class WMShellModule {
mainHandler));
}
+ @WMSingleton
+ @Provides
+ static Optional<DesktopImeHandler> provideDesktopImeHandler(
+ DisplayImeController displayImeController,
+ Context context,
+ ShellInit shellInit) {
+ if (!DesktopModeStatus.canEnterDesktopMode(context)) {
+ return Optional.empty();
+ }
+ return Optional.of(new DesktopImeHandler(displayImeController, shellInit));
+ }
+
//
// App zoom out
//
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopActivityOrientationChangeHandler.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopActivityOrientationChangeHandler.kt
index b8f4bb8d8323..39ce5d9023a6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopActivityOrientationChangeHandler.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopActivityOrientationChangeHandler.kt
@@ -23,10 +23,10 @@ import android.content.pm.ActivityInfo.ScreenOrientation
import android.content.res.Configuration.ORIENTATION_LANDSCAPE
import android.content.res.Configuration.ORIENTATION_PORTRAIT
import android.graphics.Rect
-import android.util.Size
import android.window.WindowContainerTransaction
import com.android.window.flags.Flags
import com.android.wm.shell.ShellTaskOrganizer
+import com.android.wm.shell.common.DisplayController
import com.android.wm.shell.common.TaskStackListenerCallback
import com.android.wm.shell.common.TaskStackListenerImpl
import com.android.wm.shell.shared.desktopmode.DesktopModeStatus
@@ -40,6 +40,7 @@ class DesktopActivityOrientationChangeHandler(
private val taskStackListener: TaskStackListenerImpl,
private val resizeHandler: ToggleResizeDesktopTaskTransitionHandler,
private val desktopUserRepositories: DesktopUserRepositories,
+ private val displayController: DisplayController,
) {
init {
@@ -101,12 +102,24 @@ class DesktopActivityOrientationChangeHandler(
orientation == ORIENTATION_LANDSCAPE &&
ActivityInfo.isFixedOrientationPortrait(requestedOrientation)
) {
+ val displayLayout = displayController.getDisplayLayout(task.displayId) ?: return
+ val captionInsets =
+ task.configuration.windowConfiguration.appBounds?.let {
+ it.top - task.configuration.windowConfiguration.bounds.top
+ } ?: 0
+ val newOrientationBounds =
+ calculateInitialBounds(
+ displayLayout = displayLayout,
+ taskInfo = task,
+ captionInsets = captionInsets,
+ requestedScreenOrientation = requestedOrientation,
+ )
- val finalSize = Size(taskHeight, taskWidth)
// Use the center x as the resizing anchor point.
- val left = taskBounds.centerX() - finalSize.width / 2
- val right = left + finalSize.width
- val finalBounds = Rect(left, taskBounds.top, right, taskBounds.top + finalSize.height)
+ val left = taskBounds.centerX() - newOrientationBounds.width() / 2
+ val right = left + newOrientationBounds.width()
+ val finalBounds =
+ Rect(left, taskBounds.top, right, taskBounds.top + newOrientationBounds.height())
val wct = WindowContainerTransaction().setBounds(task.token, finalBounds)
resizeHandler.startTransition(wct)
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopDisplayEventHandler.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopDisplayEventHandler.kt
index 683b74392fa6..3b98f8123b46 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopDisplayEventHandler.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopDisplayEventHandler.kt
@@ -17,16 +17,20 @@
package com.android.wm.shell.desktopmode
import android.content.Context
+import android.view.Display
import android.view.Display.DEFAULT_DISPLAY
import android.window.DesktopExperienceFlags
import com.android.internal.protolog.ProtoLog
+import com.android.wm.shell.RootTaskDisplayAreaOrganizer
import com.android.wm.shell.common.DisplayController
import com.android.wm.shell.common.DisplayController.OnDisplaysChangedListener
import com.android.wm.shell.desktopmode.multidesks.OnDeskRemovedListener
import com.android.wm.shell.desktopmode.persistence.DesktopRepositoryInitializer
import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE
import com.android.wm.shell.shared.desktopmode.DesktopModeStatus
+import com.android.wm.shell.sysui.ShellController
import com.android.wm.shell.sysui.ShellInit
+import com.android.wm.shell.sysui.UserChangeListener
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.cancel
import kotlinx.coroutines.launch
@@ -36,7 +40,9 @@ class DesktopDisplayEventHandler(
private val context: Context,
shellInit: ShellInit,
private val mainScope: CoroutineScope,
+ private val shellController: ShellController,
private val displayController: DisplayController,
+ private val rootTaskDisplayAreaOrganizer: RootTaskDisplayAreaOrganizer,
private val desktopRepositoryInitializer: DesktopRepositoryInitializer,
private val desktopUserRepositories: DesktopUserRepositories,
private val desktopTasksController: DesktopTasksController,
@@ -53,8 +59,17 @@ class DesktopDisplayEventHandler(
private fun onInit() {
displayController.addDisplayWindowListener(this)
- if (DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue()) {
+ if (DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue) {
desktopTasksController.onDeskRemovedListener = this
+
+ shellController.addUserChangeListener(
+ object : UserChangeListener {
+ override fun onUserChanged(newUserId: Int, userContext: Context) {
+ val displayIds = rootTaskDisplayAreaOrganizer.displayIds
+ createDefaultDesksIfNeeded(displayIds.toSet())
+ }
+ }
+ )
}
}
@@ -63,23 +78,7 @@ class DesktopDisplayEventHandler(
desktopDisplayModeController.refreshDisplayWindowingMode()
}
- if (!supportsDesks(displayId)) {
- logV("Display #$displayId does not support desks")
- return
- }
-
- mainScope.launch {
- desktopRepositoryInitializer.isInitialized.collect { initialized ->
- if (!initialized) return@collect
- if (desktopRepository.getNumberOfDesks(displayId) == 0) {
- logV("Creating new desk in new display#$displayId")
- // TODO: b/393978539 - consider activating the desk on creation when
- // applicable, such as for connected displays.
- desktopTasksController.createDesk(displayId)
- }
- cancel()
- }
- }
+ createDefaultDesksIfNeeded(displayIds = setOf(displayId))
}
override fun onDisplayRemoved(displayId: Int) {
@@ -93,8 +92,34 @@ class DesktopDisplayEventHandler(
override fun onDeskRemoved(lastDisplayId: Int, deskId: Int) {
val remainingDesks = desktopRepository.getNumberOfDesks(lastDisplayId)
if (remainingDesks == 0) {
- logV("All desks removed from display#$lastDisplayId, creating empty desk")
- desktopTasksController.createDesk(lastDisplayId)
+ logV("All desks removed from display#$lastDisplayId")
+ createDefaultDesksIfNeeded(setOf(lastDisplayId))
+ }
+ }
+
+ private fun createDefaultDesksIfNeeded(displayIds: Set<Int>) {
+ if (!DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue) return
+ logV("createDefaultDesksIfNeeded displays=%s", displayIds)
+ mainScope.launch {
+ desktopRepositoryInitializer.isInitialized.collect { initialized ->
+ if (!initialized) return@collect
+ displayIds
+ .filter { displayId -> displayId != Display.INVALID_DISPLAY }
+ .filter { displayId -> supportsDesks(displayId) }
+ .filter { displayId -> desktopRepository.getNumberOfDesks(displayId) == 0 }
+ .also { displaysNeedingDesk ->
+ logV(
+ "createDefaultDesksIfNeeded creating default desks in displays=%s",
+ displaysNeedingDesk,
+ )
+ }
+ .forEach { displayId ->
+ // TODO: b/393978539 - consider activating the desk on creation when
+ // applicable, such as for connected displays.
+ desktopTasksController.createDesk(displayId)
+ }
+ cancel()
+ }
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopDisplayModeController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopDisplayModeController.kt
index 489e4f0aed01..ea2fdc0ee8ed 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopDisplayModeController.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopDisplayModeController.kt
@@ -28,6 +28,7 @@ import android.provider.Settings
import android.provider.Settings.Global.DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS
import android.view.Display.DEFAULT_DISPLAY
import android.view.IWindowManager
+import android.view.InputDevice
import android.view.WindowManager.TRANSIT_CHANGE
import android.window.DesktopExperienceFlags
import android.window.WindowContainerTransaction
@@ -62,12 +63,28 @@ class DesktopDisplayModeController(
}
}
+ private val inputDeviceListener =
+ object : InputManager.InputDeviceListener {
+ override fun onInputDeviceAdded(deviceId: Int) {
+ refreshDisplayWindowingMode()
+ }
+
+ override fun onInputDeviceChanged(deviceId: Int) {
+ refreshDisplayWindowingMode()
+ }
+
+ override fun onInputDeviceRemoved(deviceId: Int) {
+ refreshDisplayWindowingMode()
+ }
+ }
+
init {
if (DesktopExperienceFlags.FORM_FACTOR_BASED_DESKTOP_FIRST_SWITCH.isTrue) {
inputManager.registerOnTabletModeChangedListener(
onTabletModeChangedListener,
mainHandler,
)
+ inputManager.registerInputDeviceListener(inputDeviceListener, mainHandler)
}
}
@@ -122,7 +139,7 @@ class DesktopDisplayModeController(
return true
}
if (DesktopExperienceFlags.FORM_FACTOR_BASED_DESKTOP_FIRST_SWITCH.isTrue) {
- if (isInClamshellMode()) {
+ if (isInClamshellMode() || hasAnyMouseDevice()) {
return true
}
}
@@ -169,6 +186,11 @@ class DesktopDisplayModeController(
private fun hasExternalDisplay() =
rootTaskDisplayAreaOrganizer.getDisplayIds().any { it != DEFAULT_DISPLAY }
+ private fun hasAnyMouseDevice() =
+ inputManager.inputDeviceIds.any {
+ inputManager.getInputDevice(it)?.supportsSource(InputDevice.SOURCE_MOUSE) == true
+ }
+
private fun isInClamshellMode() = inputManager.isInTabletMode() == InputManager.SWITCH_STATE_OFF
private fun isDefaultDisplayDesktopEligible(): Boolean {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopImeHandler.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopImeHandler.kt
new file mode 100644
index 000000000000..93ba71a17937
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopImeHandler.kt
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.desktopmode
+
+import android.view.SurfaceControl
+import com.android.window.flags.Flags
+import com.android.wm.shell.common.DisplayImeController
+import com.android.wm.shell.common.DisplayImeController.ImePositionProcessor.IME_ANIMATION_DEFAULT
+import com.android.wm.shell.sysui.ShellInit
+
+/** Handles the interactions between IME and desktop tasks */
+class DesktopImeHandler(
+ private val displayImeController: DisplayImeController,
+ shellInit: ShellInit,
+) : DisplayImeController.ImePositionProcessor {
+
+ init {
+ shellInit.addInitCallback(::onInit, this)
+ }
+
+ private fun onInit() {
+ if (Flags.enableDesktopImeBugfix()) {
+ displayImeController.addPositionProcessor(this)
+ }
+ }
+
+ override fun onImeStartPositioning(
+ displayId: Int,
+ hiddenTop: Int,
+ shownTop: Int,
+ showing: Boolean,
+ isFloating: Boolean,
+ t: SurfaceControl.Transaction?,
+ ): Int {
+ return IME_ANIMATION_DEFAULT
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeKeyGestureHandler.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeKeyGestureHandler.kt
index 1ea545f3ab67..19507c17bc95 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeKeyGestureHandler.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeKeyGestureHandler.kt
@@ -23,10 +23,7 @@ import android.hardware.input.InputManager
import android.hardware.input.InputManager.KeyGestureEventHandler
import android.hardware.input.KeyGestureEvent
import android.os.IBinder
-import android.window.DesktopModeFlags
-import com.android.hardware.input.Flags.manageKeyGestures
import com.android.internal.protolog.ProtoLog
-import com.android.window.flags.Flags.enableMoveToNextDisplayShortcut
import com.android.wm.shell.ShellTaskOrganizer
import com.android.wm.shell.common.DisplayController
import com.android.wm.shell.common.ShellExecutor
@@ -51,16 +48,20 @@ class DesktopModeKeyGestureHandler(
) : KeyGestureEventHandler {
init {
- inputManager.registerKeyGestureEventHandler(this)
+ if (desktopTasksController.isPresent && desktopModeWindowDecorViewModel.isPresent) {
+ val supportedGestures =
+ listOf(
+ KeyGestureEvent.KEY_GESTURE_TYPE_MOVE_TO_NEXT_DISPLAY,
+ KeyGestureEvent.KEY_GESTURE_TYPE_SNAP_LEFT_FREEFORM_WINDOW,
+ KeyGestureEvent.KEY_GESTURE_TYPE_SNAP_RIGHT_FREEFORM_WINDOW,
+ KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_MAXIMIZE_FREEFORM_WINDOW,
+ KeyGestureEvent.KEY_GESTURE_TYPE_MINIMIZE_FREEFORM_WINDOW,
+ )
+ inputManager.registerKeyGestureEventHandler(supportedGestures, this)
+ }
}
- override fun handleKeyGestureEvent(event: KeyGestureEvent, focusedToken: IBinder?): Boolean {
- if (
- !desktopTasksController.isPresent ||
- !desktopModeWindowDecorViewModel.isPresent
- ) {
- return false
- }
+ override fun handleKeyGestureEvent(event: KeyGestureEvent, focusedToken: IBinder?) {
when (event.keyGestureType) {
KeyGestureEvent.KEY_GESTURE_TYPE_MOVE_TO_NEXT_DISPLAY -> {
logV("Key gesture MOVE_TO_NEXT_DISPLAY is handled")
@@ -69,7 +70,6 @@ class DesktopModeKeyGestureHandler(
desktopTasksController.get().moveToNextDisplay(it.taskId)
}
}
- return true
}
KeyGestureEvent.KEY_GESTURE_TYPE_SNAP_LEFT_FREEFORM_WINDOW -> {
logV("Key gesture SNAP_LEFT_FREEFORM_WINDOW is handled")
@@ -85,7 +85,6 @@ class DesktopModeKeyGestureHandler(
)
}
}
- return true
}
KeyGestureEvent.KEY_GESTURE_TYPE_SNAP_RIGHT_FREEFORM_WINDOW -> {
logV("Key gesture SNAP_RIGHT_FREEFORM_WINDOW is handled")
@@ -101,7 +100,6 @@ class DesktopModeKeyGestureHandler(
)
}
}
- return true
}
KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_MAXIMIZE_FREEFORM_WINDOW -> {
logV("Key gesture TOGGLE_MAXIMIZE_FREEFORM_WINDOW is handled")
@@ -120,7 +118,6 @@ class DesktopModeKeyGestureHandler(
)
}
}
- return true
}
KeyGestureEvent.KEY_GESTURE_TYPE_MINIMIZE_FREEFORM_WINDOW -> {
logV("Key gesture MINIMIZE_FREEFORM_WINDOW is handled")
@@ -129,9 +126,7 @@ class DesktopModeKeyGestureHandler(
desktopTasksController.get().minimizeTask(it, MinimizeReason.KEY_GESTURE)
}
}
- return true
}
- else -> return false
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeMoveToDisplayTransitionHandler.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeMoveToDisplayTransitionHandler.kt
index 91bd3c9b6c22..844a1f896fd2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeMoveToDisplayTransitionHandler.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeMoveToDisplayTransitionHandler.kt
@@ -19,19 +19,26 @@ package com.android.wm.shell.desktopmode
import android.animation.Animator
import android.animation.AnimatorSet
import android.animation.ValueAnimator
+import android.os.Handler
import android.os.IBinder
import android.view.Choreographer
import android.view.SurfaceControl
import android.window.TransitionInfo
import android.window.TransitionRequestInfo
import android.window.WindowContainerTransaction
+import com.android.internal.jank.Cuj.CUJ_DESKTOP_MODE_MOVE_WINDOW_TO_DISPLAY
+import com.android.internal.jank.InteractionJankMonitor
+import com.android.wm.shell.common.DisplayController
import com.android.wm.shell.shared.animation.Interpolators
import com.android.wm.shell.transition.Transitions
import kotlin.time.Duration.Companion.milliseconds
/** Transition handler for moving a window to a different display. */
class DesktopModeMoveToDisplayTransitionHandler(
- private val animationTransaction: SurfaceControl.Transaction
+ private val animationTransaction: SurfaceControl.Transaction,
+ private val interactionJankMonitor: InteractionJankMonitor,
+ private val shellMainHandler: Handler,
+ private val displayController: DisplayController,
) : Transitions.TransitionHandler {
override fun handleRequest(
@@ -74,18 +81,31 @@ class DesktopModeMoveToDisplayTransitionHandler(
}
}
)
+
animator.addListener(
object : Animator.AnimatorListener {
- override fun onAnimationStart(animation: Animator) = Unit
+ override fun onAnimationStart(animation: Animator) {
+ val displayContext =
+ displayController.getDisplayContext(changes[0].endDisplayId)
+ if (displayContext == null) return
+ interactionJankMonitor.begin(
+ changes[0].leash,
+ displayContext,
+ shellMainHandler,
+ CUJ_DESKTOP_MODE_MOVE_WINDOW_TO_DISPLAY,
+ )
+ }
override fun onAnimationEnd(animation: Animator) {
finishTransaction.apply()
finishCallback.onTransitionFinished(null)
+ interactionJankMonitor.end(CUJ_DESKTOP_MODE_MOVE_WINDOW_TO_DISPLAY)
}
override fun onAnimationCancel(animation: Animator) {
finishTransaction.apply()
finishCallback.onTransitionFinished(null)
+ interactionJankMonitor.cancel(CUJ_DESKTOP_MODE_MOVE_WINDOW_TO_DISPLAY)
}
override fun onAnimationRepeat(animation: Animator) = Unit
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeUiEventLogger.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeUiEventLogger.kt
index b9cb32d8a14f..e0300d688379 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeUiEventLogger.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeUiEventLogger.kt
@@ -167,7 +167,35 @@ class DesktopModeUiEventLogger(
@UiEvent(doc = "Exit desktop mode education tooltip on the app header menu is clicked")
EXIT_DESKTOP_MODE_EDUCATION_TOOLTIP_CLICKED(2104),
@UiEvent(doc = "Exit desktop mode education tooltip is dismissed by the user")
- EXIT_DESKTOP_MODE_EDUCATION_TOOLTIP_DISMISSED(2105);
+ EXIT_DESKTOP_MODE_EDUCATION_TOOLTIP_DISMISSED(2105),
+ @UiEvent(doc = "A11y service opened app handle menu by selecting handle from fullscreen")
+ A11Y_APP_HANDLE_MENU_OPENED(2156),
+ @UiEvent(doc = "A11y service opened app handle menu through Switch Access actions menu ")
+ A11Y_SYSTEM_ACTION_APP_HANDLE_MENU(2157),
+ @UiEvent(doc = "A11y service selected desktop mode from app handle menu")
+ A11Y_APP_HANDLE_MENU_DESKTOP_VIEW(2158),
+ @UiEvent(doc = "A11y service selected fullscreen mode from app handle menu")
+ A11Y_APP_HANDLE_MENU_FULLSCREEN(2159),
+ @UiEvent(doc = "A11y service selected split screen mode from app handle menu")
+ A11Y_APP_HANDLE_MENU_SPLIT_SCREEN(2160),
+ @UiEvent(doc = "A11y service selected maximize/restore button from app header")
+ A11Y_APP_WINDOW_MAXIMIZE_RESTORE_BUTTON(2161),
+ @UiEvent(doc = "A11y service selected minimize button from app header")
+ A11Y_APP_WINDOW_MINIMIZE_BUTTON(2162),
+ @UiEvent(doc = "A11y service selected close button from app header")
+ A11Y_APP_WINDOW_CLOSE_BUTTON(2163),
+ @UiEvent(doc = "A11y service selected maximize button from app header maximize menu")
+ A11Y_MAXIMIZE_MENU_MAXIMIZE(2164),
+ @UiEvent(doc = "A11y service selected resize left button from app header maximize menu")
+ A11Y_MAXIMIZE_MENU_RESIZE_LEFT(2165),
+ @UiEvent(doc = "A11y service selected resize right button from app header maximize menu")
+ A11Y_MAXIMIZE_MENU_RESIZE_RIGHT(2166),
+ @UiEvent(doc = "A11y service triggered a11y action to maximize/restore app window")
+ A11Y_ACTION_MAXIMIZE_RESTORE(2167),
+ @UiEvent(doc = "A11y service triggered a11y action to resize app window left")
+ A11Y_ACTION_RESIZE_LEFT(2168),
+ @UiEvent(doc = "A11y service triggered a11y action to resize app window right")
+ A11Y_ACTION_RESIZE_RIGHT(2169);
override fun getId(): Int = mId
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeUtils.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeUtils.kt
index a8b0bafee724..3c44fe8061aa 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeUtils.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeUtils.kt
@@ -69,6 +69,7 @@ fun calculateInitialBounds(
taskInfo: RunningTaskInfo,
scale: Float = DESKTOP_MODE_INITIAL_BOUNDS_SCALE,
captionInsets: Int = 0,
+ requestedScreenOrientation: Int? = null,
): Rect {
val screenBounds = Rect(0, 0, displayLayout.width(), displayLayout.height())
val appAspectRatio = calculateAspectRatio(taskInfo)
@@ -85,12 +86,13 @@ fun calculateInitialBounds(
}
val topActivityInfo =
taskInfo.topActivityInfo ?: return positionInScreen(idealSize, stableBounds)
+ val screenOrientation = requestedScreenOrientation ?: topActivityInfo.screenOrientation
val initialSize: Size =
when (taskInfo.configuration.orientation) {
ORIENTATION_LANDSCAPE -> {
if (taskInfo.canChangeAspectRatio) {
- if (isFixedOrientationPortrait(topActivityInfo.screenOrientation)) {
+ if (isFixedOrientationPortrait(screenOrientation)) {
// For portrait resizeable activities, respect apps fullscreen width but
// apply ideal size height.
Size(
@@ -104,14 +106,20 @@ fun calculateInitialBounds(
} else {
// If activity is unresizeable, regardless of orientation, calculate maximum
// size (within the ideal size) maintaining original aspect ratio.
- maximizeSizeGivenAspectRatio(taskInfo, idealSize, appAspectRatio, captionInsets)
+ maximizeSizeGivenAspectRatio(
+ taskInfo,
+ idealSize,
+ appAspectRatio,
+ captionInsets,
+ screenOrientation,
+ )
}
}
ORIENTATION_PORTRAIT -> {
val customPortraitWidthForLandscapeApp =
screenBounds.width() - (DESKTOP_MODE_LANDSCAPE_APP_PADDING * 2)
if (taskInfo.canChangeAspectRatio) {
- if (isFixedOrientationLandscape(topActivityInfo.screenOrientation)) {
+ if (isFixedOrientationLandscape(screenOrientation)) {
// For landscape resizeable activities, respect apps fullscreen height and
// apply custom app width.
Size(
@@ -123,7 +131,7 @@ fun calculateInitialBounds(
idealSize
}
} else {
- if (isFixedOrientationLandscape(topActivityInfo.screenOrientation)) {
+ if (isFixedOrientationLandscape(screenOrientation)) {
// For landscape unresizeable activities, apply custom app width to ideal
// size and calculate maximum size with this area while maintaining original
// aspect ratio.
@@ -132,6 +140,7 @@ fun calculateInitialBounds(
Size(customPortraitWidthForLandscapeApp, idealSize.height),
appAspectRatio,
captionInsets,
+ screenOrientation,
)
} else {
// For portrait unresizeable activities, calculate maximum size (within the
@@ -141,6 +150,7 @@ fun calculateInitialBounds(
idealSize,
appAspectRatio,
captionInsets,
+ screenOrientation,
)
}
}
@@ -190,13 +200,16 @@ fun maximizeSizeGivenAspectRatio(
targetArea: Size,
aspectRatio: Float,
captionInsets: Int = 0,
+ requestedScreenOrientation: Int? = null,
): Size {
val targetHeight = targetArea.height - captionInsets
val targetWidth = targetArea.width
val finalHeight: Int
val finalWidth: Int
// Get orientation either through top activity or task's orientation
- if (taskInfo.hasPortraitTopActivity()) {
+ val screenOrientation =
+ requestedScreenOrientation ?: taskInfo.topActivityInfo?.screenOrientation
+ if (taskInfo.hasPortraitTopActivity(screenOrientation)) {
val tempWidth = ceil(targetHeight / aspectRatio).toInt()
if (tempWidth <= targetWidth) {
finalHeight = targetHeight
@@ -354,9 +367,8 @@ fun centerInArea(desiredSize: Size, areaBounds: Rect, leftStart: Int, topStart:
return Rect(newLeft, newTop, newRight, newBottom)
}
-private fun TaskInfo.hasPortraitTopActivity(): Boolean {
- val topActivityScreenOrientation =
- topActivityInfo?.screenOrientation ?: SCREEN_ORIENTATION_UNSPECIFIED
+private fun TaskInfo.hasPortraitTopActivity(screenOrientation: Int?): Boolean {
+ val topActivityScreenOrientation = screenOrientation ?: SCREEN_ORIENTATION_UNSPECIFIED
val appBounds = configuration.windowConfiguration.appBounds
return when {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopRepository.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopRepository.kt
index 6cb26b54e802..25fdbb348356 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopRepository.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopRepository.kt
@@ -68,6 +68,8 @@ class DesktopRepository(
* @property topTransparentFullscreenTaskId the task id of any current top transparent
* fullscreen task launched on top of the desk. Cleared when the transparent task is closed or
* sent to back. (top is at index 0).
+ * @property leftTiledTaskId task id of the task tiled on the left.
+ * @property rightTiledTaskId task id of the task tiled on the right.
*/
private data class Desk(
val deskId: Int,
@@ -80,6 +82,8 @@ class DesktopRepository(
val freeformTasksInZOrder: ArrayList<Int> = ArrayList(),
var fullImmersiveTaskId: Int? = null,
var topTransparentFullscreenTaskId: Int? = null,
+ var leftTiledTaskId: Int? = null,
+ var rightTiledTaskId: Int? = null,
) {
fun deepCopy(): Desk =
Desk(
@@ -92,6 +96,8 @@ class DesktopRepository(
freeformTasksInZOrder = ArrayList(freeformTasksInZOrder),
fullImmersiveTaskId = fullImmersiveTaskId,
topTransparentFullscreenTaskId = topTransparentFullscreenTaskId,
+ leftTiledTaskId = leftTiledTaskId,
+ rightTiledTaskId = rightTiledTaskId,
)
// TODO: b/362720497 - remove when multi-desktops is enabled where instances aren't
@@ -104,6 +110,8 @@ class DesktopRepository(
freeformTasksInZOrder.clear()
fullImmersiveTaskId = null
topTransparentFullscreenTaskId = null
+ leftTiledTaskId = null
+ rightTiledTaskId = null
}
}
@@ -268,6 +276,106 @@ class DesktopRepository(
}
}
+ /** Register a left tiled task to desktop state. */
+ fun addLeftTiledTask(displayId: Int, taskId: Int) {
+ logD("addLeftTiledTask for displayId=%d, taskId=%d", displayId, taskId)
+ val activeDesk =
+ checkNotNull(desktopData.getDefaultDesk(displayId)) {
+ "Expected desk in display: $displayId"
+ }
+ addLeftTiledTaskToDesk(displayId, taskId, activeDesk.deskId)
+ }
+
+ private fun addLeftTiledTaskToDesk(displayId: Int, taskId: Int, deskId: Int) {
+ logD("addLeftTiledTaskToDesk for displayId=%d, taskId=%d", displayId, taskId)
+ val desk = checkNotNull(desktopData.getDesk(deskId)) { "Did not find desk: $deskId" }
+ desk.leftTiledTaskId = taskId
+ if (DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_PERSISTENCE.isTrue()) {
+ updatePersistentRepository(displayId)
+ }
+ }
+
+ /** Register a right tiled task to desktop state. */
+ fun addRightTiledTask(displayId: Int, taskId: Int) {
+ logD("addRightTiledTask for displayId=%d, taskId=%d", displayId, taskId)
+ val activeDesk =
+ checkNotNull(desktopData.getDefaultDesk(displayId)) {
+ "Expected desk in display: $displayId"
+ }
+ addRightTiledTaskToDesk(displayId, taskId, activeDesk.deskId)
+ }
+
+ private fun addRightTiledTaskToDesk(displayId: Int, taskId: Int, deskId: Int) {
+ logD("addRightTiledTaskToDesk for displayId=%d, taskId=%d", displayId, taskId)
+ val desk = checkNotNull(desktopData.getDesk(deskId)) { "Did not find desk: $deskId" }
+ desk.rightTiledTaskId = taskId
+ if (DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_PERSISTENCE.isTrue()) {
+ updatePersistentRepository(displayId)
+ }
+ }
+
+ /** Gets a registered left tiled task to desktop state or returns null. */
+ fun getLeftTiledTask(displayId: Int): Int? {
+ logD("getLeftTiledTask for displayId=%d", displayId)
+ val activeDesk =
+ checkNotNull(desktopData.getDefaultDesk(displayId)) {
+ "Expected desk in display: $displayId"
+ }
+ val deskId = activeDesk.deskId
+ val desk = checkNotNull(desktopData.getDesk(deskId)) { "Did not find desk: $deskId" }
+ return desk.leftTiledTaskId
+ }
+
+ /** gets a registered right tiled task to desktop state or returns null. */
+ fun getRightTiledTask(displayId: Int): Int? {
+ logD("getRightTiledTask for displayId=%d", displayId)
+ val activeDesk =
+ checkNotNull(desktopData.getDefaultDesk(displayId)) {
+ "Expected desk in display: $displayId"
+ }
+ val deskId = activeDesk.deskId
+ val desk = checkNotNull(desktopData.getDesk(deskId)) { "Did not find desk: $deskId" }
+ return desk.rightTiledTaskId
+ }
+
+ /* Unregisters a left tiled task from desktop state. */
+ fun removeLeftTiledTask(displayId: Int) {
+ logD("removeLeftTiledTask for displayId=%d", displayId)
+ val activeDesk =
+ checkNotNull(desktopData.getDefaultDesk(displayId)) {
+ "Expected desk in display: $displayId"
+ }
+ removeLeftTiledTaskFromDesk(displayId, activeDesk.deskId)
+ }
+
+ private fun removeLeftTiledTaskFromDesk(displayId: Int, deskId: Int) {
+ logD("removeLeftTiledTaskToDesk for displayId=%d", displayId)
+ val desk = checkNotNull(desktopData.getDesk(deskId)) { "Did not find desk: $deskId" }
+ desk.leftTiledTaskId = null
+ if (DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_PERSISTENCE.isTrue()) {
+ updatePersistentRepository(displayId)
+ }
+ }
+
+ /* Unregisters a right tiled task from desktop state. */
+ fun removeRightTiledTask(displayId: Int) {
+ logD("removeRightTiledTask for displayId=%d", displayId)
+ val activeDesk =
+ checkNotNull(desktopData.getDefaultDesk(displayId)) {
+ "Expected desk in display: $displayId"
+ }
+ removeRightTiledTaskFromDesk(displayId, activeDesk.deskId)
+ }
+
+ private fun removeRightTiledTaskFromDesk(displayId: Int, deskId: Int) {
+ logD("removeRightTiledTaskFromDesk for displayId=%d", displayId)
+ val desk = checkNotNull(desktopData.getDesk(deskId)) { "Did not find desk: $deskId" }
+ desk.rightTiledTaskId = null
+ if (DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_PERSISTENCE.isTrue()) {
+ updatePersistentRepository(displayId)
+ }
+ }
+
/** Returns the id of the active desk in the given display, if any. */
fun getActiveDeskId(displayId: Int): Int? = desktopData.getActiveDesk(displayId)?.deskId
@@ -972,6 +1080,8 @@ class DesktopRepository(
visibleTasks = desk.visibleTasks,
minimizedTasks = desk.minimizedTasks,
freeformTasksInZOrder = desk.freeformTasksInZOrder,
+ leftTiledTask = desk.leftTiledTaskId,
+ rightTiledTask = desk.rightTiledTaskId,
)
} catch (exception: Exception) {
logE(
@@ -1002,6 +1112,7 @@ class DesktopRepository(
internal fun dump(pw: PrintWriter, prefix: String) {
val innerPrefix = "$prefix "
pw.println("${prefix}DesktopRepository")
+ pw.println("${innerPrefix}userId=$userId")
dumpDesktopTaskData(pw, innerPrefix)
pw.println("${innerPrefix}activeTasksListeners=${activeTasksListeners.size}")
pw.println("${innerPrefix}visibleTasksListeners=${visibleTasksListeners.size}")
@@ -1188,6 +1299,7 @@ class DesktopRepository(
deskByDisplayId[displayId]?.let { sequenceOf(it) } ?: emptySequence()
override fun remove(deskId: Int) {
+ setDeskInactive(deskId)
deskByDisplayId[deskId]?.clear()
}
@@ -1287,6 +1399,7 @@ class DesktopRepository(
desktopDisplays[displayId]?.orderedDesks?.asSequence() ?: emptySequence()
override fun remove(deskId: Int) {
+ setDeskInactive(deskId)
desktopDisplays.forEach { _, display ->
display.orderedDesks.removeIf { it.deskId == deskId }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
index 487c7d15fa38..5849d4af4e7e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
@@ -287,8 +287,8 @@ class DesktopTasksController(
DeskRecreationFactory { deskUserId, destinationDisplayId, deskId ->
if (deskUserId != userId) {
// TODO: b/400984250 - add multi-user support for multi-desk restoration.
- logW("Tried to recreated desk of another user.")
- deskId
+ logW("Tried to re-create desk of another user.")
+ null
} else {
desksOrganizer.createDesk(destinationDisplayId)
}
@@ -306,6 +306,8 @@ class DesktopTasksController(
this,
)
shellController.addUserChangeListener(this)
+ // Update the current user id again because it might be updated between init and onInit().
+ updateCurrentUser(ActivityManager.getCurrentUser())
transitions.addHandler(this)
dragToDesktopTransitionHandler.dragToDesktopStateListener = dragToDesktopStateListener
recentsTransitionHandler.addTransitionStateListener(
@@ -463,6 +465,30 @@ class DesktopTasksController(
return isFreeformDisplay
}
+ /** Called when the recents transition that started while in desktop is finishing. */
+ fun onRecentsInDesktopAnimationFinishing(
+ transition: IBinder,
+ finishWct: WindowContainerTransaction,
+ returnToApp: Boolean,
+ ) {
+ if (!DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue) return
+ logV("onRecentsInDesktopAnimationFinishing returnToApp=%b", returnToApp)
+ if (returnToApp) return
+ // Home/Recents only exists in the default display.
+ val activeDesk = taskRepository.getActiveDeskId(DEFAULT_DISPLAY) ?: return
+ // Not going back to the active desk, deactivate it.
+ val runOnTransitStart =
+ performDesktopExitCleanUp(
+ wct = finishWct,
+ deskId = activeDesk,
+ displayId = DEFAULT_DISPLAY,
+ willExitDesktop = true,
+ shouldEndUpAtHome = true,
+ fromRecentsTransition = true,
+ )
+ runOnTransitStart?.invoke(transition)
+ }
+
/** Creates a new desk in the given display. */
fun createDesk(displayId: Int) {
if (displayId == Display.INVALID_DISPLAY) {
@@ -833,20 +859,22 @@ class DesktopTasksController(
val requestRes = transitions.dispatchRequest(Binder(), requestInfo, /* skip= */ null)
wct.merge(requestRes.second, true)
- desktopPipTransitionObserver.get().addPendingPipTransition(
- DesktopPipTransitionObserver.PendingPipTransition(
- token = freeformTaskTransitionStarter.startPipTransition(wct),
- taskId = taskInfo.taskId,
- onSuccess = {
- onDesktopTaskEnteredPip(
- taskId = taskId,
- deskId = deskId,
- displayId = taskInfo.displayId,
- taskIsLastVisibleTaskBeforePip = isLastTask,
- )
- },
+ desktopPipTransitionObserver
+ .get()
+ .addPendingPipTransition(
+ DesktopPipTransitionObserver.PendingPipTransition(
+ token = freeformTaskTransitionStarter.startPipTransition(wct),
+ taskId = taskInfo.taskId,
+ onSuccess = {
+ onDesktopTaskEnteredPip(
+ taskId = taskId,
+ deskId = deskId,
+ displayId = taskInfo.displayId,
+ taskIsLastVisibleTaskBeforePip = isLastTask,
+ )
+ },
+ )
)
- )
} else {
snapEventHandler.removeTaskIfTiled(displayId, taskId)
val willExitDesktop = willExitDesktop(taskId, displayId, forceExitDesktop = false)
@@ -1206,6 +1234,7 @@ class DesktopTasksController(
pendingIntentBackgroundActivityStartMode =
ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOW_ALWAYS
launchBounds = bounds
+ launchDisplayId = displayId
if (DesktopModeFlags.ENABLE_SHELL_INITIAL_BOUNDS_REGRESSION_BUG_FIX.isTrue) {
// Sets launch bounds size as flexible so core can recalculate.
flexibleLaunchSize = true
@@ -1935,16 +1964,23 @@ class DesktopTasksController(
displayId: Int,
willExitDesktop: Boolean,
shouldEndUpAtHome: Boolean = true,
+ fromRecentsTransition: Boolean = false,
): RunOnTransitStart? {
if (!willExitDesktop) return null
desktopModeEnterExitTransitionListener?.onExitDesktopModeTransitionStarted(
FULLSCREEN_ANIMATION_DURATION
)
- removeWallpaperActivity(wct, displayId)
- if (shouldEndUpAtHome) {
- // If the transition should end up with user going to home, launch home with a pending
- // intent.
- addLaunchHomePendingIntent(wct, displayId)
+ // No need to clean up the wallpaper / reorder home when coming from a recents transition.
+ if (
+ !fromRecentsTransition ||
+ !DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue
+ ) {
+ removeWallpaperActivity(wct, displayId)
+ if (shouldEndUpAtHome) {
+ // If the transition should end up with user going to home, launch home with a
+ // pending intent.
+ addLaunchHomePendingIntent(wct, displayId)
+ }
}
return prepareDeskDeactivationIfNeeded(wct, deskId)
}
@@ -3547,9 +3583,15 @@ class DesktopTasksController(
// TODO(b/366397912): Support full multi-user mode in Windowing.
override fun onUserChanged(newUserId: Int, userContext: Context) {
logV("onUserChanged previousUserId=%d, newUserId=%d", userId, newUserId)
+ updateCurrentUser(newUserId)
+ }
+
+ private fun updateCurrentUser(newUserId: Int) {
userId = newUserId
taskRepository = userRepositories.getProfile(userId)
- snapEventHandler.onUserChange()
+ if (this::snapEventHandler.isInitialized) {
+ snapEventHandler.onUserChange()
+ }
}
/** Called when a task's info changes. */
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopUserRepositories.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopUserRepositories.kt
index c10752d36bf9..72758bd538d6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopUserRepositories.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopUserRepositories.kt
@@ -21,6 +21,7 @@ import android.content.Context
import android.content.pm.UserInfo
import android.os.UserManager
import android.util.SparseArray
+import android.window.DesktopExperienceFlags
import android.window.DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_HSUM
import androidx.core.util.forEach
import com.android.internal.protolog.ProtoLog
@@ -68,7 +69,10 @@ class DesktopUserRepositories(
if (DesktopModeStatus.canEnterDesktopMode(context)) {
shellInit.addInitCallback(::onInit, this)
}
- if (ENABLE_DESKTOP_WINDOWING_HSUM.isTrue()) {
+ if (
+ ENABLE_DESKTOP_WINDOWING_HSUM.isTrue() &&
+ !DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue
+ ) {
userIdToProfileIdsMap[userId] = userManager.getProfiles(userId).map { it.id }
}
}
@@ -76,6 +80,13 @@ class DesktopUserRepositories(
private fun onInit() {
repositoryInitializer.initialize(this)
shellController.addUserChangeListener(this)
+ if (
+ ENABLE_DESKTOP_WINDOWING_HSUM.isTrue() &&
+ DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue
+ ) {
+ userId = ActivityManager.getCurrentUser()
+ userIdToProfileIdsMap[userId] = userManager.getProfiles(userId).map { it.id }
+ }
}
/** Returns [DesktopRepository] for the parent user id. */
@@ -97,10 +108,10 @@ class DesktopUserRepositories(
/** Dumps [DesktopRepository] for each user. */
fun dump(pw: PrintWriter, prefix: String) {
- desktopRepoByUserId.forEach { key, value ->
- pw.println("${prefix}userId=$key")
- value.dump(pw, prefix)
- }
+ val innerPrefix = "$prefix "
+ pw.println("${prefix}DesktopUserRepositories:")
+ pw.println("${innerPrefix}currentUserId=$userId")
+ desktopRepoByUserId.forEach { key, value -> value.dump(pw, innerPrefix) }
}
override fun onUserChanged(newUserId: Int, userContext: Context) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/ExitDesktopTaskTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/ExitDesktopTaskTransitionHandler.java
index 95cc1e68ac11..f382632ff790 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/ExitDesktopTaskTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/ExitDesktopTaskTransitionHandler.java
@@ -46,6 +46,7 @@ import androidx.annotation.Nullable;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.jank.Cuj;
import com.android.internal.jank.InteractionJankMonitor;
+import com.android.internal.util.LatencyTracker;
import com.android.wm.shell.shared.annotations.ShellMainThread;
import com.android.wm.shell.shared.desktopmode.DesktopModeTransitionSource;
import com.android.wm.shell.transition.Transitions;
@@ -68,6 +69,7 @@ public class ExitDesktopTaskTransitionHandler implements Transitions.TransitionH
private final Context mContext;
private final Transitions mTransitions;
private final InteractionJankMonitor mInteractionJankMonitor;
+ private final LatencyTracker mLatencyTracker;
@ShellMainThread
private final Handler mHandler;
private final List<IBinder> mPendingTransitionTokens = new ArrayList<>();
@@ -95,6 +97,7 @@ public class ExitDesktopTaskTransitionHandler implements Transitions.TransitionH
mTransactionSupplier = supplier;
mContext = context;
mInteractionJankMonitor = interactionJankMonitor;
+ mLatencyTracker = LatencyTracker.getInstance(mContext);
mHandler = handler;
}
@@ -109,6 +112,7 @@ public class ExitDesktopTaskTransitionHandler implements Transitions.TransitionH
public IBinder startTransition(@NonNull DesktopModeTransitionSource transitionSource,
@NonNull WindowContainerTransaction wct, Point position,
Function0<Unit> onAnimationEndCallback) {
+ mLatencyTracker.onActionStart(LatencyTracker.ACTION_DESKTOP_MODE_EXIT_MODE);
mPosition = position;
mOnAnimationFinishedCallback = onAnimationEndCallback;
final IBinder token = mTransitions.startTransition(getExitTransitionType(transitionSource),
@@ -141,6 +145,11 @@ public class ExitDesktopTaskTransitionHandler implements Transitions.TransitionH
mPendingTransitionTokens.remove(transition);
+
+ if (transitionHandled) {
+ mLatencyTracker.onActionEnd(LatencyTracker.ACTION_DESKTOP_MODE_EXIT_MODE);
+ }
+
return transitionHandled;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/VisualIndicatorViewContainer.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/VisualIndicatorViewContainer.kt
index 23562388b3e5..5e4122ba14ec 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/VisualIndicatorViewContainer.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/VisualIndicatorViewContainer.kt
@@ -18,6 +18,7 @@ package com.android.wm.shell.desktopmode
import android.animation.Animator
import android.animation.AnimatorListenerAdapter
+import android.animation.AnimatorSet
import android.animation.RectEvaluator
import android.animation.ValueAnimator
import android.app.ActivityManager
@@ -32,6 +33,8 @@ import android.view.View
import android.view.WindowManager
import android.view.WindowlessWindowManager
import android.view.animation.DecelerateInterpolator
+import android.widget.FrameLayout
+import androidx.core.animation.doOnEnd
import com.android.internal.annotations.VisibleForTesting
import com.android.window.flags.Flags
import com.android.wm.shell.R
@@ -42,6 +45,7 @@ import com.android.wm.shell.common.SyncTransactionQueue
import com.android.wm.shell.desktopmode.DesktopModeVisualIndicator.IndicatorType
import com.android.wm.shell.shared.annotations.ShellDesktopThread
import com.android.wm.shell.shared.annotations.ShellMainThread
+import com.android.wm.shell.shared.bubbles.BubbleAnythingFlagHelper
import com.android.wm.shell.shared.bubbles.BubbleDropTargetBoundsProvider
import com.android.wm.shell.windowdecor.WindowDecoration.SurfaceControlViewHostFactory
import com.android.wm.shell.windowdecor.tiling.SnapEventHandler
@@ -64,6 +68,8 @@ constructor(
private val snapEventHandler: SnapEventHandler,
) {
@VisibleForTesting var indicatorView: View? = null
+ // Optional extra indicator showing the outline of the bubble bar
+ private var barIndicatorView: View? = null
private var indicatorViewHost: SurfaceControlViewHost? = null
// Below variables and the SyncTransactionQueue are the only variables that should
// be accessed from shell main thread. Everything else should be used exclusively
@@ -93,7 +99,12 @@ constructor(
screenWidth = metrics.widthPixels
screenHeight = metrics.heightPixels
}
- indicatorView = View(context)
+ indicatorView =
+ if (BubbleAnythingFlagHelper.enableBubbleToFullscreen()) {
+ FrameLayout(context)
+ } else {
+ View(context)
+ }
val leash =
indicatorBuilder
.setName("Desktop Mode Visual Indicator")
@@ -183,23 +194,50 @@ constructor(
)
} else {
val animStartType = IndicatorType.valueOf(currentType.name)
- val animator =
- indicatorView?.let {
- VisualIndicatorAnimator.animateIndicatorType(
- it,
- layout,
- animStartType,
- newType,
- bubbleBoundsProvider,
- taskInfo.displayId,
- snapEventHandler,
- )
- } ?: return@execute
+ val indicator = indicatorView ?: return@execute
+ var animator: Animator =
+ VisualIndicatorAnimator.animateIndicatorType(
+ indicator,
+ layout,
+ animStartType,
+ newType,
+ bubbleBoundsProvider,
+ taskInfo.displayId,
+ snapEventHandler,
+ )
+ if (BubbleAnythingFlagHelper.enableBubbleToFullscreen()) {
+ if (currentType.isBubbleType() || newType.isBubbleType()) {
+ animator = addBarIndicatorAnimation(animator, currentType, newType)
+ }
+ }
animator.start()
}
}
}
+ private fun addBarIndicatorAnimation(
+ visualIndicatorAnimator: Animator,
+ currentType: IndicatorType,
+ newType: IndicatorType,
+ ): Animator {
+ if (newType.isBubbleType()) {
+ getOrCreateBubbleBarIndicator(newType)?.let { bar ->
+ return AnimatorSet().apply {
+ playTogether(visualIndicatorAnimator, fadeBarIndicatorIn(bar))
+ }
+ }
+ }
+ if (currentType.isBubbleType()) {
+ barIndicatorView?.let { bar ->
+ barIndicatorView = null
+ return AnimatorSet().apply {
+ playTogether(visualIndicatorAnimator, fadeBarIndicatorOut(bar))
+ }
+ }
+ }
+ return visualIndicatorAnimator
+ }
+
/**
* Fade indicator in as provided type.
*
@@ -223,17 +261,20 @@ constructor(
snapEventHandler: SnapEventHandler,
) {
desktopExecutor.assertCurrentThread()
- indicatorView?.let {
- it.setBackgroundResource(R.drawable.desktop_windowing_transition_background)
- val animator =
+ indicatorView?.let { indicator ->
+ indicator.setBackgroundResource(R.drawable.desktop_windowing_transition_background)
+ var animator: Animator =
VisualIndicatorAnimator.fadeBoundsIn(
- it,
+ indicator,
type,
layout,
bubbleBoundsProvider,
displayId,
snapEventHandler,
)
+ if (BubbleAnythingFlagHelper.enableBubbleToFullscreen()) {
+ animator = addBarIndicatorAnimation(animator, IndicatorType.NO_INDICATOR, type)
+ }
animator.start()
}
}
@@ -259,7 +300,7 @@ constructor(
desktopExecutor.execute {
indicatorView?.let {
val animStartType = IndicatorType.valueOf(currentType.name)
- val animator =
+ var animator: Animator =
VisualIndicatorAnimator.fadeBoundsOut(
it,
animStartType,
@@ -268,6 +309,10 @@ constructor(
displayId,
snapEventHandler,
)
+ if (BubbleAnythingFlagHelper.enableBubbleToFullscreen()) {
+ animator =
+ addBarIndicatorAnimation(animator, currentType, IndicatorType.NO_INDICATOR)
+ }
animator.addListener(
object : AnimatorListenerAdapter() {
override fun onAnimationEnd(animation: Animator) {
@@ -302,6 +347,38 @@ constructor(
isReleased = true
}
+ private fun getOrCreateBubbleBarIndicator(type: IndicatorType): View? {
+ val container = indicatorView as? FrameLayout ?: return null
+ val onLeft = type == IndicatorType.TO_BUBBLE_LEFT_INDICATOR
+ val bounds = bubbleBoundsProvider?.getBarDropTargetBounds(onLeft) ?: return null
+ val lp = FrameLayout.LayoutParams(bounds.width(), bounds.height())
+ lp.leftMargin = bounds.left
+ lp.topMargin = bounds.top
+ if (barIndicatorView == null) {
+ val indicator = View(container.context)
+ indicator.setBackgroundResource(R.drawable.desktop_windowing_transition_background)
+ container.addView(indicator, lp)
+ barIndicatorView = indicator
+ } else {
+ barIndicatorView?.layoutParams = lp
+ }
+ return barIndicatorView
+ }
+
+ private fun fadeBarIndicatorIn(barIndicator: View): Animator {
+ // Use layout bounds as the end bounds in case the view has not been laid out yet
+ val lp = barIndicator.layoutParams
+ val endBounds = Rect(0, 0, lp.width, lp.height)
+ return VisualIndicatorAnimator.fadeBoundsIn(barIndicator, endBounds)
+ }
+
+ private fun fadeBarIndicatorOut(barIndicator: View): Animator {
+ val startBounds = Rect(0, 0, barIndicator.width, barIndicator.height)
+ val barAnimator = VisualIndicatorAnimator.fadeBoundsOut(barIndicator, startBounds)
+ barAnimator.doOnEnd { (indicatorView as? FrameLayout)?.removeView(barIndicator) }
+ return barAnimator
+ }
+
/**
* Animator for Desktop Mode transitions which supports bounds and alpha animation. Functions
* should only be called from the desktop executor.
@@ -383,9 +460,13 @@ constructor(
displayId,
snapEventHandler,
)
+ return fadeBoundsIn(view, endBounds)
+ }
+
+ @ShellDesktopThread
+ fun fadeBoundsIn(view: View, endBounds: Rect): VisualIndicatorAnimator {
val startBounds = getMinBounds(endBounds)
view.background.bounds = startBounds
-
val animator = VisualIndicatorAnimator(view, startBounds, endBounds)
animator.interpolator = DecelerateInterpolator()
setupIndicatorAnimation(animator, AlphaAnimType.ALPHA_FADE_IN_ANIM)
@@ -409,6 +490,11 @@ constructor(
displayId,
snapEventHandler,
)
+ return fadeBoundsOut(view, startBounds)
+ }
+
+ @ShellDesktopThread
+ fun fadeBoundsOut(view: View, startBounds: Rect): VisualIndicatorAnimator {
val endBounds = getMinBounds(startBounds)
view.background.bounds = startBounds
val animator = VisualIndicatorAnimator(view, startBounds, endBounds)
@@ -571,4 +657,9 @@ constructor(
}
}
}
+
+ private fun IndicatorType.isBubbleType(): Boolean {
+ return this == IndicatorType.TO_BUBBLE_LEFT_INDICATOR ||
+ this == IndicatorType.TO_BUBBLE_RIGHT_INDICATOR
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/common/DefaultHomePackageSupplier.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/common/DefaultHomePackageSupplier.kt
index 8ce624e103ef..bf687f2e4f3a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/common/DefaultHomePackageSupplier.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/common/DefaultHomePackageSupplier.kt
@@ -20,6 +20,7 @@ import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
+import android.content.pm.PackageManager
import android.os.Handler
import com.android.wm.shell.shared.annotations.ShellMainThread
import com.android.wm.shell.sysui.ShellInit
@@ -28,6 +29,7 @@ import java.util.function.Supplier
/**
* This supplies the package name of default home in an efficient way. The query to package manager
* only executes on initialization and when the preferred activity (e.g. default home) is changed.
+ * Note that this returns null package name if the default home is setup wizard.
*/
class DefaultHomePackageSupplier(
private val context: Context,
@@ -36,6 +38,7 @@ class DefaultHomePackageSupplier(
) : BroadcastReceiver(), Supplier<String?> {
private var defaultHomePackage: String? = null
+ private var isSetupWizard: Boolean = false
init {
shellInit.addInitCallback({ onInit() }, this)
@@ -52,6 +55,14 @@ class DefaultHomePackageSupplier(
private fun updateDefaultHomePackage(): String? {
defaultHomePackage = context.packageManager.getHomeActivities(ArrayList())?.packageName
+ isSetupWizard =
+ defaultHomePackage != null &&
+ context.packageManager.resolveActivity(
+ Intent()
+ .setPackage(defaultHomePackage)
+ .addCategory(Intent.CATEGORY_SETUP_WIZARD),
+ PackageManager.MATCH_SYSTEM_ONLY,
+ ) != null
return defaultHomePackage
}
@@ -60,6 +71,7 @@ class DefaultHomePackageSupplier(
}
override fun get(): String? {
+ if (isSetupWizard) return null
return defaultHomePackage ?: updateDefaultHomePackage()
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/multidesks/DeskTransition.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/multidesks/DeskTransition.kt
index 9dec96933ee5..454419c805c1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/multidesks/DeskTransition.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/multidesks/DeskTransition.kt
@@ -18,9 +18,12 @@ package com.android.wm.shell.desktopmode.multidesks
import android.os.IBinder
/** Represents shell-started transitions involving desks. */
-sealed class DeskTransition {
+sealed interface DeskTransition {
/** The transition token. */
- abstract val token: IBinder
+ val token: IBinder
+
+ /** Returns a copy of this desk transition with a new transition token. */
+ fun copyWithToken(token: IBinder): DeskTransition
/** A transition to remove a desk and its tasks from a display. */
data class RemoveDesk(
@@ -29,11 +32,15 @@ sealed class DeskTransition {
val deskId: Int,
val tasks: Set<Int>,
val onDeskRemovedListener: OnDeskRemovedListener?,
- ) : DeskTransition()
+ ) : DeskTransition {
+ override fun copyWithToken(token: IBinder): DeskTransition = copy(token)
+ }
/** A transition to activate a desk in its display. */
data class ActivateDesk(override val token: IBinder, val displayId: Int, val deskId: Int) :
- DeskTransition()
+ DeskTransition {
+ override fun copyWithToken(token: IBinder): DeskTransition = copy(token)
+ }
/** A transition to activate a desk by moving an outside task to it. */
data class ActiveDeskWithTask(
@@ -41,8 +48,12 @@ sealed class DeskTransition {
val displayId: Int,
val deskId: Int,
val enterTaskId: Int,
- ) : DeskTransition()
+ ) : DeskTransition {
+ override fun copyWithToken(token: IBinder): DeskTransition = copy(token)
+ }
/** A transition to deactivate a desk. */
- data class DeactivateDesk(override val token: IBinder, val deskId: Int) : DeskTransition()
+ data class DeactivateDesk(override val token: IBinder, val deskId: Int) : DeskTransition {
+ override fun copyWithToken(token: IBinder): DeskTransition = copy(token)
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/multidesks/DesksTransitionObserver.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/multidesks/DesksTransitionObserver.kt
index b521b2e8c942..588b5c350330 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/multidesks/DesksTransitionObserver.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/multidesks/DesksTransitionObserver.kt
@@ -39,6 +39,7 @@ class DesksTransitionObserver(
val transitions = deskTransitions[transition.token] ?: mutableSetOf()
transitions += transition
deskTransitions[transition.token] = transitions
+ logD("Added pending desk transition: %s", transition)
}
/**
@@ -51,6 +52,43 @@ class DesksTransitionObserver(
deskTransitions.forEach { deskTransition -> handleDeskTransition(info, deskTransition) }
}
+ /**
+ * Called when a transition is merged with another transition, which may include transitions not
+ * tracked by this observer.
+ */
+ fun onTransitionMerged(merged: IBinder, playing: IBinder) {
+ if (!DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue) return
+ val transitions = deskTransitions.remove(merged) ?: return
+ deskTransitions[playing] =
+ transitions
+ .map { deskTransition -> deskTransition.copyWithToken(token = playing) }
+ .toMutableSet()
+ }
+
+ /**
+ * Called when any transition finishes, which may include transitions not tracked by this
+ * observer.
+ *
+ * Most [DeskTransition]s are not handled here because [onTransitionReady] handles them and
+ * removes them from the map. However, there can be cases where the transition was added after
+ * [onTransitionReady] had already been called and they need to be handled here, such as the
+ * swipe-to-home recents transition when there is no book-end transition.
+ */
+ fun onTransitionFinished(transition: IBinder) {
+ if (!DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue) return
+ val deskTransitions = deskTransitions.remove(transition) ?: return
+ deskTransitions.forEach { deskTransition ->
+ if (deskTransition is DeskTransition.DeactivateDesk) {
+ handleDeactivateDeskTransition(null, deskTransition)
+ } else {
+ logW(
+ "Unexpected desk transition finished without being handled: %s",
+ deskTransition,
+ )
+ }
+ }
+ }
+
private fun handleDeskTransition(info: TransitionInfo, deskTransition: DeskTransition) {
logD("Desk transition ready: %s", deskTransition)
val desktopRepository = desktopUserRepositories.current
@@ -102,41 +140,54 @@ class DesksTransitionObserver(
)
}
}
- is DeskTransition.DeactivateDesk -> {
- var visibleDeactivation = false
- for (change in info.changes) {
- val isDeskChange = desksOrganizer.isDeskChange(change, deskTransition.deskId)
- if (isDeskChange) {
- visibleDeactivation = true
- continue
- }
- val taskId = change.taskInfo?.taskId ?: continue
- val removedFromDesk =
- desktopRepository.getDeskIdForTask(taskId) == deskTransition.deskId &&
- desksOrganizer.getDeskAtEnd(change) == null
- if (removedFromDesk) {
- desktopRepository.removeTaskFromDesk(
- deskId = deskTransition.deskId,
- taskId = taskId,
- )
- }
- }
- // Always deactivate even if there's no change that confirms the desk was
- // deactivated. Some interactions, such as the desk deactivating because it's
- // occluded by a fullscreen task result in a transition change, but others, such
- // as transitioning from an empty desk to home may not.
- if (!visibleDeactivation) {
- logD("Deactivating desk without transition change")
- }
- desktopRepository.setDeskInactive(deskId = deskTransition.deskId)
+ is DeskTransition.DeactivateDesk -> handleDeactivateDeskTransition(info, deskTransition)
+ }
+ }
+
+ private fun handleDeactivateDeskTransition(
+ info: TransitionInfo?,
+ deskTransition: DeskTransition.DeactivateDesk,
+ ) {
+ logD("handleDeactivateDeskTransition: %s", deskTransition)
+ val desktopRepository = desktopUserRepositories.current
+ var deskChangeFound = false
+
+ val changes = info?.changes ?: emptyList()
+ for (change in changes) {
+ val isDeskChange = desksOrganizer.isDeskChange(change, deskTransition.deskId)
+ if (isDeskChange) {
+ deskChangeFound = true
+ continue
+ }
+ val taskId = change.taskInfo?.taskId ?: continue
+ val removedFromDesk =
+ desktopRepository.getDeskIdForTask(taskId) == deskTransition.deskId &&
+ desksOrganizer.getDeskAtEnd(change) == null
+ if (removedFromDesk) {
+ desktopRepository.removeTaskFromDesk(
+ deskId = deskTransition.deskId,
+ taskId = taskId,
+ )
}
}
+ // Always deactivate even if there's no change that confirms the desk was
+ // deactivated. Some interactions, such as the desk deactivating because it's
+ // occluded by a fullscreen task result in a transition change, but others, such
+ // as transitioning from an empty desk to home may not.
+ if (!deskChangeFound) {
+ logD("Deactivating desk without transition change")
+ }
+ desktopRepository.setDeskInactive(deskId = deskTransition.deskId)
}
private fun logD(msg: String, vararg arguments: Any?) {
ProtoLog.d(WM_SHELL_DESKTOP_MODE, "%s: $msg", TAG, *arguments)
}
+ private fun logW(msg: String, vararg arguments: Any?) {
+ ProtoLog.w(WM_SHELL_DESKTOP_MODE, "%s: $msg", TAG, *arguments)
+ }
+
private companion object {
private const val TAG = "DesksTransitionObserver"
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/persistence/DesktopPersistentRepository.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/persistence/DesktopPersistentRepository.kt
index f71eacab518d..e04a5cdb8a38 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/persistence/DesktopPersistentRepository.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/persistence/DesktopPersistentRepository.kt
@@ -113,6 +113,8 @@ class DesktopPersistentRepository(private val dataStore: DataStore<DesktopPersis
visibleTasks: ArraySet<Int> = ArraySet(),
minimizedTasks: ArraySet<Int> = ArraySet(),
freeformTasksInZOrder: ArrayList<Int> = ArrayList(),
+ leftTiledTask: Int? = null,
+ rightTiledTask: Int? = null,
) {
// TODO: b/367609270 - Improve the API to support multi-user
try {
@@ -125,7 +127,13 @@ class DesktopPersistentRepository(private val dataStore: DataStore<DesktopPersis
val desktop =
getDesktop(currentRepository, desktopId)
.toBuilder()
- .updateTaskStates(visibleTasks, minimizedTasks, freeformTasksInZOrder)
+ .updateTaskStates(
+ visibleTasks,
+ minimizedTasks,
+ freeformTasksInZOrder,
+ leftTiledTask,
+ rightTiledTask,
+ )
.updateZOrder(freeformTasksInZOrder)
persistentRepositories
@@ -222,6 +230,8 @@ class DesktopPersistentRepository(private val dataStore: DataStore<DesktopPersis
visibleTasks: ArraySet<Int>,
minimizedTasks: ArraySet<Int>,
freeformTasksInZOrder: ArrayList<Int>,
+ leftTiledTask: Int?,
+ rightTiledTask: Int?,
): Desktop.Builder {
clearTasksByTaskId()
@@ -238,7 +248,11 @@ class DesktopPersistentRepository(private val dataStore: DataStore<DesktopPersis
}
putAllTasksByTaskId(
visibleTasks.associateWith {
- createDesktopTask(it, state = DesktopTaskState.VISIBLE)
+ createDesktopTask(
+ it,
+ state = DesktopTaskState.VISIBLE,
+ getTilingStateForTask(it, leftTiledTask, rightTiledTask),
+ )
}
)
putAllTasksByTaskId(
@@ -249,6 +263,17 @@ class DesktopPersistentRepository(private val dataStore: DataStore<DesktopPersis
return this
}
+ private fun getTilingStateForTask(
+ taskId: Int,
+ leftTiledTask: Int?,
+ rightTiledTask: Int?,
+ ): DesktopTaskTilingState =
+ when (taskId) {
+ leftTiledTask -> DesktopTaskTilingState.LEFT
+ rightTiledTask -> DesktopTaskTilingState.RIGHT
+ else -> DesktopTaskTilingState.NONE
+ }
+
private fun Desktop.Builder.updateZOrder(
freeformTasksInZOrder: ArrayList<Int>
): Desktop.Builder {
@@ -260,7 +285,12 @@ class DesktopPersistentRepository(private val dataStore: DataStore<DesktopPersis
private fun createDesktopTask(
taskId: Int,
state: DesktopTaskState = DesktopTaskState.VISIBLE,
+ tiling_state: DesktopTaskTilingState = DesktopTaskTilingState.NONE,
): DesktopTask =
- DesktopTask.newBuilder().setTaskId(taskId).setDesktopTaskState(state).build()
+ DesktopTask.newBuilder()
+ .setTaskId(taskId)
+ .setDesktopTaskState(state)
+ .setDesktopTaskTilingState(tiling_state)
+ .build()
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/persistence/DesktopRepositoryInitializer.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/persistence/DesktopRepositoryInitializer.kt
index 8191181cac11..a2dd5dbc8709 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/persistence/DesktopRepositoryInitializer.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/persistence/DesktopRepositoryInitializer.kt
@@ -21,7 +21,7 @@ import kotlinx.coroutines.flow.StateFlow
/** Interface for initializing the [DesktopUserRepositories]. */
interface DesktopRepositoryInitializer {
- /** A factory used to recreate a desk from persistence. */
+ /** A factory used to re-create a desk from persistence. */
var deskRecreationFactory: DeskRecreationFactory
/** A flow that emits true when the repository has been initialized. */
@@ -30,9 +30,11 @@ interface DesktopRepositoryInitializer {
/** Initialize the user repositories from a persistent data store. */
fun initialize(userRepositories: DesktopUserRepositories)
- /** A factory for recreating desks. */
+ /** A factory for re-creating desks. */
fun interface DeskRecreationFactory {
- /** Recreates a restored desk and returns the new desk id. */
- suspend fun recreateDesk(userId: Int, destinationDisplayId: Int, deskId: Int): Int
+ /**
+ * Re-creates a restored desk and returns the new desk id, or null if re-creation failed.
+ */
+ suspend fun recreateDesk(userId: Int, destinationDisplayId: Int, deskId: Int): Int?
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/persistence/DesktopRepositoryInitializerImpl.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/persistence/DesktopRepositoryInitializerImpl.kt
index 49cb7391fe97..3ee48072ee86 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/persistence/DesktopRepositoryInitializerImpl.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/persistence/DesktopRepositoryInitializerImpl.kt
@@ -69,7 +69,7 @@ class DesktopRepositoryInitializerImpl(
desksToRestore.map { it.desktopId },
userId,
)
- desksToRestore.forEach { persistentDesktop ->
+ for (persistentDesktop in desksToRestore) {
val maxTasks = getTaskLimit(persistentDesktop)
val displayId = persistentDesktop.displayId
val deskId = persistentDesktop.desktopId
@@ -81,17 +81,29 @@ class DesktopRepositoryInitializerImpl(
destinationDisplayId = newDisplayId,
deskId = deskId,
)
- logV(
- "Recreated desk=%d in display=%d using new deskId=%d and displayId=%d",
- deskId,
- displayId,
- newDeskId,
- newDisplayId,
- )
- if (newDeskId != deskId || newDisplayId != displayId) {
+ if (newDeskId != null) {
+ logV(
+ "Re-created desk=%d in display=%d using new" +
+ " deskId=%d and displayId=%d",
+ deskId,
+ displayId,
+ newDeskId,
+ newDisplayId,
+ )
+ }
+ if (newDeskId == null || newDeskId != deskId || newDisplayId != displayId) {
logV("Removing obsolete desk from persistence under deskId=%d", deskId)
persistentRepository.removeDesktop(userId, deskId)
}
+ if (newDeskId == null) {
+ logW(
+ "Could not re-create desk=%d from display=%d in displayId=%d",
+ deskId,
+ displayId,
+ newDisplayId,
+ )
+ continue
+ }
// TODO: b/393961770 - [DesktopRepository] doesn't save desks to the
// persistent repository until a task is added to them. Update it so that
@@ -126,6 +138,20 @@ class DesktopRepositoryInitializerImpl(
taskId = task.taskId,
)
}
+
+ if (task.desktopTaskTilingState == DesktopTaskTilingState.LEFT) {
+ repository.addLeftTiledTask(
+ persistentDesktop.displayId,
+ task.taskId,
+ )
+ } else if (
+ task.desktopTaskTilingState == DesktopTaskTilingState.RIGHT
+ ) {
+ repository.addRightTiledTask(
+ persistentDesktop.displayId,
+ task.taskId,
+ )
+ }
}
}
}
@@ -163,6 +189,10 @@ class DesktopRepositoryInitializerImpl(
ProtoLog.v(WM_SHELL_DESKTOP_MODE, "%s: $msg", TAG, *arguments)
}
+ private fun logW(msg: String, vararg arguments: Any?) {
+ ProtoLog.w(WM_SHELL_DESKTOP_MODE, "%s: $msg", TAG, *arguments)
+ }
+
/** A default implementation of [DeskRecreationFactory] that reuses the desk id. */
private class DefaultDeskRecreationFactory : DeskRecreationFactory {
override suspend fun recreateDesk(
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/persistence/persistent_desktop_repositories.proto b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/persistence/persistent_desktop_repositories.proto
index 010523162cec..86dcee8f8515 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/persistence/persistent_desktop_repositories.proto
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/persistence/persistent_desktop_repositories.proto
@@ -9,9 +9,16 @@ enum DesktopTaskState {
MINIMIZED = 1;
}
+enum DesktopTaskTilingState {
+ NONE = 1;
+ LEFT = 2;
+ RIGHT = 3;
+}
+
message DesktopTask {
optional int32 task_id = 1;
optional DesktopTaskState desktop_task_state= 2;
+ optional DesktopTaskTilingState desktop_task_tiling_state = 3;
}
message Desktop {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/docs/debugging.md b/libs/WindowManager/Shell/src/com/android/wm/shell/docs/debugging.md
index dd5827af97d9..320de2ae5945 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/docs/debugging.md
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/docs/debugging.md
@@ -142,8 +142,8 @@ transaction which is applied.
## Tracing activity starts & finishes in the app process
It's sometimes useful to know when to see a stack trace of when an activity starts in the app code
-(ie. if you are repro'ing a bug related to activity starts). You can enable this system property to
-get this trace:
+or via a `WindowContainerTransaction` (ie. if you are repro'ing a bug related to activity starts).
+You can enable this system property to get this trace:
```shell
# Enabling
adb shell setprop persist.wm.debug.start_activity true
@@ -168,6 +168,21 @@ adb shell setprop persist.wm.debug.finish_activity \"\"
adb reboot
```
+## Tracing transition requests in the Shell
+
+To trace where a new WM transition is started in the Shell, you can enable this system property:
+```shell
+# Enabling
+adb shell setprop persist.wm.debug.start_shell_transition true
+adb reboot
+adb logcat -s "ShellTransitions"
+
+# Disabling
+adb shell setprop persist.wm.debug.start_shell_transition \"\"
+adb reboot
+```
+
+
## Dumps
Because the Shell library is built as a part of SystemUI, dumping the state is currently done as a
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java
index b3c1a92f5e1d..d6084138b7e7 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java
@@ -663,6 +663,9 @@ public class DragLayout extends LinearLayout
mSession = null;
}
});
+ // notify bubbles of drag cancel
+ mCurrentBubbleBarTarget = null;
+ mBubbleBarDragListener.onItemDraggedOutsideBubbleBarDropZone();
// Reset the state if we previously force-ignore the bottom margin
mDropZoneView1.setForceIgnoreBottomMargin(false);
mDropZoneView2.setForceIgnoreBottomMargin(false);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserver.java b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserver.java
index 0bf2ea61b0a4..e7492f17835a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserver.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserver.java
@@ -207,6 +207,7 @@ public class FreeformTaskTransitionObserver implements Transitions.TransitionObs
@Override
public void onTransitionMerged(@NonNull IBinder merged, @NonNull IBinder playing) {
+ mDesksTransitionObserver.ifPresent(o -> o.onTransitionMerged(merged, playing));
if (DesktopModeFlags.ENABLE_FULLY_IMMERSIVE_IN_DESKTOP.isTrue()) {
// TODO(b/367268953): Remove when DesktopTaskListener is introduced.
mDesktopImmersiveController.ifPresent(h -> h.onTransitionMerged(merged, playing));
@@ -232,6 +233,7 @@ public class FreeformTaskTransitionObserver implements Transitions.TransitionObs
@Override
public void onTransitionFinished(@NonNull IBinder transition, boolean aborted) {
+ mDesksTransitionObserver.ifPresent(o -> o.onTransitionFinished(transition));
if (DesktopModeFlags.ENABLE_FULLY_IMMERSIVE_IN_DESKTOP.isTrue()) {
// TODO(b/367268953): Remove when DesktopTaskListener is introduced.
mDesktopImmersiveController.ifPresent(h -> h.onTransitionFinished(transition, aborted));
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java
index cef18f55b86d..c58bb6e3bd31 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java
@@ -40,7 +40,6 @@ import android.view.SurfaceControl;
import android.view.WindowManager;
import android.window.TransitionInfo;
import android.window.TransitionRequestInfo;
-import android.window.WindowContainerToken;
import android.window.WindowContainerTransaction;
import androidx.annotation.NonNull;
@@ -341,23 +340,6 @@ public abstract class PipTransitionController implements Transitions.TransitionH
return false;
}
- /**
- * @return a change representing a config-at-end activity for a given parent.
- */
- @Nullable
- public TransitionInfo.Change getDeferConfigActivityChange(TransitionInfo info,
- @android.annotation.NonNull WindowContainerToken parent) {
- for (TransitionInfo.Change change : info.getChanges()) {
- if (change.getTaskInfo() == null
- && change.hasFlags(TransitionInfo.FLAG_CONFIG_AT_END)
- && change.getParent() != null && change.getParent().equals(parent)) {
- return change;
- }
- }
- return null;
- }
-
-
/** Whether a particular package is same as current pip package. */
public boolean isPackageActiveInPip(@Nullable String packageName) {
// No-op, to be handled differently in PIP1 and PIP2
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipController.java
index 119763ff2022..0e974ef9083b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipController.java
@@ -31,6 +31,8 @@ import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
import android.graphics.Rect;
import android.os.Bundle;
+import android.os.Debug;
+import android.util.Log;
import android.view.SurfaceControl;
import android.window.DesktopExperienceFlags;
import android.window.DisplayAreaInfo;
@@ -340,7 +342,8 @@ public class PipController implements ConfigurationChangeListener,
mPipDisplayLayoutState.rotateTo(toRotation);
}
- if (!mPipTransitionState.isInPip()) {
+ if (!mPipTransitionState.isInPip()
+ && mPipTransitionState.getState() != PipTransitionState.ENTERING_PIP) {
// Skip the PiP-relevant updates if we aren't in a valid PiP state.
if (mPipTransitionState.isInFixedRotation()) {
ProtoLog.e(ShellProtoLogGroup.WM_SHELL_TRANSITIONS,
@@ -368,7 +371,13 @@ public class PipController implements ConfigurationChangeListener,
mPipBoundsAlgorithm.applySnapFraction(toBounds, snapFraction);
mPipBoundsState.setBounds(toBounds);
}
- t.setBounds(mPipTransitionState.getPipTaskToken(), mPipBoundsState.getBounds());
+ if (mPipTransitionState.getPipTaskToken() == null) {
+ Log.wtf(TAG, "PipController.onDisplayChange no PiP task token"
+ + " state=" + mPipTransitionState.getState()
+ + " callers=\n" + Debug.getCallers(4, " "));
+ } else {
+ t.setBounds(mPipTransitionState.getPipTaskToken(), mPipBoundsState.getBounds());
+ }
// Update the size spec in PipBoundsState afterwards.
mPipBoundsState.updateMinMaxSize(mPipBoundsState.getAspectRatio());
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTaskListener.java
index 294ef48c01d0..92f36d08a941 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTaskListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTaskListener.java
@@ -43,6 +43,7 @@ import com.android.wm.shell.shared.annotations.ShellMainThread;
import java.util.ArrayList;
import java.util.List;
+import java.util.StringJoiner;
/**
* A Task Listener implementation used only for CUJs and trigger paths that cannot be initiated via
@@ -111,8 +112,20 @@ public class PipTaskListener implements ShellTaskOrganizer.TaskListener,
listener.onActionsChanged(params.getActions(), params.getCloseAction());
}
}
- mPictureInPictureParams.copyOnlySet(params != null ? params
- : new PictureInPictureParams.Builder().build());
+ // Set the new params but make sure mPictureInPictureParams is not null.
+ mPictureInPictureParams = params == null
+ ? new PictureInPictureParams.Builder().build() : params;
+ logRemoteActions(mPictureInPictureParams);
+ }
+
+ private void logRemoteActions(@android.annotation.NonNull PictureInPictureParams params) {
+ StringJoiner sj = new StringJoiner("|", "[", "]");
+ if (params.hasSetActions()) {
+ params.getActions().forEach((action) -> sj.add(action.getTitle()));
+ }
+
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "PIP remote actions=%s", sj.toString());
}
/** Add a PipParamsChangedCallback listener. */
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java
index 9bb2e38e1526..5d8d8b685a23 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java
@@ -45,6 +45,7 @@ import android.app.ActivityManager;
import android.app.PictureInPictureParams;
import android.app.TaskInfo;
import android.content.Context;
+import android.graphics.Matrix;
import android.graphics.Point;
import android.graphics.PointF;
import android.graphics.Rect;
@@ -75,6 +76,7 @@ import com.android.wm.shell.pip2.PipSurfaceTransactionHelper;
import com.android.wm.shell.pip2.animation.PipAlphaAnimator;
import com.android.wm.shell.pip2.animation.PipEnterAnimator;
import com.android.wm.shell.pip2.phone.transition.PipExpandHandler;
+import com.android.wm.shell.pip2.phone.transition.PipTransitionUtils;
import com.android.wm.shell.shared.TransitionUtil;
import com.android.wm.shell.splitscreen.SplitScreenController;
import com.android.wm.shell.sysui.ShellInit;
@@ -386,8 +388,8 @@ public class PipTransition extends PipTransitionController implements
mFinishCallback = finishCallback;
// We expect the PiP activity as a separate change in a config-at-end transition;
// only flings are not using config-at-end for resize bounds changes
- TransitionInfo.Change pipActivityChange = getDeferConfigActivityChange(info,
- pipChange.getTaskInfo().getToken());
+ TransitionInfo.Change pipActivityChange = PipTransitionUtils.getDeferConfigActivityChange(
+ info, pipChange.getTaskInfo().getToken());
if (pipActivityChange != null) {
// Transform calculations use PiP params by default, so make sure they are null to
// default to using bounds for scaling calculations instead.
@@ -426,8 +428,8 @@ public class PipTransition extends PipTransitionController implements
}
// We expect the PiP activity as a separate change in a config-at-end transition.
- TransitionInfo.Change pipActivityChange = getDeferConfigActivityChange(info,
- pipChange.getTaskInfo().getToken());
+ TransitionInfo.Change pipActivityChange = PipTransitionUtils.getDeferConfigActivityChange(
+ info, pipChange.getTaskInfo().getToken());
if (pipActivityChange == null) {
return false;
}
@@ -452,7 +454,7 @@ public class PipTransition extends PipTransitionController implements
final int delta = getFixedRotationDelta(info, pipChange, mPipDisplayLayoutState);
if (delta != ROTATION_0) {
// Update transition target changes in place to prepare for fixed rotation.
- handleBoundsEnterFixedRotation(info, pipChange, pipActivityChange);
+ updatePipChangesForFixedRotation(info, pipChange, pipActivityChange);
}
// Update the src-rect-hint in params in place, to set up initial animator transform.
@@ -496,8 +498,8 @@ public class PipTransition extends PipTransitionController implements
}
// We expect the PiP activity as a separate change in a config-at-end transition.
- TransitionInfo.Change pipActivityChange = getDeferConfigActivityChange(info,
- pipChange.getTaskInfo().getToken());
+ TransitionInfo.Change pipActivityChange = PipTransitionUtils.getDeferConfigActivityChange(
+ info, pipChange.getTaskInfo().getToken());
if (pipActivityChange == null) {
return false;
}
@@ -513,7 +515,7 @@ public class PipTransition extends PipTransitionController implements
final int delta = getFixedRotationDelta(info, pipChange, mPipDisplayLayoutState);
if (delta != ROTATION_0) {
// Update transition target changes in place to prepare for fixed rotation.
- handleBoundsEnterFixedRotation(info, pipChange, pipActivityChange);
+ updatePipChangesForFixedRotation(info, pipChange, pipActivityChange);
}
PipEnterAnimator animator = new PipEnterAnimator(mContext, pipLeash,
@@ -546,7 +548,7 @@ public class PipTransition extends PipTransitionController implements
return true;
}
- private void handleBoundsEnterFixedRotation(TransitionInfo info,
+ private void updatePipChangesForFixedRotation(TransitionInfo info,
TransitionInfo.Change outPipTaskChange,
TransitionInfo.Change outPipActivityChange) {
final TransitionInfo.Change fixedRotationChange = findFixedRotationChange(info);
@@ -604,10 +606,33 @@ public class PipTransition extends PipTransitionController implements
SurfaceControl pipLeash = mPipTransitionState.getPinnedTaskLeash();
Preconditions.checkNotNull(pipLeash, "Leash is null for alpha transition.");
- // Start transition with 0 alpha at the entry bounds.
- startTransaction.setPosition(pipLeash, destinationBounds.left, destinationBounds.top)
- .setWindowCrop(pipLeash, destinationBounds.width(), destinationBounds.height())
- .setAlpha(pipLeash, 0f);
+ final int delta = getFixedRotationDelta(info, pipChange, mPipDisplayLayoutState);
+ if (delta != ROTATION_0) {
+ updatePipChangesForFixedRotation(info, pipChange,
+ // We don't have an activity change to animate in legacy enter,
+ // so just use a placeholder one as the outPipActivityChange.
+ new TransitionInfo.Change(null /* container */, new SurfaceControl()));
+ }
+ startTransaction.setWindowCrop(pipLeash,
+ destinationBounds.width(), destinationBounds.height());
+ if (delta != ROTATION_0) {
+ // In a fixed rotation case, rotate PiP leash in the old orientation to its final
+ // position, but keep the bounds visually invariant until async rotation changes
+ // the display rotation after
+ int normalizedRotation = delta;
+ if (normalizedRotation == ROTATION_270) {
+ normalizedRotation = -ROTATION_90;
+ }
+ Matrix transformTensor = new Matrix();
+ final float[] matrixTmp = new float[9];
+ transformTensor.setTranslate(destinationBounds.left, destinationBounds.top);
+ transformTensor.postRotate(-normalizedRotation * 90f);
+
+ startTransaction.setMatrix(pipLeash, transformTensor, matrixTmp);
+ finishTransaction.setMatrix(pipLeash, transformTensor, matrixTmp);
+ } else {
+ startTransaction.setPosition(pipLeash, destinationBounds.left, destinationBounds.top);
+ }
PipAlphaAnimator animator = new PipAlphaAnimator(mContext, pipLeash, startTransaction,
finishTransaction, PipAlphaAnimator.FADE_IN);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/transition/PipTransitionUtils.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/transition/PipTransitionUtils.java
index 01cda6c91108..e562f3340217 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/transition/PipTransitionUtils.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/transition/PipTransitionUtils.java
@@ -67,6 +67,36 @@ public class PipTransitionUtils {
}
/**
+ * @return a change representing a config-at-end activity for ancestor.
+ */
+ @Nullable
+ public static TransitionInfo.Change getDeferConfigActivityChange(TransitionInfo info,
+ @NonNull WindowContainerToken ancestor) {
+ final TransitionInfo.Change ancestorChange =
+ PipTransitionUtils.getChangeByToken(info, ancestor);
+ if (ancestorChange == null) return null;
+
+ // Iterate through changes bottom-to-top, going up the parent chain starting with ancestor.
+ TransitionInfo.Change lastPipChildChange = ancestorChange;
+ for (int i = info.getChanges().size() - 1; i >= 0; --i) {
+ TransitionInfo.Change change = info.getChanges().get(i);
+ if (change == ancestorChange) continue;
+
+ if (change.getParent() != null
+ && change.getParent().equals(lastPipChildChange.getContainer())) {
+ // Found a child of the last cached child along the ancestral chain.
+ lastPipChildChange = change;
+ if (change.getTaskInfo() == null
+ && change.hasFlags(TransitionInfo.FLAG_CONFIG_AT_END)) {
+ // If this is a config-at-end activity change, then we found the chain leaf.
+ return change;
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
* @return the leash to interact with the container this change represents.
* @throws NullPointerException if the leash is null.
*/
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 bb5b5cec1b4a..382fa9640ff9 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
@@ -536,17 +536,20 @@ public class RecentTasksController implements TaskStackListenerCallback,
}
/**
- * Represents a desk whose ID is `mDeskId` and contains the tasks in `mDeskTasks`. Some of these
- * tasks are minimized and their IDs are contained in the `mMinimizedDeskTasks` set.
+ * Represents a desk whose ID is `mDeskId` inside the display with `mDisplayId` and contains
+ * the tasks in `mDeskTasks`. Some of these tasks are minimized and their IDs are contained
+ * in the `mMinimizedDeskTasks` set.
*/
private static class Desk {
final int mDeskId;
+ final int mDisplayId;
boolean mHasVisibleTasks = false;
final ArrayList<TaskInfo> mDeskTasks = new ArrayList<>();
final Set<Integer> mMinimizedDeskTasks = new HashSet<>();
- Desk(int deskId) {
+ Desk(int deskId, int displayId) {
mDeskId = deskId;
+ mDisplayId = displayId;
}
void addTask(TaskInfo taskInfo, boolean isMinimized, boolean isVisible) {
@@ -562,7 +565,8 @@ public class RecentTasksController implements TaskStackListenerCallback,
}
GroupedTaskInfo createDeskTaskInfo() {
- return GroupedTaskInfo.forDeskTasks(mDeskId, mDeskTasks, mMinimizedDeskTasks);
+ return GroupedTaskInfo.forDeskTasks(mDeskId, mDisplayId, mDeskTasks,
+ mMinimizedDeskTasks);
}
}
@@ -601,7 +605,8 @@ public class RecentTasksController implements TaskStackListenerCallback,
private Desk getOrCreateDesk(int deskId) {
var desk = mTmpDesks.get(deskId);
if (desk == null) {
- desk = new Desk(deskId);
+ desk = new Desk(deskId,
+ mDesktopUserRepositories.get().getCurrent().getDisplayForDesk(deskId));
mTmpDesks.put(deskId, desk);
}
return desk;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java
index 3e03e001c49b..8e10f15a36cc 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java
@@ -1135,6 +1135,7 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler,
if (openingLeafCount > 0) {
appearedTargets = new RemoteAnimationTarget[openingLeafCount];
}
+ boolean onlyOpeningPausedTasks = true;
int nextTargetIdx = 0;
for (int i = 0; i < openingTasks.size(); ++i) {
final TransitionInfo.Change change = openingTasks.get(i);
@@ -1188,6 +1189,7 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler,
" opening new leaf taskId=%d wasClosing=%b",
target.taskId, wasClosing);
mOpeningTasks.add(new TaskState(change, target.leash));
+ onlyOpeningPausedTasks = false;
} else {
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION,
" opening new taskId=%d", change.getTaskInfo().taskId);
@@ -1196,10 +1198,17 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler,
// is only animating the leafs.
startT.show(change.getLeash());
mOpeningTasks.add(new TaskState(change, null));
+ onlyOpeningPausedTasks = false;
}
}
didMergeThings = true;
- mState = STATE_NEW_TASK;
+ if (!onlyOpeningPausedTasks) {
+ // If we are only opening paused leaf tasks, then we aren't actually quick
+ // switching or launching a new task from overview, and if Launcher requests to
+ // finish(toHome=false) as a response to the pausing tasks being opened again,
+ // we should allow that to be considered returningToApp
+ mState = STATE_NEW_TASK;
+ }
}
if (mPausingTasks.isEmpty()) {
// The pausing tasks may be removed by the incoming closing tasks.
@@ -1368,8 +1377,9 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler,
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION,
"[%d] RecentsController.finishInner: toHome=%b userLeave=%b "
- + "willFinishToHome=%b state=%d reason=%s",
- mInstanceId, toHome, sendUserLeaveHint, mWillFinishToHome, mState, reason);
+ + "willFinishToHome=%b state=%d hasPausingTasks=%b reason=%s",
+ mInstanceId, toHome, sendUserLeaveHint, mWillFinishToHome, mState,
+ mPausingTasks != null, reason);
final SurfaceControl.Transaction t = mFinishTransaction;
final WindowContainerTransaction wct = new WindowContainerTransaction();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
index a0fb62508cc1..e19ad23b2125 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
@@ -394,6 +394,9 @@ public class DefaultMixedHandler implements MixedTransitionHandler,
if (mixed.mType == MixedTransition.TYPE_RECENTS_DURING_SPLIT) {
((RecentsMixedTransition) mixed).onAnimateRecentsDuringSplitFinishing(
returnToApp, finishWct, finishT);
+ } else if (mixed.mType == MixedTransition.TYPE_RECENTS_DURING_DESKTOP) {
+ ((RecentsMixedTransition) mixed).onAnimateRecentsDuringDesktopFinishing(
+ returnToApp, finishWct);
}
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedTransition.java
index 1853ffa96dfc..320a63a95302 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedTransition.java
@@ -34,6 +34,7 @@ import com.android.wm.shell.activityembedding.ActivityEmbeddingController;
import com.android.wm.shell.desktopmode.DesktopTasksController;
import com.android.wm.shell.keyguard.KeyguardTransitionHandler;
import com.android.wm.shell.pip.PipTransitionController;
+import com.android.wm.shell.pip2.phone.transition.PipTransitionUtils;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
import com.android.wm.shell.splitscreen.StageCoordinator;
import com.android.wm.shell.unfold.UnfoldTransitionHandler;
@@ -132,7 +133,7 @@ class DefaultMixedTransition extends DefaultMixedHandler.MixedTransition {
TransitionInfo.Change pipActivityChange = null;
if (pipChange != null) {
- pipActivityChange = mPipHandler.getDeferConfigActivityChange(
+ pipActivityChange = PipTransitionUtils.getDeferConfigActivityChange(
info, pipChange.getContainer());
everythingElse.getChanges().remove(pipActivityChange);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultSurfaceAnimator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultSurfaceAnimator.java
index ce98b03b77a6..bff08ba6d88f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultSurfaceAnimator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultSurfaceAnimator.java
@@ -42,9 +42,10 @@ public class DefaultSurfaceAnimator {
@NonNull Animation anim, @NonNull SurfaceControl leash,
@NonNull Runnable finishCallback, @NonNull TransactionPool pool,
@NonNull ShellExecutor mainExecutor, @Nullable Point position, float cornerRadius,
- @Nullable Rect clipRect) {
+ @Nullable Rect clipRect,
+ @Nullable TransitionAnimationHelper.RoundedContentPerDisplay roundedBounds) {
final DefaultAnimationAdapter adapter = new DefaultAnimationAdapter(anim, leash,
- position, clipRect, cornerRadius);
+ position, clipRect, cornerRadius, roundedBounds);
buildSurfaceAnimation(animations, anim, finishCallback, pool, mainExecutor, adapter);
}
@@ -109,9 +110,17 @@ public class DefaultSurfaceAnimator {
@Nullable final Rect mClipRect;
@Nullable private final Rect mAnimClipRect;
final float mCornerRadius;
+ final int mWindowBottom;
+
+ /**
+ * Inset changes aren't synchronized with transitions, so use a "provider" to track the
+ * bottom of the display content during the animation.
+ */
+ @Nullable final TransitionAnimationHelper.RoundedContentPerDisplay mRoundedContentBounds;
DefaultAnimationAdapter(@NonNull Animation anim, @NonNull SurfaceControl leash,
- @Nullable Point position, @Nullable Rect clipRect, float cornerRadius) {
+ @Nullable Point position, @Nullable Rect clipRect, float cornerRadius,
+ TransitionAnimationHelper.RoundedContentPerDisplay roundedBounds) {
super(leash);
mAnim = anim;
mPosition = (position != null && (position.x != 0 || position.y != 0))
@@ -119,6 +128,8 @@ public class DefaultSurfaceAnimator {
mClipRect = (clipRect != null && !clipRect.isEmpty()) ? clipRect : null;
mAnimClipRect = mClipRect != null ? new Rect() : null;
mCornerRadius = cornerRadius;
+ mWindowBottom = clipRect != null ? clipRect.bottom : 0;
+ mRoundedContentBounds = roundedBounds;
}
@Override
@@ -136,6 +147,11 @@ public class DefaultSurfaceAnimator {
if (mClipRect != null) {
boolean needCrop = false;
+ if (mRoundedContentBounds != null) {
+ mClipRect.bottom = Math.min(mRoundedContentBounds.mBounds.bottom,
+ mWindowBottom);
+ }
+
mAnimClipRect.set(mClipRect);
if (transformation.hasClipRect()) {
mAnimClipRect.intersectUnchecked(transformation.getClipRect());
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
index bf5800330979..e9200834c5dd 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
@@ -59,13 +59,13 @@ import static android.window.TransitionInfo.FLAG_STARTING_WINDOW_TRANSFER_RECIPI
import static android.window.TransitionInfo.FLAG_TRANSLUCENT;
import static com.android.internal.jank.Cuj.CUJ_DEFAULT_TASK_TO_TASK_ANIMATION;
-import static com.android.internal.policy.TransitionAnimation.DEFAULT_APP_TRANSITION_DURATION;
import static com.android.internal.policy.TransitionAnimation.WALLPAPER_TRANSITION_CHANGE;
import static com.android.internal.policy.TransitionAnimation.WALLPAPER_TRANSITION_CLOSE;
import static com.android.internal.policy.TransitionAnimation.WALLPAPER_TRANSITION_INTRA_CLOSE;
import static com.android.internal.policy.TransitionAnimation.WALLPAPER_TRANSITION_INTRA_OPEN;
import static com.android.internal.policy.TransitionAnimation.WALLPAPER_TRANSITION_NONE;
import static com.android.internal.policy.TransitionAnimation.WALLPAPER_TRANSITION_OPEN;
+import static com.android.wm.shell.Flags.enableDynamicInsetsForAppLaunch;
import static com.android.wm.shell.transition.DefaultSurfaceAnimator.buildSurfaceAnimation;
import static com.android.wm.shell.transition.TransitionAnimationHelper.getTransitionBackgroundColorIfSet;
import static com.android.wm.shell.transition.TransitionAnimationHelper.getTransitionTypeFromInfo;
@@ -111,11 +111,13 @@ import com.android.window.flags.Flags;
import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
import com.android.wm.shell.animation.SizeChangeAnimation;
import com.android.wm.shell.common.DisplayController;
+import com.android.wm.shell.common.DisplayInsetsController;
import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
import com.android.wm.shell.shared.TransactionPool;
import com.android.wm.shell.shared.TransitionUtil;
+import com.android.wm.shell.shared.animation.Interpolators;
import com.android.wm.shell.sysui.ShellInit;
import java.util.ArrayList;
@@ -125,6 +127,7 @@ import java.util.function.Consumer;
/** The default handler that handles anything not already handled. */
public class DefaultTransitionHandler implements Transitions.TransitionHandler {
private static final int MAX_ANIMATION_DURATION = 3000;
+ private static final int SIZE_CHANGE_ANIMATION_DURATION = 400;
private final TransactionPool mTransactionPool;
private final DisplayController mDisplayController;
@@ -134,6 +137,7 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler {
private final ShellExecutor mAnimExecutor;
private final TransitionAnimation mTransitionAnimation;
private final DevicePolicyManager mDevicePolicyManager;
+ private final TransitionAnimationHelper.RoundedContentTracker mRoundedContentBounds;
/** Keeps track of the currently-running animations associated with each transition. */
private final ArrayMap<IBinder, ArrayList<Animator>> mAnimations = new ArrayMap<>();
@@ -163,6 +167,7 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler {
DefaultTransitionHandler(@NonNull Context context,
@NonNull ShellInit shellInit,
@NonNull DisplayController displayController,
+ @NonNull DisplayInsetsController displayInsetsController,
@NonNull TransactionPool transactionPool,
@NonNull ShellExecutor mainExecutor, @NonNull Handler mainHandler,
@NonNull ShellExecutor animExecutor,
@@ -179,6 +184,8 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler {
mDevicePolicyManager = mContext.getSystemService(DevicePolicyManager.class);
shellInit.addInitCallback(this::onInit, this);
mRootTDAOrganizer = rootTDAOrganizer;
+ mRoundedContentBounds = new TransitionAnimationHelper.RoundedContentTracker(
+ displayController, displayInsetsController);
mInteractionJankMonitor = interactionJankMonitor;
}
@@ -191,6 +198,7 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler {
mMainHandler);
TransitionAnimation.initAttributeCache(mContext, mMainHandler);
+ mRoundedContentBounds.init();
}
private void updateEnterpriseThumbnailDrawable() {
@@ -297,6 +305,18 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler {
return ROTATION_ANIMATION_SEAMLESS;
}
+ @Nullable
+ final TransitionAnimationHelper.RoundedContentPerDisplay getRoundedContentBounds(
+ TransitionInfo.Change change) {
+ if (!enableDynamicInsetsForAppLaunch()) {
+ return null;
+ }
+ if (change.getTaskInfo() == null && change.getActivityComponent() == null) {
+ return null;
+ }
+ return mRoundedContentBounds.forDisplay(change.getEndDisplayId());
+ }
+
@Override
public boolean startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
@NonNull SurfaceControl.Transaction startTransaction,
@@ -556,7 +576,8 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler {
change.getEndAbsBounds().left - animRoot.getOffset().x,
change.getEndAbsBounds().top - animRoot.getOffset().y);
- if (change.getActivityComponent() != null) {
+ final boolean isActivity = change.getActivityComponent() != null;
+ if (isActivity) {
// For appcompat letterbox: we intentionally report the task-bounds so that we
// can animate as-if letterboxes are "part of" the activity. This means we can't
// always rely solely on endAbsBounds and need to also max with endRelOffset.
@@ -564,7 +585,7 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler {
animRelOffset.y = Math.max(animRelOffset.y, change.getEndRelOffset().y);
}
- if (change.getActivityComponent() != null && !isActivityLevel
+ if (isActivity && !isActivityLevel
&& !mRotator.isRotated(change)) {
// At this point, this is an independent activity change in a non-activity
// transition. This means that an activity transition got erroneously combined
@@ -589,8 +610,10 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler {
}
buildSurfaceAnimation(animations, a, change.getLeash(), onAnimFinish,
- mTransactionPool, mMainExecutor, animRelOffset, cornerRadius,
- clipRect);
+ mTransactionPool, mMainExecutor, animRelOffset, cornerRadius, clipRect,
+ isTask || isActivity
+ ? mRoundedContentBounds.forDisplay(change.getEndDisplayId())
+ : null);
final TransitionInfo.AnimationOptions options = change.getAnimationOptions();
if (options != null) {
@@ -779,15 +802,16 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler {
private void startBoundsChangeAnimation(@NonNull SurfaceControl.Transaction startT,
@NonNull ArrayList<Animator> animations, @NonNull TransitionInfo.Change change,
@NonNull Runnable finishCb, @NonNull ShellExecutor mainExecutor) {
- final SizeChangeAnimation sca =
- new SizeChangeAnimation(change.getStartAbsBounds(), change.getEndAbsBounds());
+ final SizeChangeAnimation sca = new SizeChangeAnimation(change.getStartAbsBounds(),
+ change.getEndAbsBounds(), /* initialScale= */ 1f, /* scaleFactor= */ 1f);
sca.initialize(change.getLeash(), change.getSnapshot(), startT);
final ValueAnimator va = sca.buildAnimator(change.getLeash(), change.getSnapshot(),
(animator) -> mainExecutor.execute(() -> {
animations.remove(animator);
finishCb.run();
}));
- va.setDuration(DEFAULT_APP_TRANSITION_DURATION);
+ va.setDuration(SIZE_CHANGE_ANIMATION_DURATION);
+ va.setInterpolator(Interpolators.EMPHASIZED);
animations.add(va);
}
@@ -932,7 +956,8 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler {
a.restrictDuration(MAX_ANIMATION_DURATION);
a.scaleCurrentDuration(mTransitionAnimationScaleSetting);
buildSurfaceAnimation(animations, a, wt.getSurface(), finisher, mTransactionPool,
- mMainExecutor, change.getEndRelOffset(), cornerRadius, change.getEndAbsBounds());
+ mMainExecutor, change.getEndRelOffset(), cornerRadius, change.getEndAbsBounds(),
+ getRoundedContentBounds(change));
}
private void attachThumbnailAnimation(@NonNull ArrayList<Animator> animations,
@@ -956,7 +981,8 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler {
a.restrictDuration(MAX_ANIMATION_DURATION);
a.scaleCurrentDuration(mTransitionAnimationScaleSetting);
buildSurfaceAnimation(animations, a, wt.getSurface(), finisher, mTransactionPool,
- mMainExecutor, change.getEndRelOffset(), cornerRadius, change.getEndAbsBounds());
+ mMainExecutor, change.getEndRelOffset(), cornerRadius, change.getEndAbsBounds(),
+ getRoundedContentBounds(change));
}
private static int getWallpaperTransitType(TransitionInfo info) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/HomeTransitionObserver.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/HomeTransitionObserver.java
index 23dfb41d52c1..c71458dec5ce 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/HomeTransitionObserver.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/HomeTransitionObserver.java
@@ -29,15 +29,18 @@ import android.annotation.NonNull;
import android.app.ActivityManager;
import android.content.Context;
import android.os.IBinder;
+import android.view.InsetsState;
import android.view.SurfaceControl;
import android.window.TransitionInfo;
+import com.android.wm.shell.common.DisplayInsetsController;
import com.android.wm.shell.common.RemoteCallable;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SingleInstanceRemoteListener;
import com.android.wm.shell.shared.IHomeTransitionListener;
import com.android.wm.shell.shared.TransitionUtil;
import com.android.wm.shell.shared.bubbles.BubbleAnythingFlagHelper;
+import com.android.wm.shell.sysui.ShellInit;
/**
* The {@link TransitionObserver} that observes for transitions involving the home
@@ -51,13 +54,30 @@ public class HomeTransitionObserver implements TransitionObserver,
private @NonNull final Context mContext;
private @NonNull final ShellExecutor mMainExecutor;
+ private @NonNull final DisplayInsetsController mDisplayInsetsController;
private IBinder mPendingStartDragTransition;
private Boolean mPendingHomeVisibilityUpdate;
public HomeTransitionObserver(@NonNull Context context,
- @NonNull ShellExecutor mainExecutor) {
+ @NonNull ShellExecutor mainExecutor,
+ @NonNull DisplayInsetsController displayInsetsController,
+ @NonNull ShellInit shellInit) {
mContext = context;
mMainExecutor = mainExecutor;
+ mDisplayInsetsController = displayInsetsController;
+
+ shellInit.addInitCallback(this::onInit, this);
+ }
+
+ private void onInit() {
+ mDisplayInsetsController.addInsetsChangedListener(DEFAULT_DISPLAY,
+ new DisplayInsetsController.OnInsetsChangedListener() {
+ @Override
+ public void insetsChanged(InsetsState insetsState) {
+ if (mListener == null) return;
+ mListener.call(l -> l.onDisplayInsetsChanged(insetsState));
+ }
+ });
}
@Override
@@ -153,7 +173,6 @@ public class HomeTransitionObserver implements TransitionObserver,
return;
}
mPendingStartDragTransition = null;
- if (aborted) return;
if (mPendingHomeVisibilityUpdate != null) {
notifyHomeVisibilityChanged(mPendingHomeVisibilityUpdate);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RecentsMixedTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RecentsMixedTransition.java
index 1e926c57ca61..cfefc1f1ac66 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RecentsMixedTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RecentsMixedTransition.java
@@ -198,6 +198,15 @@ class RecentsMixedTransition extends DefaultMixedHandler.MixedTransition {
}
}
+ /**
+ * Called when the recents animation during desktop is about to finish.
+ */
+ void onAnimateRecentsDuringDesktopFinishing(boolean returnToApp,
+ @NonNull WindowContainerTransaction finishWct) {
+ mDesktopTasksController.onRecentsInDesktopAnimationFinishing(mTransition, finishWct,
+ returnToApp);
+ }
+
@Override
void mergeAnimation(
@NonNull IBinder transition, @NonNull TransitionInfo info,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/ScreenRotationAnimation.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/ScreenRotationAnimation.java
index aa42b7f0ca76..8100f1d1a9a9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/ScreenRotationAnimation.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/ScreenRotationAnimation.java
@@ -347,21 +347,21 @@ class ScreenRotationAnimation {
@NonNull Runnable finishCallback, @NonNull ShellExecutor mainExecutor) {
buildSurfaceAnimation(animations, mRotateEnterAnimation, getEnterSurface(), finishCallback,
mTransactionPool, mainExecutor, null /* position */, 0 /* cornerRadius */,
- null /* clipRect */);
+ null /* clipRect */, null);
}
private void startScreenshotRotationAnimation(@NonNull ArrayList<Animator> animations,
@NonNull Runnable finishCallback, @NonNull ShellExecutor mainExecutor) {
buildSurfaceAnimation(animations, mRotateExitAnimation, mAnimLeash, finishCallback,
mTransactionPool, mainExecutor, null /* position */, 0 /* cornerRadius */,
- null /* clipRect */);
+ null /* clipRect */, null);
}
private void buildScreenshotAlphaAnimation(@NonNull ArrayList<Animator> animations,
@NonNull Runnable finishCallback, @NonNull ShellExecutor mainExecutor) {
buildSurfaceAnimation(animations, mRotateAlphaAnimation, mAnimLeash, finishCallback,
mTransactionPool, mainExecutor, null /* position */, 0 /* cornerRadius */,
- null /* clipRect */);
+ null /* clipRect */, null);
}
private void buildLumaAnimation(@NonNull ArrayList<Animator> animations,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/TransitionAnimationHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/TransitionAnimationHelper.java
index edfb56019a60..48b48640a37f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/TransitionAnimationHelper.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/TransitionAnimationHelper.java
@@ -39,6 +39,11 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.WindowConfiguration;
import android.graphics.Color;
+import android.graphics.Insets;
+import android.graphics.Rect;
+import android.util.SparseArray;
+import android.view.InsetsSource;
+import android.view.InsetsState;
import android.view.SurfaceControl;
import android.view.WindowManager;
import android.view.animation.Animation;
@@ -47,6 +52,9 @@ import android.window.TransitionInfo;
import com.android.internal.R;
import com.android.internal.policy.TransitionAnimation;
import com.android.internal.protolog.ProtoLog;
+import com.android.wm.shell.common.DisplayController;
+import com.android.wm.shell.common.DisplayInsetsController;
+import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
import com.android.wm.shell.shared.TransitionUtil;
@@ -325,4 +333,77 @@ public class TransitionAnimationHelper {
}
return false;
}
+
+ /**
+ * In some situations (eg. TaskBar) the content area of a display appears to be rounded. For
+ * these situations, we may want the animation to also express the same rounded corners (even
+ * though in steady-state, the app internally manages the insets). This class Keeps track of,
+ * and provides, the bounds of rounded-corner display content.
+ *
+ * This is used to enable already-running animations to adapt to changes in taskbar/navbar
+ * position live.
+ */
+ public static class RoundedContentPerDisplay implements
+ DisplayInsetsController.OnInsetsChangedListener {
+
+ /** The current bounds of the display content (post-inset). */
+ final Rect mBounds = new Rect();
+
+ @Override
+ public void insetsChanged(InsetsState insetsState) {
+ Insets insets = Insets.NONE;
+ for (int i = insetsState.sourceSize() - 1; i >= 0; i--) {
+ final InsetsSource source = insetsState.sourceAt(i);
+ if (!source.hasFlags(InsetsSource.FLAG_INSETS_ROUNDED_CORNER)) {
+ continue;
+ }
+ insets = Insets.max(source.calculateInsets(insetsState.getDisplayFrame(), false),
+ insets);
+ }
+ mBounds.set(insetsState.getDisplayFrame());
+ mBounds.inset(insets);
+ }
+ }
+
+ /**
+ * Keeps track of the bounds of rounded-corner display content (post-inset).
+ *
+ * @see RoundedContentPerDisplay
+ */
+ public static class RoundedContentTracker implements
+ DisplayController.OnDisplaysChangedListener {
+ final DisplayController mDisplayController;
+ final DisplayInsetsController mDisplayInsetsController;
+ final SparseArray<RoundedContentPerDisplay> mPerDisplay = new SparseArray<>();
+
+ RoundedContentTracker(DisplayController dc, DisplayInsetsController dic) {
+ mDisplayController = dc;
+ mDisplayInsetsController = dic;
+ }
+
+ void init() {
+ mDisplayController.addDisplayWindowListener(this);
+ }
+
+ RoundedContentPerDisplay forDisplay(int displayId) {
+ return mPerDisplay.get(displayId);
+ }
+
+ @Override
+ public void onDisplayAdded(int displayId) {
+ final RoundedContentPerDisplay perDisplay = new RoundedContentPerDisplay();
+ mDisplayInsetsController.addInsetsChangedListener(displayId, perDisplay);
+ mPerDisplay.put(displayId, perDisplay);
+ final DisplayLayout dl = mDisplayController.getDisplayLayout(displayId);
+ perDisplay.mBounds.set(0, 0, dl.width(), dl.height());
+ }
+
+ @Override
+ public void onDisplayRemoved(int displayId) {
+ final RoundedContentPerDisplay listener = mPerDisplay.removeReturnOld(displayId);
+ if (listener != null) {
+ mDisplayInsetsController.removeInsetsChangedListener(displayId, listener);
+ }
+ }
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
index e28a7fa159c5..3dc8733c879d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
@@ -39,6 +39,7 @@ import static android.window.TransitionInfo.FLAG_STARTING_WINDOW_TRANSFER_RECIPI
import static com.android.systemui.shared.Flags.returnAnimationFrameworkLongLived;
import static com.android.window.flags.Flags.ensureWallpaperInTransitions;
+import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_TRANSITIONS;
import static com.android.wm.shell.shared.TransitionUtil.FLAG_IS_DESKTOP_WALLPAPER_ACTIVITY;
import static com.android.wm.shell.shared.TransitionUtil.isClosingType;
import static com.android.wm.shell.shared.TransitionUtil.isOpeningType;
@@ -52,6 +53,7 @@ import android.content.ContentResolver;
import android.content.Context;
import android.content.pm.PackageManager;
import android.database.ContentObserver;
+import android.os.Build;
import android.os.Handler;
import android.os.IBinder;
import android.os.RemoteException;
@@ -82,6 +84,7 @@ import com.android.internal.protolog.ProtoLog;
import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.common.DisplayController;
+import com.android.wm.shell.common.DisplayInsetsController;
import com.android.wm.shell.common.ExternalInterfaceBinder;
import com.android.wm.shell.common.RemoteCallable;
import com.android.wm.shell.common.ShellExecutor;
@@ -138,6 +141,10 @@ public class Transitions implements RemoteCallable<Transitions>,
ShellCommandHandler.ShellCommandActionHandler {
static final String TAG = "ShellTransitions";
+ // If set, will print the stack trace for transition starts within the process
+ static final boolean DEBUG_START_TRANSITION = Build.IS_DEBUGGABLE &&
+ SystemProperties.getBoolean("persist.wm.debug.start_shell_transition", false);
+
/** Set to {@code true} to enable shell transitions. */
public static final boolean ENABLE_SHELL_TRANSITIONS = getShellTransitEnabled();
public static final boolean SHELL_TRANSITIONS_ROTATION = ENABLE_SHELL_TRANSITIONS
@@ -308,13 +315,14 @@ public class Transitions implements RemoteCallable<Transitions>,
@NonNull ShellTaskOrganizer organizer,
@NonNull TransactionPool pool,
@NonNull DisplayController displayController,
+ @NonNull DisplayInsetsController displayInsetsController,
@NonNull ShellExecutor mainExecutor,
@NonNull Handler mainHandler,
@NonNull ShellExecutor animExecutor,
@NonNull HomeTransitionObserver homeTransitionObserver,
@NonNull FocusTransitionObserver focusTransitionObserver) {
this(context, shellInit, new ShellCommandHandler(), shellController, organizer, pool,
- displayController, mainExecutor, mainHandler, animExecutor,
+ displayController, displayInsetsController, mainExecutor, mainHandler, animExecutor,
new RootTaskDisplayAreaOrganizer(mainExecutor, context, shellInit),
homeTransitionObserver, focusTransitionObserver);
}
@@ -326,6 +334,7 @@ public class Transitions implements RemoteCallable<Transitions>,
@NonNull ShellTaskOrganizer organizer,
@NonNull TransactionPool pool,
@NonNull DisplayController displayController,
+ @NonNull DisplayInsetsController displayInsetsController,
@NonNull ShellExecutor mainExecutor,
@NonNull Handler mainHandler,
@NonNull ShellExecutor animExecutor,
@@ -339,17 +348,17 @@ public class Transitions implements RemoteCallable<Transitions>,
mDisplayController = displayController;
mPlayerImpl = new TransitionPlayerImpl();
mDefaultTransitionHandler = new DefaultTransitionHandler(context, shellInit,
- displayController, pool, mainExecutor, mainHandler, animExecutor, rootTDAOrganizer,
- InteractionJankMonitor.getInstance());
+ displayController, displayInsetsController, pool, mainExecutor, mainHandler,
+ animExecutor, rootTDAOrganizer, InteractionJankMonitor.getInstance());
mRemoteTransitionHandler = new RemoteTransitionHandler(mMainExecutor);
mShellCommandHandler = shellCommandHandler;
mShellController = shellController;
// The very last handler (0 in the list) should be the default one.
mHandlers.add(mDefaultTransitionHandler);
- ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "addHandler: Default");
+ ProtoLog.v(WM_SHELL_TRANSITIONS, "addHandler: Default");
// Next lowest priority is remote transitions.
mHandlers.add(mRemoteTransitionHandler);
- ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "addHandler: Remote");
+ ProtoLog.v(WM_SHELL_TRANSITIONS, "addHandler: Remote");
shellInit.addInitCallback(this::onInit, this);
mHomeTransitionObserver = homeTransitionObserver;
mFocusTransitionObserver = focusTransitionObserver;
@@ -439,7 +448,7 @@ public class Transitions implements RemoteCallable<Transitions>,
mHandlers.add(handler);
// Set initial scale settings.
handler.setAnimScaleSetting(mTransitionAnimationScaleSetting);
- ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "addHandler: %s",
+ ProtoLog.v(WM_SHELL_TRANSITIONS, "addHandler: %s",
handler.getClass().getSimpleName());
}
@@ -596,6 +605,11 @@ public class Transitions implements RemoteCallable<Transitions>,
// Just in case there is a race with another animation (eg. recents finish()).
// Changes are visible->visible so it's a problem if it isn't visible.
t.show(leash);
+ // If there is a transient launch followed by a launch of one of the pausing tasks,
+ // we may end up with TRANSIT_TO_BACK followed by a CHANGE (w/ flag MOVE_TO_TOP),
+ // but since we are hiding the leash in the finish transaction above, we should also
+ // update the finish transaction here to reflect the change in visibility
+ finishT.show(leash);
}
}
}
@@ -691,7 +705,7 @@ public class Transitions implements RemoteCallable<Transitions>,
void onTransitionReady(@NonNull IBinder transitionToken, @NonNull TransitionInfo info,
@NonNull SurfaceControl.Transaction t, @NonNull SurfaceControl.Transaction finishT) {
info.setUnreleasedWarningCallSiteForAllSurfaces("Transitions.onTransitionReady");
- ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "onTransitionReady (#%d) %s: %s",
+ ProtoLog.v(WM_SHELL_TRANSITIONS, "onTransitionReady (#%d) %s: %s",
info.getDebugId(), transitionToken, info.toString(" " /* prefix */));
int activeIdx = findByToken(mPendingTransitions, transitionToken);
if (activeIdx < 0) {
@@ -753,7 +767,7 @@ public class Transitions implements RemoteCallable<Transitions>,
if (tr.isIdle()) continue;
hadPreceding = true;
// Sleep starts a process of forcing all prior transitions to finish immediately
- ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS,
+ ProtoLog.v(WM_SHELL_TRANSITIONS,
"Start finish-for-sync track %d", i);
finishForSync(active.mToken, i, null /* forceFinish */);
}
@@ -797,7 +811,7 @@ public class Transitions implements RemoteCallable<Transitions>,
if (info.getRootCount() == 0 && !KeyguardTransitionHandler.handles(info)) {
// No root-leashes implies that the transition is empty/no-op, so just do
// housekeeping and return.
- ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "No transition roots in %s so"
+ ProtoLog.v(WM_SHELL_TRANSITIONS, "No transition roots in %s so"
+ " abort", active);
onAbort(active);
return true;
@@ -839,7 +853,7 @@ public class Transitions implements RemoteCallable<Transitions>,
&& allOccluded)) {
// Treat this as an abort since we are bypassing any merge logic and effectively
// finishing immediately.
- ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS,
+ ProtoLog.v(WM_SHELL_TRANSITIONS,
"Non-visible anim so abort: %s", active);
onAbort(active);
return true;
@@ -873,7 +887,7 @@ public class Transitions implements RemoteCallable<Transitions>,
void processReadyQueue(Track track) {
if (track.mReadyTransitions.isEmpty()) {
if (track.mActiveTransition == null) {
- ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Track %d became idle",
+ ProtoLog.v(WM_SHELL_TRANSITIONS, "Track %d became idle",
mTracks.indexOf(track));
if (areTracksIdle()) {
if (!mReadyDuringSync.isEmpty()) {
@@ -885,7 +899,7 @@ public class Transitions implements RemoteCallable<Transitions>,
if (!success) break;
}
} else if (mPendingTransitions.isEmpty()) {
- ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "All active transition "
+ ProtoLog.v(WM_SHELL_TRANSITIONS, "All active transition "
+ "animations finished");
mKnownTransitions.clear();
// Run all runnables from the run-when-idle queue.
@@ -926,7 +940,7 @@ public class Transitions implements RemoteCallable<Transitions>,
onMerged(playingToken, readyToken);
return;
}
- ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Transition %s ready while"
+ ProtoLog.v(WM_SHELL_TRANSITIONS, "Transition %s ready while"
+ " %s is still animating. Notify the animating transition"
+ " in case they can be merged", ready, playing);
mTransitionTracer.logMergeRequested(ready.mInfo.getDebugId(), playing.mInfo.getDebugId());
@@ -955,7 +969,7 @@ public class Transitions implements RemoteCallable<Transitions>,
}
final Track track = mTracks.get(playing.getTrack());
- ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Transition was merged: %s into %s",
+ ProtoLog.v(WM_SHELL_TRANSITIONS, "Transition was merged: %s into %s",
merged, playing);
int readyIdx = 0;
if (track.mReadyTransitions.isEmpty() || track.mReadyTransitions.get(0) != merged) {
@@ -996,7 +1010,7 @@ public class Transitions implements RemoteCallable<Transitions>,
}
private void playTransition(@NonNull ActiveTransition active) {
- ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Playing animation for %s", active);
+ ProtoLog.v(WM_SHELL_TRANSITIONS, "Playing animation for %s", active);
final var token = active.mToken;
for (int i = 0; i < mObservers.size(); ++i) {
@@ -1007,12 +1021,12 @@ public class Transitions implements RemoteCallable<Transitions>,
// If a handler already chose to run this animation, try delegating to it first.
if (active.mHandler != null) {
- ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " try firstHandler %s",
+ ProtoLog.v(WM_SHELL_TRANSITIONS, " try firstHandler %s",
active.mHandler);
boolean consumed = active.mHandler.startAnimation(token, active.mInfo,
active.mStartT, active.mFinishT, (wct) -> onFinish(token, wct));
if (consumed) {
- ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " animated by firstHandler");
+ ProtoLog.v(WM_SHELL_TRANSITIONS, " animated by firstHandler");
mTransitionTracer.logDispatched(active.mInfo.getDebugId(), active.mHandler);
if (Trace.isTagEnabled(TRACE_TAG_WINDOW_MANAGER)) {
Trace.instant(TRACE_TAG_WINDOW_MANAGER,
@@ -1042,14 +1056,14 @@ public class Transitions implements RemoteCallable<Transitions>,
) {
for (int i = mHandlers.size() - 1; i >= 0; --i) {
if (mHandlers.get(i) == skip) {
- ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " skip handler %s",
+ ProtoLog.v(WM_SHELL_TRANSITIONS, " skip handler %s",
mHandlers.get(i));
continue;
}
boolean consumed = mHandlers.get(i).startAnimation(transition, info, startT, finishT,
finishCB);
if (consumed) {
- ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " animated by %s",
+ ProtoLog.v(WM_SHELL_TRANSITIONS, " animated by %s",
mHandlers.get(i));
mTransitionTracer.logDispatched(info.getDebugId(), mHandlers.get(i));
if (Trace.isTagEnabled(TRACE_TAG_WINDOW_MANAGER)) {
@@ -1155,7 +1169,7 @@ public class Transitions implements RemoteCallable<Transitions>,
for (int i = 0; i < mObservers.size(); ++i) {
mObservers.get(i).onTransitionFinished(active.mToken, active.mAborted);
}
- ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Transition animation finished "
+ ProtoLog.v(WM_SHELL_TRANSITIONS, "Transition animation finished "
+ "(aborted=%b), notifying core %s", active.mAborted, active);
if (active.mStartT != null) {
// Applied by now, so clear immediately to remove any references. Do not set to null
@@ -1209,7 +1223,7 @@ public class Transitions implements RemoteCallable<Transitions>,
void requestStartTransition(@NonNull IBinder transitionToken,
@Nullable TransitionRequestInfo request) {
- ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Transition requested (#%d): %s %s",
+ ProtoLog.v(WM_SHELL_TRANSITIONS, "Transition requested (#%d): %s %s",
request.getDebugId(), transitionToken, request);
if (mKnownTransitions.containsKey(transitionToken)) {
throw new RuntimeException("Transition already started " + transitionToken);
@@ -1228,6 +1242,8 @@ public class Transitions implements RemoteCallable<Transitions>,
if (requestResult != null) {
active.mHandler = requestResult.first;
wct = requestResult.second;
+ ProtoLog.v(WM_SHELL_TRANSITIONS, "Transition (#%d): request handled by %s",
+ request.getDebugId(), active.mHandler.getClass().getSimpleName());
}
if (request.getDisplayChange() != null) {
TransitionRequestInfo.DisplayChange change = request.getDisplayChange();
@@ -1273,8 +1289,12 @@ public class Transitions implements RemoteCallable<Transitions>,
*/
public IBinder startTransition(@WindowManager.TransitionType int type,
@NonNull WindowContainerTransaction wct, @Nullable TransitionHandler handler) {
- ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Directly starting a new transition "
+ ProtoLog.v(WM_SHELL_TRANSITIONS, "Directly starting a new transition "
+ "type=%s wct=%s handler=%s", transitTypeToString(type), wct, handler);
+ if (DEBUG_START_TRANSITION) {
+ Log.d(TAG, "startTransition: type=" + transitTypeToString(type)
+ + " wct=" + wct + " handler=" + handler.getClass().getName(), new Throwable());
+ }
final ActiveTransition active =
new ActiveTransition(mOrganizer.startNewTransition(type, wct));
active.mHandler = handler;
@@ -1362,7 +1382,7 @@ public class Transitions implements RemoteCallable<Transitions>,
}
// Attempt to merge a SLEEP info to signal that the playing transition needs to
// fast-forward.
- ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Attempt to merge sync %s"
+ ProtoLog.v(WM_SHELL_TRANSITIONS, " Attempt to merge sync %s"
+ " into %s via a SLEEP proxy", nextSync, playing);
playing.mHandler.mergeAnimation(nextSync.mToken, dummyInfo, dummyT, dummyT,
playing.mToken, (wct) -> {});
@@ -1598,7 +1618,7 @@ public class Transitions implements RemoteCallable<Transitions>,
public void onTransitionReady(IBinder iBinder, TransitionInfo transitionInfo,
SurfaceControl.Transaction t, SurfaceControl.Transaction finishT)
throws RemoteException {
- ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "onTransitionReady(transaction=%d)",
+ ProtoLog.v(WM_SHELL_TRANSITIONS, "onTransitionReady(transaction=%d)",
t.getId());
mMainExecutor.execute(() -> Transitions.this.onTransitionReady(
iBinder, transitionInfo, t, finishT));
@@ -1784,8 +1804,9 @@ public class Transitions implements RemoteCallable<Transitions>,
pw.println(prefix + TAG);
final String innerPrefix = prefix + " ";
- pw.println(prefix + "Handlers:");
- for (TransitionHandler handler : mHandlers) {
+ pw.println(prefix + "Handlers (ordered by priority):");
+ for (int i = mHandlers.size() - 1; i >= 0; i--) {
+ final TransitionHandler handler = mHandlers.get(i);
pw.print(innerPrefix);
pw.print(handler.getClass().getSimpleName());
pw.println(" (" + Integer.toHexString(System.identityHashCode(handler)) + ")");
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java
index 5e8c1fe2aa8d..e08ef5883390 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java
@@ -49,6 +49,7 @@ import android.view.ViewConfiguration;
import android.view.WindowInsets;
import android.view.WindowManager;
import android.view.WindowManagerGlobal;
+import android.window.DesktopExperienceFlags;
import android.window.DesktopModeFlags;
import android.window.WindowContainerTransaction;
@@ -218,11 +219,17 @@ public class CaptionWindowDecoration extends WindowDecoration<WindowDecorLinearL
relayoutParams.mRunningTaskInfo = taskInfo;
relayoutParams.mLayoutResId = R.layout.caption_window_decor;
relayoutParams.mCaptionHeightId = getCaptionHeightIdStatic(taskInfo.getWindowingMode());
- relayoutParams.mShadowRadius = hasGlobalFocus
- ? context.getResources().getDimensionPixelSize(
- R.dimen.freeform_decor_shadow_focused_thickness)
- : context.getResources().getDimensionPixelSize(
- R.dimen.freeform_decor_shadow_unfocused_thickness);
+ if (DesktopExperienceFlags.ENABLE_DYNAMIC_RADIUS_COMPUTATION_BUGFIX.isTrue()) {
+ relayoutParams.mShadowRadiusId = hasGlobalFocus
+ ? R.dimen.freeform_decor_shadow_focused_thickness
+ : R.dimen.freeform_decor_shadow_unfocused_thickness;
+ } else {
+ relayoutParams.mShadowRadius = hasGlobalFocus
+ ? context.getResources().getDimensionPixelSize(
+ R.dimen.freeform_decor_shadow_focused_thickness)
+ : context.getResources().getDimensionPixelSize(
+ R.dimen.freeform_decor_shadow_unfocused_thickness);
+ }
relayoutParams.mApplyStartTransactionOnDraw = applyStartTransactionOnDraw;
relayoutParams.mSetTaskVisibilityPositionAndCrop = shouldSetTaskVisibilityPositionAndCrop;
relayoutParams.mIsCaptionVisible = taskInfo.isFreeform()
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CarWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CarWindowDecoration.java
index 3182745d813e..f6acca95916f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CarWindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CarWindowDecoration.java
@@ -27,6 +27,7 @@ import android.view.InsetsState;
import android.view.SurfaceControl;
import android.view.View;
import android.view.WindowInsets;
+import android.window.DesktopModeFlags;
import android.window.WindowContainerTransaction;
import androidx.annotation.NonNull;
@@ -88,6 +89,9 @@ public class CarWindowDecoration extends WindowDecoration<WindowDecorLinearLayou
updateRelayoutParams(mRelayoutParams, taskInfo, isCaptionVisible);
relayout(mRelayoutParams, startT, finishT, wct, mRootView, mResult);
+ if (DesktopModeFlags.ENABLE_DESKTOP_APP_HANDLE_ANIMATION.isTrue()) {
+ setCaptionVisibility(isCaptionVisible);
+ }
// After this line, mTaskInfo is up-to-date and should be used instead of taskInfo
mBgExecutor.execute(() -> mTaskOrganizer.applyTransaction(wct));
@@ -102,6 +106,15 @@ public class CarWindowDecoration extends WindowDecoration<WindowDecorLinearLayou
}
}
+ private void setCaptionVisibility(boolean visible) {
+ if (mRootView == null) {
+ return;
+ }
+ final int v = visible ? View.VISIBLE : View.GONE;
+ final View captionView = mRootView.findViewById(getCaptionViewId());
+ captionView.setVisibility(v);
+ }
+
@Override
@NonNull
Rect calculateValidDragArea() {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
index 800faca830f4..69e1f36dec0b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
@@ -108,6 +108,8 @@ import com.android.wm.shell.common.MultiInstanceHelper;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.compatui.CompatUIController;
+import com.android.wm.shell.compatui.api.CompatUIHandler;
+import com.android.wm.shell.compatui.impl.CompatUIRequests;
import com.android.wm.shell.desktopmode.DesktopActivityOrientationChangeHandler;
import com.android.wm.shell.desktopmode.DesktopImmersiveController;
import com.android.wm.shell.desktopmode.DesktopModeEventLogger;
@@ -266,6 +268,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
private final DesktopTilingDecorViewModel mDesktopTilingDecorViewModel;
private final MultiDisplayDragMoveIndicatorController mMultiDisplayDragMoveIndicatorController;
private final LatencyTracker mLatencyTracker;
+ private final CompatUIHandler mCompatUI;
public DesktopModeWindowDecorViewModel(
Context context,
@@ -306,7 +309,8 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
RecentsTransitionHandler recentsTransitionHandler,
DesktopModeCompatPolicy desktopModeCompatPolicy,
DesktopTilingDecorViewModel desktopTilingDecorViewModel,
- MultiDisplayDragMoveIndicatorController multiDisplayDragMoveIndicatorController) {
+ MultiDisplayDragMoveIndicatorController multiDisplayDragMoveIndicatorController,
+ CompatUIHandler compatUI) {
this(
context,
shellExecutor,
@@ -353,7 +357,8 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
recentsTransitionHandler,
desktopModeCompatPolicy,
desktopTilingDecorViewModel,
- multiDisplayDragMoveIndicatorController);
+ multiDisplayDragMoveIndicatorController,
+ compatUI);
}
@VisibleForTesting
@@ -403,7 +408,8 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
RecentsTransitionHandler recentsTransitionHandler,
DesktopModeCompatPolicy desktopModeCompatPolicy,
DesktopTilingDecorViewModel desktopTilingDecorViewModel,
- MultiDisplayDragMoveIndicatorController multiDisplayDragMoveIndicatorController) {
+ MultiDisplayDragMoveIndicatorController multiDisplayDragMoveIndicatorController,
+ CompatUIHandler compatUI) {
mContext = context;
mMainExecutor = shellExecutor;
mMainHandler = mainHandler;
@@ -444,6 +450,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
mActivityOrientationChangeHandler = activityOrientationChangeHandler;
mAssistContentRequester = assistContentRequester;
mWindowDecorViewHostSupplier = windowDecorViewHostSupplier;
+ mCompatUI = compatUI;
mOnDisplayChangingListener = (displayId, fromRotation, toRotation, displayAreaInfo, t) -> {
DesktopModeWindowDecoration decoration;
RunningTaskInfo taskInfo;
@@ -1795,6 +1802,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
mMultiInstanceHelper,
mWindowDecorCaptionHandleRepository,
mDesktopModeEventLogger,
+ mDesktopModeUiEventLogger,
mDesktopModeCompatPolicy);
mWindowDecorByTaskId.put(taskInfo.taskId, windowDecoration);
@@ -1864,6 +1872,11 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
CompatUIController.launchUserAspectRatioSettings(mContext, taskInfo);
return Unit.INSTANCE;
});
+ windowDecoration.setOnRestartClickListener(() -> {
+ mCompatUI.sendCompatUIRequest(new CompatUIRequests.DisplayCompatShowRestartDialog(
+ taskInfo.taskId));
+ return Unit.INSTANCE;
+ });
windowDecoration.setOnMaximizeHoverListener(() -> {
if (!windowDecoration.isMaximizeMenuActive()) {
mDesktopModeUiEventLogger.log(taskInfo,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
index bcf9396ff0c1..50bc7b5e865b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
@@ -72,6 +72,7 @@ import android.view.WindowInsets;
import android.view.WindowManager;
import android.view.WindowManagerGlobal;
import android.widget.ImageButton;
+import android.window.DesktopExperienceFlags;
import android.window.DesktopModeFlags;
import android.window.TaskSnapshot;
import android.window.WindowContainerTransaction;
@@ -92,6 +93,7 @@ import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.desktopmode.CaptionState;
import com.android.wm.shell.desktopmode.DesktopModeEventLogger;
+import com.android.wm.shell.desktopmode.DesktopModeUiEventLogger;
import com.android.wm.shell.desktopmode.DesktopModeUtils;
import com.android.wm.shell.desktopmode.DesktopUserRepositories;
import com.android.wm.shell.desktopmode.WindowDecorCaptionHandleRepository;
@@ -162,6 +164,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
private Function0<Unit> mOnNewWindowClickListener;
private Function0<Unit> mOnManageWindowsClickListener;
private Function0<Unit> mOnChangeAspectRatioClickListener;
+ private Function0<Unit> mOnRestartClickListener;
private Function0<Unit> mOnMaximizeHoverListener;
private DragPositioningCallback mDragPositioningCallback;
private DragResizeInputListener mDragResizeListener;
@@ -209,6 +212,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
private final MultiInstanceHelper mMultiInstanceHelper;
private final WindowDecorCaptionHandleRepository mWindowDecorCaptionHandleRepository;
private final DesktopUserRepositories mDesktopUserRepositories;
+ private final DesktopModeUiEventLogger mDesktopModeUiEventLogger;
private boolean mIsRecentsTransitionRunning = false;
private boolean mIsDragging = false;
private Runnable mLoadAppInfoRunnable;
@@ -241,6 +245,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
MultiInstanceHelper multiInstanceHelper,
WindowDecorCaptionHandleRepository windowDecorCaptionHandleRepository,
DesktopModeEventLogger desktopModeEventLogger,
+ DesktopModeUiEventLogger desktopModeUiEventLogger,
DesktopModeCompatPolicy desktopModeCompatPolicy) {
this (context, userContext, displayController, taskResourceLoader, splitScreenController,
desktopUserRepositories, taskOrganizer, taskInfo, taskSurface, handler,
@@ -255,7 +260,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
DefaultMaximizeMenuFactory.INSTANCE,
DefaultHandleMenuFactory.INSTANCE, multiInstanceHelper,
windowDecorCaptionHandleRepository, desktopModeEventLogger,
- desktopModeCompatPolicy);
+ desktopModeUiEventLogger, desktopModeCompatPolicy);
}
DesktopModeWindowDecoration(
@@ -292,6 +297,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
MultiInstanceHelper multiInstanceHelper,
WindowDecorCaptionHandleRepository windowDecorCaptionHandleRepository,
DesktopModeEventLogger desktopModeEventLogger,
+ DesktopModeUiEventLogger desktopModeUiEventLogger,
DesktopModeCompatPolicy desktopModeCompatPolicy) {
super(context, userContext, displayController, taskOrganizer, taskInfo,
taskSurface, surfaceControlBuilderSupplier, surfaceControlTransactionSupplier,
@@ -319,6 +325,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
mTaskResourceLoader = taskResourceLoader;
mTaskResourceLoader.onWindowDecorCreated(taskInfo);
mDesktopModeCompatPolicy = desktopModeCompatPolicy;
+ mDesktopModeUiEventLogger = desktopModeUiEventLogger;
}
/**
@@ -407,6 +414,11 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
mOnChangeAspectRatioClickListener = listener;
}
+ /** Registers a listener to be called when the aspect ratio action is triggered. */
+ void setOnRestartClickListener(Function0<Unit> listener) {
+ mOnRestartClickListener = listener;
+ }
+
/** Registers a listener to be called when the maximize header button is hovered. */
void setOnMaximizeHoverListener(Function0<Unit> listener) {
mOnMaximizeHoverListener = listener;
@@ -719,7 +731,8 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
.getScaledTouchSlop();
final Resources res = mResult.mRootView.getResources();
final DragResizeWindowGeometry newGeometry = new DragResizeWindowGeometry(
- mRelayoutParams.mCornerRadius,
+ DesktopExperienceFlags.ENABLE_DYNAMIC_RADIUS_COMPUTATION_BUGFIX.isTrue()
+ ? mResult.mCornerRadius : mRelayoutParams.mCornerRadius,
new Size(mResult.mWidth, mResult.mHeight),
getResizeEdgeHandleSize(res), getResizeHandleEdgeInset(res),
getFineResizeCornerSize(res), getLargeResizeCornerSize(res),
@@ -861,7 +874,8 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
if (!isAppHandle(mWindowDecorViewHolder)) return;
asAppHandle(mWindowDecorViewHolder).bindData(new AppHandleViewHolder.HandleData(
mTaskInfo, determineHandlePosition(), mResult.mCaptionWidth,
- mResult.mCaptionHeight, isCaptionVisible()
+ mResult.mCaptionHeight, /* showInputLayer= */ isCaptionVisible(),
+ /* isCaptionVisible= */ isCaptionVisible()
));
}
@@ -874,7 +888,8 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
inFullImmersive,
hasGlobalFocus,
/* maximizeHoverEnabled= */ canOpenMaximizeMenu(
- /* animatingTaskResizeOrReposition= */ false)
+ /* animatingTaskResizeOrReposition= */ false),
+ isCaptionVisible()
));
}
@@ -885,10 +900,10 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
mOnCaptionTouchListener,
mOnCaptionButtonClickListener,
mWindowManagerWrapper,
- mHandler
+ mHandler,
+ mDesktopModeUiEventLogger
);
- } else if (mRelayoutParams.mLayoutResId
- == R.layout.desktop_mode_app_header) {
+ } else if (mRelayoutParams.mLayoutResId == R.layout.desktop_mode_app_header) {
return mAppHeaderViewHolderFactory.create(
mResult.mRootView,
mOnCaptionTouchListener,
@@ -898,7 +913,8 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
mOnLeftSnapClickListener,
mOnRightSnapClickListener,
mOnMaximizeOrRestoreClickListener,
- mOnMaximizeHoverListener);
+ mOnMaximizeHoverListener,
+ mDesktopModeUiEventLogger);
}
throw new IllegalArgumentException("Unexpected layout resource id");
}
@@ -1072,13 +1088,23 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
}
if (isAppHeader
&& DesktopModeStatus.useWindowShadow(/* isFocusedWindow= */ hasGlobalFocus)) {
- relayoutParams.mShadowRadius = hasGlobalFocus
- ? context.getResources().getDimensionPixelSize(
- R.dimen.freeform_decor_shadow_focused_thickness)
- : context.getResources().getDimensionPixelSize(
- R.dimen.freeform_decor_shadow_unfocused_thickness);
+ if (DesktopExperienceFlags.ENABLE_DYNAMIC_RADIUS_COMPUTATION_BUGFIX.isTrue()) {
+ relayoutParams.mShadowRadiusId = hasGlobalFocus
+ ? R.dimen.freeform_decor_shadow_focused_thickness
+ : R.dimen.freeform_decor_shadow_unfocused_thickness;
+ } else {
+ relayoutParams.mShadowRadius = hasGlobalFocus
+ ? context.getResources().getDimensionPixelSize(
+ R.dimen.freeform_decor_shadow_focused_thickness)
+ : context.getResources().getDimensionPixelSize(
+ R.dimen.freeform_decor_shadow_unfocused_thickness);
+ }
} else {
- relayoutParams.mShadowRadius = INVALID_SHADOW_RADIUS;
+ if (DesktopExperienceFlags.ENABLE_DYNAMIC_RADIUS_COMPUTATION_BUGFIX.isTrue()) {
+ relayoutParams.mShadowRadiusId = Resources.ID_NULL;
+ } else {
+ relayoutParams.mShadowRadius = INVALID_SHADOW_RADIUS;
+ }
}
relayoutParams.mApplyStartTransactionOnDraw = applyStartTransactionOnDraw;
relayoutParams.mSetTaskVisibilityPositionAndCrop = shouldSetTaskVisibilityPositionAndCrop;
@@ -1104,8 +1130,13 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
relayoutParams.mWindowDecorConfig = windowDecorConfig;
if (DesktopModeStatus.useRoundedCorners()) {
- relayoutParams.mCornerRadius = shouldIgnoreCornerRadius ? INVALID_CORNER_RADIUS :
- getCornerRadius(context, relayoutParams.mLayoutResId);
+ if (DesktopExperienceFlags.ENABLE_DYNAMIC_RADIUS_COMPUTATION_BUGFIX.isTrue()) {
+ relayoutParams.mCornerRadiusId = shouldIgnoreCornerRadius ? Resources.ID_NULL :
+ getCornerRadiusId(relayoutParams.mLayoutResId);
+ } else {
+ relayoutParams.mCornerRadius = shouldIgnoreCornerRadius ? INVALID_CORNER_RADIUS :
+ getCornerRadius(context, relayoutParams.mLayoutResId);
+ }
}
// Set opaque background for all freeform tasks to prevent freeform tasks below
// from being visible if freeform task window above is translucent.
@@ -1113,6 +1144,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
relayoutParams.mShouldSetBackground = DesktopModeStatus.shouldSetBackground(taskInfo);
}
+ @Deprecated
private static int getCornerRadius(@NonNull Context context, int layoutResId) {
if (layoutResId == R.layout.desktop_mode_app_header) {
return loadDimensionPixelSize(context.getResources(),
@@ -1122,6 +1154,14 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
return INVALID_CORNER_RADIUS;
}
+ private static int getCornerRadiusId(int layoutResId) {
+ if (layoutResId == R.layout.desktop_mode_app_header) {
+ return com.android.wm.shell.shared.R.dimen
+ .desktop_windowing_freeform_rounded_corner_radius;
+ }
+ return Resources.ID_NULL;
+ }
+
/**
* If task has focused window decor, return the caption id of the fullscreen caption size
* resource. Otherwise, return ID_NULL and caption width be set to task width.
@@ -1338,7 +1378,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
mMaximizeMenu = mMaximizeMenuFactory.create(mSyncQueue, mRootTaskDisplayAreaOrganizer,
mDisplayController, mTaskInfo, mContext,
(width, height) -> calculateMaximizeMenuPosition(width, height),
- mSurfaceControlTransactionSupplier);
+ mSurfaceControlTransactionSupplier, mDesktopModeUiEventLogger);
mMaximizeMenu.show(
/* isTaskInImmersiveMode= */
@@ -1433,6 +1473,8 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
&& mMinimumInstancesFound;
final boolean shouldShowChangeAspectRatioButton = HandleMenu.Companion
.shouldShowChangeAspectRatioButton(mTaskInfo);
+ final boolean shouldShowRestartButton = HandleMenu.Companion
+ .shouldShowRestartButton(mTaskInfo);
final boolean inDesktopImmersive = mDesktopUserRepositories.getProfile(mTaskInfo.userId)
.isTaskInFullImmersiveState(mTaskInfo.taskId);
final boolean isBrowserApp = isBrowserApp();
@@ -1449,8 +1491,10 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
shouldShowManageWindowsButton,
shouldShowChangeAspectRatioButton,
isDesktopModeSupportedOnDisplay(mContext, mDisplay),
+ shouldShowRestartButton,
isBrowserApp,
isBrowserApp ? getAppLink() : getBrowserLink(),
+ mDesktopModeUiEventLogger,
mResult.mCaptionWidth,
mResult.mCaptionHeight,
mResult.mCaptionX,
@@ -1485,6 +1529,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
}
return Unit.INSTANCE;
},
+ /* onRestartClickListener= */ mOnRestartClickListener,
/* onCloseMenuClickListener= */ () -> {
closeHandleMenu();
return Unit.INSTANCE;
@@ -1756,8 +1801,10 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
mExclusionRegionListener.onExclusionRegionDismissed(mTaskInfo.taskId);
disposeResizeVeil();
disposeStatusBarInputLayer();
- mWindowDecorViewHolder.close();
- mWindowDecorViewHolder = null;
+ if (mWindowDecorViewHolder != null) {
+ mWindowDecorViewHolder.close();
+ mWindowDecorViewHolder = null;
+ }
if (canEnterDesktopMode(mContext) && isEducationEnabled()) {
notifyNoCaptionHandle();
}
@@ -1838,7 +1885,8 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
DesktopModeUtils.isTaskMaximized(mTaskInfo, mDisplayController),
inFullImmersive,
isFocused(),
- /* maximizeHoverEnabled= */ canOpenMaximizeMenu(animatingTaskResizeOrReposition)));
+ /* maximizeHoverEnabled= */ canOpenMaximizeMenu(animatingTaskResizeOrReposition),
+ isCaptionVisible()));
}
/**
@@ -1932,6 +1980,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
MultiInstanceHelper multiInstanceHelper,
WindowDecorCaptionHandleRepository windowDecorCaptionHandleRepository,
DesktopModeEventLogger desktopModeEventLogger,
+ DesktopModeUiEventLogger desktopModeUiEventLogger,
DesktopModeCompatPolicy desktopModeCompatPolicy) {
return new DesktopModeWindowDecoration(
context,
@@ -1959,6 +2008,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
multiInstanceHelper,
windowDecorCaptionHandleRepository,
desktopModeEventLogger,
+ desktopModeUiEventLogger,
desktopModeCompatPolicy);
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.kt
index 9cc64ac9c276..cdadce57d610 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.kt
@@ -28,6 +28,7 @@ import android.graphics.Bitmap
import android.graphics.Point
import android.graphics.PointF
import android.graphics.Rect
+import android.os.Bundle
import android.view.LayoutInflater
import android.view.MotionEvent
import android.view.MotionEvent.ACTION_OUTSIDE
@@ -35,9 +36,9 @@ import android.view.SurfaceControl
import android.view.View
import android.view.WindowInsets.Type.systemBars
import android.view.WindowManager
+import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction
import android.widget.ImageButton
import android.widget.ImageView
-import android.widget.LinearLayout
import android.widget.Space
import android.window.DesktopModeFlags
import android.window.SurfaceSyncGroup
@@ -49,10 +50,14 @@ import androidx.core.view.accessibility.AccessibilityNodeInfoCompat.Accessibilit
import androidx.core.view.isGone
import com.android.window.flags.Flags
import com.android.wm.shell.R
-import com.android.wm.shell.bubbles.ContextUtils.isRtl
+import com.android.wm.shell.desktopmode.DesktopModeUiEventLogger
+import com.android.wm.shell.desktopmode.DesktopModeUiEventLogger.DesktopUiEventEnum.A11Y_APP_HANDLE_MENU_DESKTOP_VIEW
+import com.android.wm.shell.desktopmode.DesktopModeUiEventLogger.DesktopUiEventEnum.A11Y_APP_HANDLE_MENU_FULLSCREEN
+import com.android.wm.shell.desktopmode.DesktopModeUiEventLogger.DesktopUiEventEnum.A11Y_APP_HANDLE_MENU_SPLIT_SCREEN
import com.android.wm.shell.shared.annotations.ShellBackgroundThread
import com.android.wm.shell.shared.annotations.ShellMainThread
import com.android.wm.shell.shared.bubbles.BubbleAnythingFlagHelper
+import com.android.wm.shell.shared.bubbles.ContextUtils.isRtl
import com.android.wm.shell.shared.split.SplitScreenConstants
import com.android.wm.shell.splitscreen.SplitScreenController
import com.android.wm.shell.windowdecor.additionalviewcontainer.AdditionalSystemViewContainer
@@ -95,8 +100,10 @@ class HandleMenu(
private val shouldShowManageWindowsButton: Boolean,
private val shouldShowChangeAspectRatioButton: Boolean,
private val shouldShowDesktopModeButton: Boolean,
+ private val shouldShowRestartButton: Boolean,
private val isBrowserApp: Boolean,
private val openInAppOrBrowserIntent: Intent?,
+ private val desktopModeUiEventLogger: DesktopModeUiEventLogger,
private val captionWidth: Int,
private val captionHeight: Int,
captionX: Int,
@@ -139,7 +146,8 @@ class HandleMenu(
private val shouldShowMoreActionsPill: Boolean
get() = SHOULD_SHOW_SCREENSHOT_BUTTON || shouldShowNewWindowButton ||
- shouldShowManageWindowsButton || shouldShowChangeAspectRatioButton
+ shouldShowManageWindowsButton || shouldShowChangeAspectRatioButton ||
+ shouldShowRestartButton
private var loadAppInfoJob: Job? = null
@@ -157,6 +165,7 @@ class HandleMenu(
onChangeAspectRatioClickListener: () -> Unit,
openInAppOrBrowserClickListener: (Intent) -> Unit,
onOpenByDefaultClickListener: () -> Unit,
+ onRestartClickListener: () -> Unit,
onCloseMenuClickListener: () -> Unit,
onOutsideTouchListener: () -> Unit,
forceShowSystemBars: Boolean = false,
@@ -176,6 +185,7 @@ class HandleMenu(
onChangeAspectRatioClickListener = onChangeAspectRatioClickListener,
openInAppOrBrowserClickListener = openInAppOrBrowserClickListener,
onOpenByDefaultClickListener = onOpenByDefaultClickListener,
+ onRestartClickListener = onRestartClickListener,
onCloseMenuClickListener = onCloseMenuClickListener,
onOutsideTouchListener = onOutsideTouchListener,
forceShowSystemBars = forceShowSystemBars,
@@ -198,12 +208,14 @@ class HandleMenu(
onChangeAspectRatioClickListener: () -> Unit,
openInAppOrBrowserClickListener: (Intent) -> Unit,
onOpenByDefaultClickListener: () -> Unit,
+ onRestartClickListener: () -> Unit,
onCloseMenuClickListener: () -> Unit,
onOutsideTouchListener: () -> Unit,
forceShowSystemBars: Boolean = false,
) {
val handleMenuView = HandleMenuView(
context = context,
+ desktopModeUiEventLogger = desktopModeUiEventLogger,
menuWidth = menuWidth,
captionHeight = captionHeight,
shouldShowWindowingPill = shouldShowWindowingPill,
@@ -212,6 +224,7 @@ class HandleMenu(
shouldShowManageWindowsButton = shouldShowManageWindowsButton,
shouldShowChangeAspectRatioButton = shouldShowChangeAspectRatioButton,
shouldShowDesktopModeButton = shouldShowDesktopModeButton,
+ shouldShowRestartButton = shouldShowRestartButton,
isBrowserApp = isBrowserApp
).apply {
bind(taskInfo, shouldShowMoreActionsPill)
@@ -225,6 +238,7 @@ class HandleMenu(
this.onOpenInAppOrBrowserClickListener = {
openInAppOrBrowserClickListener.invoke(openInAppOrBrowserIntent!!)
}
+ this.onRestartClickListener = onRestartClickListener
this.onOpenByDefaultClickListener = onOpenByDefaultClickListener
this.onCloseMenuClickListener = onCloseMenuClickListener
this.onOutsideTouchListener = onOutsideTouchListener
@@ -431,6 +445,10 @@ class HandleMenu(
R.dimen.desktop_mode_handle_menu_change_aspect_ratio_height
)
}
+ if (!shouldShowRestartButton) {
+ menuHeight -= loadDimensionPixelSize(
+ R.dimen.desktop_mode_handle_menu_restart_button_height)
+ }
if (!shouldShowMoreActionsPill) {
menuHeight -= pillTopMargin
}
@@ -465,6 +483,7 @@ class HandleMenu(
@SuppressLint("ClickableViewAccessibility")
class HandleMenuView(
private val context: Context,
+ private val desktopModeUiEventLogger: DesktopModeUiEventLogger,
menuWidth: Int,
captionHeight: Int,
private val shouldShowWindowingPill: Boolean,
@@ -473,6 +492,7 @@ class HandleMenu(
private val shouldShowManageWindowsButton: Boolean,
private val shouldShowChangeAspectRatioButton: Boolean,
private val shouldShowDesktopModeButton: Boolean,
+ private val shouldShowRestartButton: Boolean,
private val isBrowserApp: Boolean
) {
val rootView = LayoutInflater.from(context)
@@ -501,6 +521,12 @@ class HandleMenu(
t = iconButtondrawableBaseInset,
b = iconButtondrawableBaseInset, l = 0, r = iconButtondrawableShiftInset
)
+ private val iconButtonDrawableInsetStart
+ get() =
+ if (context.isRtl) iconButtonDrawableInsetsRight else iconButtonDrawableInsetsLeft
+ private val iconButtonDrawableInsetEnd
+ get() =
+ if (context.isRtl) iconButtonDrawableInsetsLeft else iconButtonDrawableInsetsRight
// App Info Pill.
private val appInfoPill = rootView.requireViewById<View>(R.id.app_info_pill)
@@ -544,14 +570,15 @@ class HandleMenu(
.requireViewById<HandleMenuActionButton>(R.id.manage_windows_button)
private val changeAspectRatioBtn = moreActionsPill
.requireViewById<HandleMenuActionButton>(R.id.change_aspect_ratio_button)
+ private val restartBtn = moreActionsPill
+ .requireViewById<HandleMenuActionButton>(R.id.handle_menu_restart_button)
// Open in Browser/App Pill.
private val openInAppOrBrowserPill = rootView.requireViewById<View>(
R.id.open_in_app_or_browser_pill
)
- private val openInAppOrBrowserBtn = openInAppOrBrowserPill.requireViewById<View>(
- R.id.open_in_app_or_browser_button
- )
+ private val openInAppOrBrowserBtn = openInAppOrBrowserPill
+ .requireViewById<HandleMenuActionButton>(R.id.open_in_app_or_browser_button)
private val openByDefaultBtn = openInAppOrBrowserPill.requireViewById<ImageButton>(
R.id.open_by_default_button
)
@@ -570,6 +597,7 @@ class HandleMenu(
var onChangeAspectRatioClickListener: (() -> Unit)? = null
var onOpenInAppOrBrowserClickListener: (() -> Unit)? = null
var onOpenByDefaultClickListener: (() -> Unit)? = null
+ var onRestartClickListener: (() -> Unit)? = null
var onCloseMenuClickListener: (() -> Unit)? = null
var onOutsideTouchListener: (() -> Unit)? = null
@@ -586,6 +614,7 @@ class HandleMenu(
newWindowBtn.setOnClickListener { onNewWindowClickListener?.invoke() }
manageWindowBtn.setOnClickListener { onManageWindowsClickListener?.invoke() }
changeAspectRatioBtn.setOnClickListener { onChangeAspectRatioClickListener?.invoke() }
+ restartBtn.setOnClickListener { onRestartClickListener?.invoke() }
rootView.setOnTouchListener { _, event ->
if (event.actionMasked == ACTION_OUTSIDE) {
@@ -595,6 +624,45 @@ class HandleMenu(
return@setOnTouchListener true
}
+ desktopBtn.accessibilityDelegate = object : View.AccessibilityDelegate() {
+ override fun performAccessibilityAction(
+ host: View,
+ action: Int,
+ args: Bundle?
+ ): Boolean {
+ if (action == AccessibilityAction.ACTION_CLICK.id) {
+ desktopModeUiEventLogger.log(taskInfo, A11Y_APP_HANDLE_MENU_DESKTOP_VIEW)
+ }
+ return super.performAccessibilityAction(host, action, args)
+ }
+ }
+
+ fullscreenBtn.accessibilityDelegate = object : View.AccessibilityDelegate() {
+ override fun performAccessibilityAction(
+ host: View,
+ action: Int,
+ args: Bundle?
+ ): Boolean {
+ if (action == AccessibilityAction.ACTION_CLICK.id) {
+ desktopModeUiEventLogger.log(taskInfo, A11Y_APP_HANDLE_MENU_FULLSCREEN)
+ }
+ return super.performAccessibilityAction(host, action, args)
+ }
+ }
+
+ splitscreenBtn.accessibilityDelegate = object : View.AccessibilityDelegate() {
+ override fun performAccessibilityAction(
+ host: View,
+ action: Int,
+ args: Bundle?
+ ): Boolean {
+ if (action == AccessibilityAction.ACTION_CLICK.id) {
+ desktopModeUiEventLogger.log(taskInfo, A11Y_APP_HANDLE_MENU_SPLIT_SCREEN)
+ }
+ return super.performAccessibilityAction(host, action, args)
+ }
+ }
+
with(context) {
// Update a11y announcement out to say "double tap to enter Fullscreen"
ViewCompat.replaceAccessibilityAction(
@@ -758,20 +826,16 @@ class HandleMenu(
floatingBtn.isEnabled = !taskInfo.isPinned
floatingBtn.imageTintList = style.windowingButtonColor
desktopBtn.isGone = !shouldShowDesktopModeButton
+ desktopBtnSpace.isGone = !shouldShowDesktopModeButton
desktopBtn.isSelected = taskInfo.isFreeform
desktopBtn.isEnabled = !taskInfo.isFreeform
desktopBtn.imageTintList = style.windowingButtonColor
- val startInsets = if (context.isRtl) iconButtonDrawableInsetsRight
- else iconButtonDrawableInsetsLeft
- val endInsets = if (context.isRtl) iconButtonDrawableInsetsLeft
- else iconButtonDrawableInsetsRight
-
fullscreenBtn.apply {
background = createBackgroundDrawable(
color = style.textColor,
cornerRadius = iconButtonRippleRadius,
- drawableInsets = startInsets
+ drawableInsets = iconButtonDrawableInsetStart
)
}
@@ -795,7 +859,7 @@ class HandleMenu(
background = createBackgroundDrawable(
color = style.textColor,
cornerRadius = iconButtonRippleRadius,
- drawableInsets = endInsets
+ drawableInsets = iconButtonDrawableInsetEnd
)
}
}
@@ -808,20 +872,16 @@ class HandleMenu(
newWindowBtn to shouldShowNewWindowButton,
manageWindowBtn to shouldShowManageWindowsButton,
changeAspectRatioBtn to shouldShowChangeAspectRatioButton,
- ).forEach {
- val button = it.first
- val shouldShow = it.second
-
- val buttonRoot = button.requireViewById<LinearLayout>(R.id.action_button)
- val label = buttonRoot.requireViewById<MarqueedTextView>(R.id.label)
- val image = buttonRoot.requireViewById<ImageView>(R.id.image)
-
- button.isGone = !shouldShow
- label.apply {
- setTextColor(style.textColor)
- startMarquee()
+ restartBtn to shouldShowRestartButton,
+ ).forEach { (button, shouldShow) ->
+ button.apply {
+ isGone = !shouldShow
+ textView.apply {
+ setTextColor(style.textColor)
+ startMarquee()
+ }
+ iconView.imageTintList = ColorStateList.valueOf(style.textColor)
}
- image.imageTintList = ColorStateList.valueOf(style.textColor)
}
}
@@ -837,20 +897,24 @@ class HandleMenu(
getString(R.string.open_in_browser_text)
}
- val buttonRoot = openInAppOrBrowserBtn.requireViewById<LinearLayout>(R.id.action_button)
- val label = openInAppOrBrowserBtn.requireViewById<MarqueedTextView>(R.id.label)
- val image = openInAppOrBrowserBtn.requireViewById<ImageView>(R.id.image)
- openInAppOrBrowserBtn.contentDescription = btnText
- buttonRoot.contentDescription = btnText
- label.apply {
- text = btnText
- setTextColor(style.textColor)
- startMarquee()
+ openInAppOrBrowserBtn.apply {
+ contentDescription = btnText
+ textView.apply {
+ text = btnText
+ setTextColor(style.textColor)
+ startMarquee()
+ }
+ iconView.imageTintList = ColorStateList.valueOf(style.textColor)
}
- image.imageTintList = ColorStateList.valueOf(style.textColor)
- openByDefaultBtn.isGone = isBrowserApp
- openByDefaultBtn.imageTintList = ColorStateList.valueOf(style.textColor)
+ openByDefaultBtn.apply {
+ isGone = isBrowserApp
+ imageTintList = ColorStateList.valueOf(style.textColor)
+ background = createBackgroundDrawable(
+ color = style.textColor,
+ cornerRadius = iconButtonRippleRadius,
+ drawableInsets = iconButtonDrawableInsetEnd)
+ }
}
private fun getString(@StringRes resId: Int): String = context.resources.getString(resId)
@@ -873,6 +937,13 @@ class HandleMenu(
fun shouldShowChangeAspectRatioButton(taskInfo: RunningTaskInfo): Boolean =
taskInfo.appCompatTaskInfo.eligibleForUserAspectRatioButton() &&
taskInfo.windowingMode == WindowConfiguration.WINDOWING_MODE_FULLSCREEN
+
+ /**
+ * Returns whether the restart button should be shown for the task. It usually means that
+ * the task has moved to a different display.
+ */
+ fun shouldShowRestartButton(taskInfo: RunningTaskInfo): Boolean =
+ taskInfo.appCompatTaskInfo.isRestartMenuEnabledForDisplayMove
}
}
@@ -891,8 +962,10 @@ interface HandleMenuFactory {
shouldShowManageWindowsButton: Boolean,
shouldShowChangeAspectRatioButton: Boolean,
shouldShowDesktopModeButton: Boolean,
+ shouldShowRestartButton: Boolean,
isBrowserApp: Boolean,
openInAppOrBrowserIntent: Intent?,
+ desktopModeUiEventLogger: DesktopModeUiEventLogger,
captionWidth: Int,
captionHeight: Int,
captionX: Int,
@@ -915,8 +988,10 @@ object DefaultHandleMenuFactory : HandleMenuFactory {
shouldShowManageWindowsButton: Boolean,
shouldShowChangeAspectRatioButton: Boolean,
shouldShowDesktopModeButton: Boolean,
+ shouldShowRestartButton: Boolean,
isBrowserApp: Boolean,
openInAppOrBrowserIntent: Intent?,
+ desktopModeUiEventLogger: DesktopModeUiEventLogger,
captionWidth: Int,
captionHeight: Int,
captionX: Int,
@@ -935,8 +1010,10 @@ object DefaultHandleMenuFactory : HandleMenuFactory {
shouldShowManageWindowsButton,
shouldShowChangeAspectRatioButton,
shouldShowDesktopModeButton,
+ shouldShowRestartButton,
isBrowserApp,
openInAppOrBrowserIntent,
+ desktopModeUiEventLogger,
captionWidth,
captionHeight,
captionX,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenuActionButton.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenuActionButton.kt
index a723a7a4ac20..7aba54eef899 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenuActionButton.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenuActionButton.kt
@@ -39,20 +39,18 @@ class HandleMenuActionButton @JvmOverloads constructor(
defStyleAttr: Int = 0
) : LinearLayout(context, attrs, defStyleAttr) {
- private val rootElement: LinearLayout
- private val iconView: ImageView
- private val textView: MarqueedTextView
+ val iconView: ImageView
+ val textView: MarqueedTextView
init {
- val view = LayoutInflater.from(context).inflate(
+ LayoutInflater.from(context).inflate(
R.layout.desktop_mode_window_decor_handle_menu_action_button, this, true)
- rootElement = findViewById(R.id.action_button)
iconView = findViewById(R.id.image)
textView = findViewById(R.id.label)
context.withStyledAttributes(attrs, R.styleable.HandleMenuActionButton) {
+ contentDescription = getString(R.styleable.HandleMenuActionButton_android_text)
textView.text = getString(R.styleable.HandleMenuActionButton_android_text)
- rootElement.contentDescription = getString(R.styleable.HandleMenuActionButton_android_text)
textView.setTextColor(getColor(R.styleable.HandleMenuActionButton_android_textColor, 0))
iconView.setImageResource(getResourceId(
R.styleable.HandleMenuActionButton_android_src, 0))
@@ -62,15 +60,6 @@ class HandleMenuActionButton @JvmOverloads constructor(
}
/**
- * Sets a listener to be invoked when this view is clicked.
- *
- * @param l the [OnClickListener] that receives click events.
- */
- override fun setOnClickListener(l: OnClickListener?) {
- rootElement.setOnClickListener(l)
- }
-
- /**
* Sets the text color for the text inside the button.
*
* @param color the color to set for the text, as a color integer.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MaximizeMenu.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MaximizeMenu.kt
index 5d1a7a0cc3a2..a10826fca3e2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MaximizeMenu.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MaximizeMenu.kt
@@ -60,14 +60,16 @@ import android.window.TaskConstants
import androidx.compose.material3.ColorScheme
import androidx.compose.ui.graphics.toArgb
import androidx.core.animation.addListener
-import androidx.core.view.ViewCompat
-import androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat
import androidx.core.view.isGone
import androidx.core.view.isVisible
import com.android.wm.shell.R
import com.android.wm.shell.RootTaskDisplayAreaOrganizer
import com.android.wm.shell.common.DisplayController
import com.android.wm.shell.common.SyncTransactionQueue
+import com.android.wm.shell.desktopmode.DesktopModeUiEventLogger
+import com.android.wm.shell.desktopmode.DesktopModeUiEventLogger.DesktopUiEventEnum.A11Y_MAXIMIZE_MENU_MAXIMIZE
+import com.android.wm.shell.desktopmode.DesktopModeUiEventLogger.DesktopUiEventEnum.A11Y_MAXIMIZE_MENU_RESIZE_LEFT
+import com.android.wm.shell.desktopmode.DesktopModeUiEventLogger.DesktopUiEventEnum.A11Y_MAXIMIZE_MENU_RESIZE_RIGHT
import com.android.wm.shell.desktopmode.isTaskMaximized
import com.android.wm.shell.shared.animation.Interpolators.EMPHASIZED_DECELERATE
import com.android.wm.shell.shared.animation.Interpolators.FAST_OUT_LINEAR_IN
@@ -85,13 +87,14 @@ import java.util.function.Supplier
* to the right or left half of the screen.
*/
class MaximizeMenu(
- private val syncQueue: SyncTransactionQueue,
- private val rootTdaOrganizer: RootTaskDisplayAreaOrganizer,
- private val displayController: DisplayController,
- private val taskInfo: RunningTaskInfo,
- private val decorWindowContext: Context,
- private val positionSupplier: (Int, Int) -> Point,
- private val transactionSupplier: Supplier<Transaction> = Supplier { Transaction() }
+ private val syncQueue: SyncTransactionQueue,
+ private val rootTdaOrganizer: RootTaskDisplayAreaOrganizer,
+ private val displayController: DisplayController,
+ private val taskInfo: RunningTaskInfo,
+ private val decorWindowContext: Context,
+ private val positionSupplier: (Int, Int) -> Point,
+ private val transactionSupplier: Supplier<Transaction> = Supplier { Transaction() },
+ private val desktopModeUiEventLogger: DesktopModeUiEventLogger
) {
private var maximizeMenu: AdditionalViewHostViewContainer? = null
private var maximizeMenuView: MaximizeMenuView? = null
@@ -132,7 +135,7 @@ class MaximizeMenu(
onLeftSnapClickListener = onLeftSnapClickListener,
onRightSnapClickListener = onRightSnapClickListener,
onHoverListener = onHoverListener,
- onOutsideTouchListener = onOutsideTouchListener
+ onOutsideTouchListener = onOutsideTouchListener,
)
maximizeMenuView?.let { view ->
view.animateOpenMenu(onEnd = {
@@ -167,7 +170,7 @@ class MaximizeMenu(
onLeftSnapClickListener: () -> Unit,
onRightSnapClickListener: () -> Unit,
onHoverListener: (Boolean) -> Unit,
- onOutsideTouchListener: () -> Unit
+ onOutsideTouchListener: () -> Unit,
) {
val t = transactionSupplier.get()
val builder = SurfaceControl.Builder()
@@ -186,6 +189,7 @@ class MaximizeMenu(
"MaximizeMenu")
maximizeMenuView = MaximizeMenuView(
context = decorWindowContext,
+ desktopModeUiEventLogger = desktopModeUiEventLogger,
sizeToggleDirection = getSizeToggleDirection(),
immersiveConfig = if (showImmersiveOption) {
MaximizeMenuView.ImmersiveConfig.Visible(
@@ -265,6 +269,7 @@ class MaximizeMenu(
*/
class MaximizeMenuView(
context: Context,
+ private val desktopModeUiEventLogger: DesktopModeUiEventLogger,
private val sizeToggleDirection: SizeToggleDirection,
immersiveConfig: ImmersiveConfig,
showSnapOptions: Boolean,
@@ -425,7 +430,10 @@ class MaximizeMenu(
) {
super.onInitializeAccessibilityNodeInfo(host, info)
- info.addAction(AccessibilityAction.ACTION_CLICK)
+ info.addAction(AccessibilityAction(
+ AccessibilityAction.ACTION_CLICK.id,
+ context.getString(R.string.maximize_menu_talkback_action_maximize_restore_text)
+ ))
host.isClickable = true
}
@@ -435,6 +443,7 @@ class MaximizeMenu(
args: Bundle?
): Boolean {
if (action == AccessibilityAction.ACTION_CLICK.id) {
+ desktopModeUiEventLogger.log(taskInfo, A11Y_MAXIMIZE_MENU_MAXIMIZE)
onMaximizeClickListener?.invoke()
}
return super.performAccessibilityAction(host, action, args)
@@ -447,7 +456,10 @@ class MaximizeMenu(
info: AccessibilityNodeInfo
) {
super.onInitializeAccessibilityNodeInfo(host, info)
- info.addAction(AccessibilityAction.ACTION_CLICK)
+ info.addAction(AccessibilityAction(
+ AccessibilityAction.ACTION_CLICK.id,
+ context.getString(R.string.maximize_menu_talkback_action_snap_left_text)
+ ))
host.isClickable = true
}
@@ -457,6 +469,7 @@ class MaximizeMenu(
args: Bundle?
): Boolean {
if (action == AccessibilityAction.ACTION_CLICK.id) {
+ desktopModeUiEventLogger.log(taskInfo, A11Y_MAXIMIZE_MENU_RESIZE_LEFT)
onLeftSnapClickListener?.invoke()
}
return super.performAccessibilityAction(host, action, args)
@@ -469,7 +482,10 @@ class MaximizeMenu(
info: AccessibilityNodeInfo
) {
super.onInitializeAccessibilityNodeInfo(host, info)
- info.addAction(AccessibilityAction.ACTION_CLICK)
+ info.addAction(AccessibilityAction(
+ AccessibilityAction.ACTION_CLICK.id,
+ context.getString(R.string.maximize_menu_talkback_action_snap_right_text)
+ ))
host.isClickable = true
}
@@ -479,35 +495,13 @@ class MaximizeMenu(
args: Bundle?
): Boolean {
if (action == AccessibilityAction.ACTION_CLICK.id) {
+ desktopModeUiEventLogger.log(taskInfo, A11Y_MAXIMIZE_MENU_RESIZE_RIGHT)
onRightSnapClickListener?.invoke()
}
return super.performAccessibilityAction(host, action, args)
}
}
- with(context.resources) {
- ViewCompat.replaceAccessibilityAction(
- snapLeftButton,
- AccessibilityActionCompat.ACTION_CLICK,
- getString(R.string.maximize_menu_talkback_action_snap_left_text),
- null
- )
-
- ViewCompat.replaceAccessibilityAction(
- snapRightButton,
- AccessibilityActionCompat.ACTION_CLICK,
- getString(R.string.maximize_menu_talkback_action_snap_right_text),
- null
- )
-
- ViewCompat.replaceAccessibilityAction(
- sizeToggleButton,
- AccessibilityActionCompat.ACTION_CLICK,
- getString(R.string.maximize_menu_talkback_action_maximize_restore_text),
- null
- )
- }
-
// Maximize/restore button.
val sizeToggleBtnTextId = if (sizeToggleDirection == SizeToggleDirection.RESTORE)
R.string.desktop_mode_maximize_menu_restore_button_text
@@ -792,15 +786,15 @@ class MaximizeMenu(
}
/** Measure width of the root view of this menu. */
- fun measureWidth() : Int {
- rootView.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
- return rootView.getMeasuredWidth()
+ fun measureWidth(): Int {
+ rootView.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED)
+ return rootView.measuredWidth
}
/** Measure height of the root view of this menu. */
- fun measureHeight() : Int {
- rootView.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
- return rootView.getMeasuredHeight()
+ fun measureHeight(): Int {
+ rootView.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED)
+ return rootView.measuredHeight
}
private fun deactivateSnapOptions() {
@@ -1048,7 +1042,8 @@ interface MaximizeMenuFactory {
taskInfo: RunningTaskInfo,
decorWindowContext: Context,
positionSupplier: (Int, Int) -> Point,
- transactionSupplier: Supplier<Transaction>
+ transactionSupplier: Supplier<Transaction>,
+ desktopModeUiEventLogger: DesktopModeUiEventLogger,
): MaximizeMenu
}
@@ -1061,7 +1056,8 @@ object DefaultMaximizeMenuFactory : MaximizeMenuFactory {
taskInfo: RunningTaskInfo,
decorWindowContext: Context,
positionSupplier: (Int, Int) -> Point,
- transactionSupplier: Supplier<Transaction>
+ transactionSupplier: Supplier<Transaction>,
+ desktopModeUiEventLogger: DesktopModeUiEventLogger,
): MaximizeMenu {
return MaximizeMenu(
syncQueue,
@@ -1070,7 +1066,8 @@ object DefaultMaximizeMenuFactory : MaximizeMenuFactory {
taskInfo,
decorWindowContext,
positionSupplier,
- transactionSupplier
+ transactionSupplier,
+ desktopModeUiEventLogger
)
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MultiDisplayVeiledResizeTaskPositioner.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MultiDisplayVeiledResizeTaskPositioner.kt
index eb324f74ca82..238242792782 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MultiDisplayVeiledResizeTaskPositioner.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MultiDisplayVeiledResizeTaskPositioner.kt
@@ -278,13 +278,16 @@ class MultiDisplayVeiledResizeTaskPositioner(
currentDisplayLayout,
)
)
-
- multiDisplayDragMoveIndicatorController.onDragEnd(
- desktopWindowDecoration.mTaskInfo.taskId,
- transactionSupplier,
- )
}
+ // Call the MultiDisplayDragMoveIndicatorController to clear any active indicator
+ // surfaces. This is necessary even if the drag ended on the same display, as surfaces
+ // may have been created for other displays during the drag.
+ multiDisplayDragMoveIndicatorController.onDragEnd(
+ desktopWindowDecoration.mTaskInfo.taskId,
+ transactionSupplier,
+ )
+
interactionJankMonitor.end(Cuj.CUJ_DESKTOP_MODE_DRAG_WINDOW)
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
index 91a899c09407..6a9b366dfb97 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
@@ -47,6 +47,8 @@ import android.view.SurfaceControlViewHost;
import android.view.View;
import android.view.WindowManager;
import android.view.WindowlessWindowManager;
+import android.window.DesktopExperienceFlags;
+import android.window.DesktopModeFlags;
import android.window.SurfaceSyncGroup;
import android.window.TaskConstants;
import android.window.WindowContainerToken;
@@ -286,6 +288,14 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer>
outResult.mCaptionX = (outResult.mWidth - outResult.mCaptionWidth) / 2;
outResult.mCaptionY = 0;
outResult.mCaptionTopPadding = params.mCaptionTopPadding;
+ if (DesktopExperienceFlags.ENABLE_DYNAMIC_RADIUS_COMPUTATION_BUGFIX.isTrue()) {
+ outResult.mCornerRadius = params.mCornerRadiusId == Resources.ID_NULL
+ ? INVALID_CORNER_RADIUS : loadDimensionPixelSize(resources,
+ params.mCornerRadiusId);
+ outResult.mShadowRadius = params.mShadowRadiusId == Resources.ID_NULL
+ ? INVALID_SHADOW_RADIUS : loadDimensionPixelSize(resources,
+ params.mShadowRadiusId);
+ }
Trace.beginSection("relayout-createViewHostIfNeeded");
createViewHostIfNeeded(mDecorWindowContext, mDisplay);
@@ -497,9 +507,16 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer>
.setPosition(mTaskSurface, taskPosition.x, taskPosition.y);
}
- if (params.mShadowRadius != INVALID_SHADOW_RADIUS) {
- startT.setShadowRadius(mTaskSurface, params.mShadowRadius);
- finishT.setShadowRadius(mTaskSurface, params.mShadowRadius);
+ if (DesktopExperienceFlags.ENABLE_DYNAMIC_RADIUS_COMPUTATION_BUGFIX.isTrue()) {
+ if (outResult.mShadowRadius != INVALID_SHADOW_RADIUS) {
+ startT.setShadowRadius(mTaskSurface, outResult.mShadowRadius);
+ finishT.setShadowRadius(mTaskSurface, outResult.mShadowRadius);
+ }
+ } else {
+ if (params.mShadowRadius != INVALID_SHADOW_RADIUS) {
+ startT.setShadowRadius(mTaskSurface, params.mShadowRadius);
+ finishT.setShadowRadius(mTaskSurface, params.mShadowRadius);
+ }
}
if (params.mSetTaskVisibilityPositionAndCrop) {
@@ -517,9 +534,16 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer>
startT.unsetColor(mTaskSurface);
}
- if (params.mCornerRadius != INVALID_CORNER_RADIUS) {
- startT.setCornerRadius(mTaskSurface, params.mCornerRadius);
- finishT.setCornerRadius(mTaskSurface, params.mCornerRadius);
+ if (DesktopExperienceFlags.ENABLE_DYNAMIC_RADIUS_COMPUTATION_BUGFIX.isTrue()) {
+ if (outResult.mCornerRadius != INVALID_CORNER_RADIUS) {
+ startT.setCornerRadius(mTaskSurface, outResult.mCornerRadius);
+ finishT.setCornerRadius(mTaskSurface, outResult.mCornerRadius);
+ }
+ } else {
+ if (params.mCornerRadius != INVALID_CORNER_RADIUS) {
+ startT.setCornerRadius(mTaskSurface, params.mCornerRadius);
+ finishT.setCornerRadius(mTaskSurface, params.mCornerRadius);
+ }
}
}
@@ -629,7 +653,9 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer>
*/
private void updateCaptionVisibility(View rootView, @NonNull RelayoutParams params) {
mIsCaptionVisible = params.mIsCaptionVisible;
- setCaptionVisibility(rootView, mIsCaptionVisible);
+ if (!DesktopModeFlags.ENABLE_DESKTOP_APP_HANDLE_ANIMATION.isTrue()) {
+ setCaptionVisibility(rootView, mIsCaptionVisible);
+ }
}
void setTaskDragResizer(TaskDragResizer taskDragResizer) {
@@ -824,9 +850,14 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer>
@InsetsSource.Flags int mInsetSourceFlags;
final Region mDisplayExclusionRegion = Region.obtain();
+ @Deprecated
int mShadowRadius = INVALID_SHADOW_RADIUS;
+ @Deprecated
int mCornerRadius = INVALID_CORNER_RADIUS;
+ int mShadowRadiusId = Resources.ID_NULL;
+ int mCornerRadiusId = Resources.ID_NULL;
+
int mCaptionTopPadding;
boolean mIsCaptionVisible;
@@ -849,9 +880,13 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer>
mIsInsetSource = true;
mInsetSourceFlags = 0;
mDisplayExclusionRegion.setEmpty();
-
- mShadowRadius = INVALID_SHADOW_RADIUS;
- mCornerRadius = INVALID_SHADOW_RADIUS;
+ if (DesktopExperienceFlags.ENABLE_DYNAMIC_RADIUS_COMPUTATION_BUGFIX.isTrue()) {
+ mShadowRadiusId = Resources.ID_NULL;
+ mCornerRadiusId = Resources.ID_NULL;
+ } else {
+ mShadowRadius = INVALID_SHADOW_RADIUS;
+ mCornerRadius = INVALID_SHADOW_RADIUS;
+ }
mCaptionTopPadding = 0;
mIsCaptionVisible = false;
@@ -893,6 +928,8 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer>
int mWidth;
int mHeight;
T mRootView;
+ int mCornerRadius;
+ int mShadowRadius;
void reset() {
mWidth = 0;
@@ -904,6 +941,10 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer>
mCaptionTopPadding = 0;
mCustomizableCaptionRegion.setEmpty();
mRootView = null;
+ if (DesktopExperienceFlags.ENABLE_DYNAMIC_RADIUS_COMPUTATION_BUGFIX.isTrue()) {
+ mCornerRadius = INVALID_CORNER_RADIUS;
+ mShadowRadius = INVALID_SHADOW_RADIUS;
+ }
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/ThemeUtils.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/ThemeUtils.kt
index c5057aa3cc18..f09f22fb1951 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/ThemeUtils.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/common/ThemeUtils.kt
@@ -92,4 +92,7 @@ internal class DecorThemeUtil(private val context: Context) {
Theme.LIGHT -> lightColors
Theme.DARK -> darkColors
}
+
+ fun getColorScheme(isDarkMode: Boolean): ColorScheme =
+ if (isDarkMode) darkColors else lightColors
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingDividerWindowManager.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingDividerWindowManager.kt
index 57f8046065b4..ab6b72947586 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingDividerWindowManager.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingDividerWindowManager.kt
@@ -198,6 +198,11 @@ class DesktopTilingDividerWindowManager(
tilingDividerView?.onUiModeChange(isDarkMode)
}
+ /** Notifies the divider view of task info change and possible color change. */
+ fun onTaskInfoChange() {
+ tilingDividerView?.onTaskInfoChange()
+ }
+
/** Hides the divider bar. */
fun hideDividerBar() {
if (!dividerShown) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingWindowDecoration.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingWindowDecoration.kt
index 7c5f34f979cd..3e92d7d994a6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingWindowDecoration.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingWindowDecoration.kt
@@ -137,6 +137,7 @@ class DesktopTilingWindowDecoration(
// Observe drag resizing to break tiling if a task is drag resized.
desktopModeWindowDecoration.addDragResizeListener(this)
val callback = { initTilingForDisplayIfNeeded(taskInfo.configuration, isFirstTiledApp) }
+ updateDesktopRepository(taskInfo.taskId, snapPosition = position)
if (isTiled) {
val wct = WindowContainerTransaction().setBounds(taskInfo.token, destinationBounds)
toggleResizeDesktopTaskTransitionHandler.startTransition(wct, currentBounds, callback)
@@ -159,6 +160,14 @@ class DesktopTilingWindowDecoration(
return isTiled
}
+ private fun updateDesktopRepository(taskId: Int, snapPosition: SnapPosition) {
+ when (snapPosition) {
+ SnapPosition.LEFT -> desktopUserRepositories.current.addLeftTiledTask(displayId, taskId)
+ SnapPosition.RIGHT ->
+ desktopUserRepositories.current.addRightTiledTask(displayId, taskId)
+ }
+ }
+
// If a task is already tiled on the same position, release this task, otherwise if the same
// task is tiled on the opposite side, remove it from the opposite side so it's tiled correctly.
private fun initTilingApps(
@@ -366,6 +375,7 @@ class DesktopTilingWindowDecoration(
fun onTaskInfoChange(taskInfo: RunningTaskInfo) {
val isCurrentTaskInDarkMode = isTaskInDarkMode(taskInfo)
+ desktopTilingDividerWindowManager?.onTaskInfoChange()
if (isCurrentTaskInDarkMode == isDarkMode || !isTilingManagerInitialised) return
isDarkMode = isCurrentTaskInDarkMode
desktopTilingDividerWindowManager?.onUiModeChange(isDarkMode)
@@ -423,15 +433,38 @@ class DesktopTilingWindowDecoration(
startTransaction: Transaction,
finishTransaction: Transaction,
) {
+ var leftTaskBroughtToFront = false
+ var rightTaskBroughtToFront = false
+
for (change in info.changes) {
change.taskInfo?.let {
if (it.isFullscreen || isMinimized(change.mode, info.type)) {
removeTaskIfTiled(it.taskId, /* taskVanished= */ false, it.isFullscreen)
} else if (isEnteringPip(change, info.type)) {
removeTaskIfTiled(it.taskId, /* taskVanished= */ true, it.isFullscreen)
+ } else if (isTransitionToFront(change.mode, info.type)) {
+ handleTaskBroughtToFront(it.taskId)
+ leftTaskBroughtToFront =
+ leftTaskBroughtToFront ||
+ it.taskId == leftTaskResizingHelper?.taskInfo?.taskId
+ rightTaskBroughtToFront =
+ rightTaskBroughtToFront ||
+ it.taskId == rightTaskResizingHelper?.taskInfo?.taskId
}
}
}
+
+ if (leftTaskBroughtToFront && rightTaskBroughtToFront) {
+ desktopTilingDividerWindowManager?.showDividerBar()
+ }
+ }
+
+ private fun handleTaskBroughtToFront(taskId: Int) {
+ if (taskId == leftTaskResizingHelper?.taskInfo?.taskId) {
+ leftTaskResizingHelper?.onAppBecomingVisible()
+ } else if (taskId == rightTaskResizingHelper?.taskInfo?.taskId) {
+ rightTaskResizingHelper?.onAppBecomingVisible()
+ }
}
private fun isMinimized(changeMode: Int, infoType: Int): Boolean {
@@ -462,6 +495,9 @@ class DesktopTilingWindowDecoration(
return false
}
+ private fun isTransitionToFront(changeMode: Int, transitionType: Int): Boolean =
+ changeMode == TRANSIT_TO_FRONT && transitionType == TRANSIT_TO_FRONT
+
class AppResizingHelper(
val taskInfo: RunningTaskInfo,
val desktopModeWindowDecoration: DesktopModeWindowDecoration,
@@ -475,6 +511,7 @@ class DesktopTilingWindowDecoration(
) {
var isInitialised = false
var newBounds = Rect(bounds)
+ var visibilityCallback: (() -> Unit)? = null
private lateinit var resizeVeil: ResizeVeil
private val displayContext = displayController.getDisplayContext(taskInfo.displayId)
private val userContext =
@@ -512,6 +549,11 @@ class DesktopTilingWindowDecoration(
fun updateVeil(t: Transaction) = resizeVeil.updateTransactionWithResizeVeil(t, newBounds)
+ fun onAppBecomingVisible() {
+ visibilityCallback?.invoke()
+ visibilityCallback = null
+ }
+
fun hideVeil() = resizeVeil.hideVeil()
private fun createIconFactory(context: Context, dimensions: Int): BaseIconFactory {
@@ -580,27 +622,40 @@ class DesktopTilingWindowDecoration(
) {
val taskRepository = desktopUserRepositories.current
if (taskId == leftTaskResizingHelper?.taskInfo?.taskId) {
+ desktopUserRepositories.current.removeLeftTiledTask(displayId)
removeTask(leftTaskResizingHelper, taskVanished, shouldDelayUpdate)
leftTaskResizingHelper = null
val taskId = rightTaskResizingHelper?.taskInfo?.taskId
- if (taskId != null && taskRepository.isVisibleTask(taskId)) {
+ val callback: (() -> Unit)? = {
rightTaskResizingHelper
?.desktopModeWindowDecoration
?.updateDisabledResizingEdge(NONE, shouldDelayUpdate)
}
+ if (taskId != null && taskRepository.isVisibleTask(taskId)) {
+ callback?.invoke()
+ } else if (rightTaskResizingHelper != null) {
+ rightTaskResizingHelper?.visibilityCallback = callback
+ }
tearDownTiling()
return
}
if (taskId == rightTaskResizingHelper?.taskInfo?.taskId) {
+ desktopUserRepositories.current.removeRightTiledTask(displayId)
removeTask(rightTaskResizingHelper, taskVanished, shouldDelayUpdate)
rightTaskResizingHelper = null
val taskId = leftTaskResizingHelper?.taskInfo?.taskId
- if (taskId != null && taskRepository.isVisibleTask(taskId)) {
+ val callback: (() -> Unit)? = {
leftTaskResizingHelper
?.desktopModeWindowDecoration
?.updateDisabledResizingEdge(NONE, shouldDelayUpdate)
}
+ if (taskId != null && taskRepository.isVisibleTask(taskId)) {
+ callback?.invoke()
+ } else if (leftTaskResizingHelper != null) {
+ leftTaskResizingHelper?.visibilityCallback = callback
+ }
+
tearDownTiling()
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/TilingDividerView.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/TilingDividerView.kt
index 54dcd2d082dc..e9bd6f8ef85c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/TilingDividerView.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/TilingDividerView.kt
@@ -29,6 +29,7 @@ import android.view.RoundedCorner
import android.view.View
import android.view.ViewConfiguration
import android.widget.FrameLayout
+import androidx.compose.ui.graphics.toArgb
import com.android.internal.annotations.VisibleForTesting
import com.android.internal.config.sysui.SystemUiDeviceConfigFlags
import com.android.wm.shell.R
@@ -36,6 +37,7 @@ import com.android.wm.shell.common.split.DividerHandleView
import com.android.wm.shell.common.split.DividerRoundedCorner
import com.android.wm.shell.shared.animation.Interpolators
import com.android.wm.shell.windowdecor.DragDetector
+import com.android.wm.shell.windowdecor.common.DecorThemeUtil
/** Divider for tiling split screen, currently mostly a copy of [DividerView]. */
class TilingDividerView : FrameLayout, View.OnTouchListener, DragDetector.MotionEventHandler {
@@ -56,6 +58,9 @@ class TilingDividerView : FrameLayout, View.OnTouchListener, DragDetector.Motion
@VisibleForTesting var handleY: IntRange = 0..0
private var canResize = false
private var resized = false
+ private var isDarkMode = false
+ private var decorThemeUtil = DecorThemeUtil(context)
+
/**
* Tracks divider bar visible bounds in screen-based coordination. Used to calculate with
* insets.
@@ -90,10 +95,12 @@ class TilingDividerView : FrameLayout, View.OnTouchListener, DragDetector.Motion
) {
callback = dividerMoveCallback
this.dividerBounds.set(dividerBounds)
+ this.isDarkMode = isDarkMode
+ paint.color = decorThemeUtil.getColorScheme(isDarkMode).outlineVariant.toArgb()
handle.setIsLeftRightSplit(true)
handle.setup(/* isSplitScreen= */ false, isDarkMode)
corners.setIsLeftRightSplit(true)
- corners.setup(/* isSplitScreen= */ false, isDarkMode)
+ corners.setup(/* isSplitScreen= */ false, paint.color)
handleRegionHeight = handleRegionSize.height
handleRegionWidth = handleRegionSize.width
cornersRadius =
@@ -108,17 +115,22 @@ class TilingDividerView : FrameLayout, View.OnTouchListener, DragDetector.Motion
}
fun onUiModeChange(isDarkMode: Boolean) {
+ this.isDarkMode = isDarkMode
handle.onUiModeChange(isDarkMode)
- corners.onUiModeChange(isDarkMode)
- paint.color =
- if (isDarkMode) {
- resources.getColor(R.color.tiling_divider_background_dark, null /* theme */)
- } else {
- resources.getColor(R.color.tiling_divider_background_light, null /* theme */)
- }
+ paint.color = decorThemeUtil.getColorScheme(isDarkMode).outlineVariant.toArgb()
+ corners.onUiModeChange(paint.color)
invalidate()
}
+ fun onTaskInfoChange() {
+ decorThemeUtil = DecorThemeUtil(context)
+ if (paint.color != decorThemeUtil.getColorScheme(isDarkMode).outlineVariant.toArgb()) {
+ paint.color = decorThemeUtil.getColorScheme(isDarkMode).outlineVariant.toArgb()
+ corners.onCornerColorChange(paint.color)
+ invalidate()
+ }
+ }
+
override fun onFinishInflate() {
super.onFinishInflate()
dividerBar = requireViewById(R.id.divider_bar)
@@ -128,15 +140,10 @@ class TilingDividerView : FrameLayout, View.OnTouchListener, DragDetector.Motion
resources.getDimensionPixelSize(R.dimen.docked_stack_divider_lift_elevation)
setOnTouchListener(this)
setWillNotDraw(false)
- paint.color =
- if (
- context.resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK ==
- Configuration.UI_MODE_NIGHT_YES
- ) {
- resources.getColor(R.color.tiling_divider_background_dark, /* theme= */null)
- } else {
- resources.getColor(R.color.tiling_divider_background_light, /* theme= */ null)
- }
+ val isDarkMode =
+ context.resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK ==
+ Configuration.UI_MODE_NIGHT_YES
+ paint.color = decorThemeUtil.getColorScheme(isDarkMode).outlineVariant.toArgb()
paint.isAntiAlias = true
paint.style = Paint.Style.FILL
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHandleAnimator.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHandleAnimator.kt
new file mode 100644
index 000000000000..f0a85306d177
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHandleAnimator.kt
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2025 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.wm.shell.windowdecor
+
+import android.animation.ObjectAnimator
+import android.view.View
+import android.view.View.Visibility
+import android.view.animation.PathInterpolator
+import android.widget.ImageButton
+import androidx.core.animation.doOnEnd
+import com.android.wm.shell.shared.animation.Interpolators
+
+/**
+ * Animates the Desktop View's app handle.
+ */
+class AppHandleAnimator(
+ private val appHandleView: View,
+ private val captionHandle: ImageButton,
+) {
+ companion object {
+ // Constants for animating the whole caption
+ private const val APP_HANDLE_ALPHA_FADE_IN_ANIMATION_DURATION_MS: Long = 275L
+ private const val APP_HANDLE_ALPHA_FADE_OUT_ANIMATION_DURATION_MS: Long = 340
+ private val APP_HANDLE_ANIMATION_INTERPOLATOR = PathInterpolator(
+ 0.4f,
+ 0f,
+ 0.2f,
+ 1f
+ )
+
+ // Constants for animating the caption's handle
+ private const val HANDLE_ANIMATION_DURATION: Long = 100
+ private val HANDLE_ANIMATION_INTERPOLATOR = Interpolators.FAST_OUT_SLOW_IN
+ }
+
+ private var animator: ObjectAnimator? = null
+
+ /** Animates the given caption view to the given visibility after a visibility change. */
+ fun animateVisibilityChange(@Visibility visible: Int) {
+ when (visible) {
+ View.VISIBLE -> animateShowAppHandle()
+ else -> animateHideAppHandle()
+ }
+ }
+
+ /** Animate appearance/disappearance of caption's handle. */
+ fun animateCaptionHandleAlpha(startValue: Float, endValue: Float) {
+ cancel()
+ animator = ObjectAnimator.ofFloat(captionHandle, View.ALPHA, startValue, endValue).apply {
+ duration = HANDLE_ANIMATION_DURATION
+ interpolator = HANDLE_ANIMATION_INTERPOLATOR
+ start()
+ }
+ }
+
+ private fun animateShowAppHandle() {
+ cancel()
+ appHandleView.alpha = 0f
+ appHandleView.visibility = View.VISIBLE
+ animator = ObjectAnimator.ofFloat(appHandleView, View.ALPHA, 1f).apply {
+ duration = APP_HANDLE_ALPHA_FADE_IN_ANIMATION_DURATION_MS
+ interpolator = APP_HANDLE_ANIMATION_INTERPOLATOR
+ start()
+ }
+ }
+
+ private fun animateHideAppHandle() {
+ cancel()
+ animator = ObjectAnimator.ofFloat(appHandleView, View.ALPHA, 0f).apply {
+ duration = APP_HANDLE_ALPHA_FADE_OUT_ANIMATION_DURATION_MS
+ interpolator = APP_HANDLE_ANIMATION_INTERPOLATOR
+ doOnEnd {
+ appHandleView.visibility = View.GONE
+ }
+ start()
+ }
+ }
+
+ /**
+ * Cancels any active animations.
+ */
+ fun cancel() {
+ animator?.removeAllListeners()
+ animator?.cancel()
+ animator = null
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHandleViewHolder.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHandleViewHolder.kt
index 0985587a330e..f09c500dcdd0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHandleViewHolder.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHandleViewHolder.kt
@@ -15,7 +15,6 @@
*/
package com.android.wm.shell.windowdecor.viewholder
-import android.animation.ObjectAnimator
import android.app.ActivityManager.RunningTaskInfo
import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM
import android.content.res.ColorStateList
@@ -40,8 +39,10 @@ import androidx.core.view.accessibility.AccessibilityNodeInfoCompat.Accessibilit
import com.android.internal.policy.SystemBarUtils
import com.android.window.flags.Flags
import com.android.wm.shell.R
-import com.android.wm.shell.shared.animation.Interpolators
+import com.android.wm.shell.desktopmode.DesktopModeUiEventLogger
+import com.android.wm.shell.desktopmode.DesktopModeUiEventLogger.DesktopUiEventEnum.A11Y_APP_HANDLE_MENU_OPENED
import com.android.wm.shell.shared.bubbles.BubbleAnythingFlagHelper
+import com.android.wm.shell.windowdecor.AppHandleAnimator
import com.android.wm.shell.windowdecor.WindowManagerWrapper
import com.android.wm.shell.windowdecor.additionalviewcontainer.AdditionalSystemViewContainer
@@ -54,25 +55,24 @@ class AppHandleViewHolder(
onCaptionTouchListener: View.OnTouchListener,
onCaptionButtonClickListener: OnClickListener,
private val windowManagerWrapper: WindowManagerWrapper,
- private val handler: Handler
+ private val handler: Handler,
+ private val desktopModeUiEventLogger: DesktopModeUiEventLogger,
) : WindowDecorationViewHolder<AppHandleViewHolder.HandleData>(rootView) {
- companion object {
- private const val CAPTION_HANDLE_ANIMATION_DURATION: Long = 100
- }
-
data class HandleData(
val taskInfo: RunningTaskInfo,
val position: Point,
val width: Int,
val height: Int,
- val showInputLayer: Boolean
+ val showInputLayer: Boolean,
+ val isCaptionVisible: Boolean,
) : Data()
private lateinit var taskInfo: RunningTaskInfo
private val captionView: View = rootView.requireViewById(R.id.desktop_mode_caption)
private val captionHandle: ImageButton = rootView.requireViewById(R.id.caption_handle)
private val inputManager = context.getSystemService(InputManager::class.java)
+ private val animator: AppHandleAnimator = AppHandleAnimator(rootView, captionHandle)
private var statusBarInputLayerExists = false
// An invisible View that takes up the same coordinates as captionHandle but is layered
@@ -101,7 +101,14 @@ class AppHandleViewHolder(
}
override fun bindData(data: HandleData) {
- bindData(data.taskInfo, data.position, data.width, data.height, data.showInputLayer)
+ bindData(
+ data.taskInfo,
+ data.position,
+ data.width,
+ data.height,
+ data.showInputLayer,
+ data.isCaptionVisible
+ )
}
private fun bindData(
@@ -109,8 +116,10 @@ class AppHandleViewHolder(
position: Point,
width: Int,
height: Int,
- showInputLayer: Boolean
+ showInputLayer: Boolean,
+ isCaptionVisible: Boolean
) {
+ setVisibility(isCaptionVisible)
captionHandle.imageTintList = ColorStateList.valueOf(getCaptionHandleBarColor(taskInfo))
this.taskInfo = taskInfo
// If handle is not in status bar region(i.e., bottom stage in vertical split),
@@ -131,11 +140,11 @@ class AppHandleViewHolder(
}
override fun onHandleMenuOpened() {
- animateCaptionHandleAlpha(startValue = 1f, endValue = 0f)
+ animator.animateCaptionHandleAlpha(startValue = 1f, endValue = 0f)
}
override fun onHandleMenuClosed() {
- animateCaptionHandleAlpha(startValue = 0f, endValue = 1f)
+ animator.animateCaptionHandleAlpha(startValue = 0f, endValue = 1f)
}
private fun createStatusBarInputLayer(handlePosition: Point,
@@ -195,6 +204,7 @@ class AppHandleViewHolder(
// Passthrough the a11y click action so the caption handle, so that app handle menu
// is opened on a11y click, similar to a real click
if (action == AccessibilityAction.ACTION_CLICK.id) {
+ desktopModeUiEventLogger.log(taskInfo, A11Y_APP_HANDLE_MENU_OPENED)
captionHandle.performClick()
}
return super.performAccessibilityAction(host, action, args)
@@ -210,10 +220,13 @@ class AppHandleViewHolder(
}
}
- // Update a11y action text so that Talkback announces "Press double tap to open app handle
- // menu" while focused on status bar input layer
+ // Update a11y action text so that Talkback announces "Press double tap to open menu"
+ // while focused on status bar input layer
ViewCompat.replaceAccessibilityAction(
- view, AccessibilityActionCompat.ACTION_CLICK, "Open app handle menu", null
+ view,
+ AccessibilityActionCompat.ACTION_CLICK,
+ context.getString(R.string.app_handle_chip_accessibility_announce),
+ null
)
}
@@ -239,6 +252,17 @@ class AppHandleViewHolder(
}
}
+ private fun setVisibility(visible: Boolean) {
+ val v = if (visible) View.VISIBLE else View.GONE
+ if (
+ captionView.visibility == v ||
+ !DesktopModeFlags.ENABLE_DESKTOP_APP_HANDLE_ANIMATION.isTrue()
+ ) {
+ return
+ }
+ animator.animateVisibilityChange(v)
+ }
+
private fun getCaptionHandleBarColor(taskInfo: RunningTaskInfo): Int {
return if (shouldUseLightCaptionColors(taskInfo)) {
context.getColor(R.color.desktop_mode_caption_handle_bar_light)
@@ -264,18 +288,10 @@ class AppHandleViewHolder(
} ?: false
}
- /** Animate appearance/disappearance of caption handle as the handle menu is animated. */
- private fun animateCaptionHandleAlpha(startValue: Float, endValue: Float) {
- val animator =
- ObjectAnimator.ofFloat(captionHandle, View.ALPHA, startValue, endValue).apply {
- duration = CAPTION_HANDLE_ANIMATION_DURATION
- interpolator = Interpolators.FAST_OUT_SLOW_IN
- }
- animator.start()
+ override fun close() {
+ animator.cancel()
}
- override fun close() {}
-
/** Factory class for creating [AppHandleViewHolder] objects. */
class Factory {
/**
@@ -288,12 +304,14 @@ class AppHandleViewHolder(
onCaptionButtonClickListener: OnClickListener,
windowManagerWrapper: WindowManagerWrapper,
handler: Handler,
+ desktopModeUiEventLogger: DesktopModeUiEventLogger,
): AppHandleViewHolder = AppHandleViewHolder(
rootView,
onCaptionTouchListener,
onCaptionButtonClickListener,
windowManagerWrapper,
handler,
+ desktopModeUiEventLogger,
)
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHeaderViewHolder.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHeaderViewHolder.kt
index 30712b55bdfa..d2e3a07f4651 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHeaderViewHolder.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHeaderViewHolder.kt
@@ -49,6 +49,13 @@ import com.android.internal.R.color.materialColorSurfaceContainerHigh
import com.android.internal.R.color.materialColorSurfaceContainerLow
import com.android.internal.R.color.materialColorSurfaceDim
import com.android.wm.shell.R
+import com.android.wm.shell.desktopmode.DesktopModeUiEventLogger
+import com.android.wm.shell.desktopmode.DesktopModeUiEventLogger.DesktopUiEventEnum.A11Y_ACTION_MAXIMIZE_RESTORE
+import com.android.wm.shell.desktopmode.DesktopModeUiEventLogger.DesktopUiEventEnum.A11Y_ACTION_RESIZE_LEFT
+import com.android.wm.shell.desktopmode.DesktopModeUiEventLogger.DesktopUiEventEnum.A11Y_ACTION_RESIZE_RIGHT
+import com.android.wm.shell.desktopmode.DesktopModeUiEventLogger.DesktopUiEventEnum.A11Y_APP_WINDOW_CLOSE_BUTTON
+import com.android.wm.shell.desktopmode.DesktopModeUiEventLogger.DesktopUiEventEnum.A11Y_APP_WINDOW_MAXIMIZE_RESTORE_BUTTON
+import com.android.wm.shell.desktopmode.DesktopModeUiEventLogger.DesktopUiEventEnum.A11Y_APP_WINDOW_MINIMIZE_BUTTON
import com.android.wm.shell.windowdecor.MaximizeButtonView
import com.android.wm.shell.windowdecor.common.DecorThemeUtil
import com.android.wm.shell.windowdecor.common.DrawableInsets
@@ -75,6 +82,7 @@ class AppHeaderViewHolder(
mOnRightSnapClickListener: () -> Unit,
mOnMaximizeOrRestoreClickListener: () -> Unit,
onMaximizeHoverAnimationFinishedListener: () -> Unit,
+ private val desktopModeUiEventLogger: DesktopModeUiEventLogger,
) : WindowDecorationViewHolder<AppHeaderViewHolder.HeaderData>(rootView) {
data class HeaderData(
@@ -83,6 +91,7 @@ class AppHeaderViewHolder(
val inFullImmersiveState: Boolean,
val hasGlobalFocus: Boolean,
val enableMaximizeLongClick: Boolean,
+ val isCaptionVisible: Boolean,
) : Data()
private val decorThemeUtil = DecorThemeUtil(context)
@@ -150,6 +159,8 @@ class AppHeaderViewHolder(
private lateinit var a11yTextMaximize: String
private lateinit var a11yTextRestore: String
+ private lateinit var currentTaskInfo: RunningTaskInfo
+
init {
captionView.setOnTouchListener(onCaptionTouchListener)
captionHandle.setOnTouchListener(onCaptionTouchListener)
@@ -196,9 +207,18 @@ class AppHeaderViewHolder(
args: Bundle?
): Boolean {
when (action) {
- R.id.action_snap_left -> mOnLeftSnapClickListener.invoke()
- R.id.action_snap_right -> mOnRightSnapClickListener.invoke()
- R.id.action_maximize_restore -> mOnMaximizeOrRestoreClickListener.invoke()
+ R.id.action_snap_left -> {
+ desktopModeUiEventLogger.log(currentTaskInfo, A11Y_ACTION_RESIZE_LEFT)
+ mOnLeftSnapClickListener.invoke()
+ }
+ R.id.action_snap_right -> {
+ desktopModeUiEventLogger.log(currentTaskInfo, A11Y_ACTION_RESIZE_RIGHT)
+ mOnRightSnapClickListener.invoke()
+ }
+ R.id.action_maximize_restore -> {
+ desktopModeUiEventLogger.log(currentTaskInfo, A11Y_ACTION_MAXIMIZE_RESTORE)
+ mOnMaximizeOrRestoreClickListener.invoke()
+ }
}
return super.performAccessibilityAction(host, action, args)
@@ -223,10 +243,56 @@ class AppHeaderViewHolder(
args: Bundle?
): Boolean {
when (action) {
- AccessibilityAction.ACTION_CLICK.id -> host.performClick()
- R.id.action_snap_left -> mOnLeftSnapClickListener.invoke()
- R.id.action_snap_right -> mOnRightSnapClickListener.invoke()
- R.id.action_maximize_restore -> mOnMaximizeOrRestoreClickListener.invoke()
+ AccessibilityAction.ACTION_CLICK.id -> {
+ desktopModeUiEventLogger.log(
+ currentTaskInfo, A11Y_APP_WINDOW_MAXIMIZE_RESTORE_BUTTON
+ )
+ host.performClick()
+ }
+ R.id.action_snap_left -> {
+ desktopModeUiEventLogger.log(currentTaskInfo, A11Y_ACTION_RESIZE_LEFT)
+ mOnLeftSnapClickListener.invoke()
+ }
+ R.id.action_snap_right -> {
+ desktopModeUiEventLogger.log(currentTaskInfo, A11Y_ACTION_RESIZE_RIGHT)
+ mOnRightSnapClickListener.invoke()
+ }
+ R.id.action_maximize_restore -> {
+ desktopModeUiEventLogger.log(currentTaskInfo, A11Y_ACTION_MAXIMIZE_RESTORE)
+ mOnMaximizeOrRestoreClickListener.invoke()
+ }
+ }
+
+ return super.performAccessibilityAction(host, action, args)
+ }
+ }
+
+ closeWindowButton.accessibilityDelegate = object : View.AccessibilityDelegate() {
+ override fun performAccessibilityAction(
+ host: View,
+ action: Int,
+ args: Bundle?
+ ): Boolean {
+ when (action) {
+ AccessibilityAction.ACTION_CLICK.id -> desktopModeUiEventLogger.log(
+ currentTaskInfo, A11Y_APP_WINDOW_CLOSE_BUTTON
+ )
+ }
+
+ return super.performAccessibilityAction(host, action, args)
+ }
+ }
+
+ minimizeWindowButton.accessibilityDelegate = object : View.AccessibilityDelegate() {
+ override fun performAccessibilityAction(
+ host: View,
+ action: Int,
+ args: Bundle?
+ ): Boolean {
+ when (action) {
+ AccessibilityAction.ACTION_CLICK.id -> desktopModeUiEventLogger.log(
+ currentTaskInfo, A11Y_APP_WINDOW_MINIMIZE_BUTTON
+ )
}
return super.performAccessibilityAction(host, action, args)
@@ -264,7 +330,8 @@ class AppHeaderViewHolder(
data.isTaskMaximized,
data.inFullImmersiveState,
data.hasGlobalFocus,
- data.enableMaximizeLongClick
+ data.enableMaximizeLongClick,
+ data.isCaptionVisible,
)
}
@@ -306,21 +373,31 @@ class AppHeaderViewHolder(
inFullImmersiveState: Boolean,
hasGlobalFocus: Boolean,
enableMaximizeLongClick: Boolean,
+ isCaptionVisible: Boolean,
) {
- if (DesktopModeFlags.ENABLE_THEMED_APP_HEADERS.isTrue()) {
+ currentTaskInfo = taskInfo
+ if (DesktopModeFlags.ENABLE_THEMED_APP_HEADERS.isTrue) {
bindDataWithThemedHeaders(
taskInfo,
isTaskMaximized,
inFullImmersiveState,
hasGlobalFocus,
enableMaximizeLongClick,
+ isCaptionVisible,
)
} else {
- bindDataLegacy(taskInfo, hasGlobalFocus)
+ bindDataLegacy(taskInfo, hasGlobalFocus, isCaptionVisible)
}
}
- private fun bindDataLegacy(taskInfo: RunningTaskInfo, hasGlobalFocus: Boolean) {
+ private fun bindDataLegacy(
+ taskInfo: RunningTaskInfo,
+ hasGlobalFocus: Boolean,
+ isCaptionVisible: Boolean,
+ ) {
+ if (DesktopModeFlags.ENABLE_DESKTOP_APP_HANDLE_ANIMATION.isTrue()) {
+ setCaptionVisibility(isCaptionVisible)
+ }
captionView.setBackgroundColor(getCaptionBackgroundColor(taskInfo, hasGlobalFocus))
val color = getAppNameAndButtonColor(taskInfo, hasGlobalFocus)
val alpha = Color.alpha(color)
@@ -350,7 +427,7 @@ class AppHeaderViewHolder(
minimizeWindowButton.background = getDrawable(1)
}
maximizeButtonView.setAnimationTints(isDarkMode())
- minimizeWindowButton.isGone = !DesktopModeFlags.ENABLE_MINIMIZE_BUTTON.isTrue()
+ minimizeWindowButton.isGone = !DesktopModeFlags.ENABLE_MINIMIZE_BUTTON.isTrue
}
private fun bindDataWithThemedHeaders(
@@ -359,10 +436,15 @@ class AppHeaderViewHolder(
inFullImmersiveState: Boolean,
hasGlobalFocus: Boolean,
enableMaximizeLongClick: Boolean,
+ isCaptionVisible: Boolean,
) {
val header = fillHeaderInfo(taskInfo, hasGlobalFocus)
val headerStyle = getHeaderStyle(header)
+ if (DesktopModeFlags.ENABLE_DESKTOP_APP_HANDLE_ANIMATION.isTrue()) {
+ setCaptionVisibility(isCaptionVisible)
+ }
+
// Caption Background
when (headerStyle.background) {
is HeaderStyle.Background.Opaque -> {
@@ -401,7 +483,7 @@ class AppHeaderViewHolder(
drawableInsets = minimizeDrawableInsets
)
}
- minimizeWindowButton.isGone = !DesktopModeFlags.ENABLE_MINIMIZE_BUTTON.isTrue()
+ minimizeWindowButton.isGone = !DesktopModeFlags.ENABLE_MINIMIZE_BUTTON.isTrue
// Maximize button.
maximizeButtonView.apply {
setAnimationTints(
@@ -464,6 +546,11 @@ class AppHeaderViewHolder(
}
}
+ private fun setCaptionVisibility(visible: Boolean) {
+ val v = if (visible) View.VISIBLE else View.GONE
+ captionView.visibility = v
+ }
+
override fun onHandleMenuOpened() {}
override fun onHandleMenuClosed() {}
@@ -740,6 +827,7 @@ class AppHeaderViewHolder(
mOnRightSnapClickListener: () -> Unit,
mOnMaximizeOrRestoreClickListener: () -> Unit,
onMaximizeHoverAnimationFinishedListener: () -> Unit,
+ desktopModeUiEventLogger: DesktopModeUiEventLogger
): AppHeaderViewHolder = AppHeaderViewHolder(
rootView,
onCaptionTouchListener,
@@ -750,6 +838,7 @@ class AppHeaderViewHolder(
mOnRightSnapClickListener,
mOnMaximizeOrRestoreClickListener,
onMaximizeHoverAnimationFinishedListener,
+ desktopModeUiEventLogger,
)
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/utils/CommonAssertions.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/utils/CommonAssertions.kt
index 509f4f202b6b..8e1cf167318e 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/utils/CommonAssertions.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/utils/CommonAssertions.kt
@@ -254,6 +254,16 @@ fun LayersTraceSubject.splitAppLayerBoundsSnapToDivider(
}
}
+/**
+ * Checks that surfaces are still within the expected region after snapping to a snap point.
+ *
+ * @param component The component we are checking (should be one of the two split apps)
+ * @param landscapePosLeft If [true], and device is in left/right split, app is on the left side of
+ * the screen. Has no meaning if device is in top/bottom split.
+ * @param portraitPosTop If [true], and device is in top/bottom split, app is on the top side of
+ * the screen. Has no meaning if device is in left/right split.
+ * @param rotation The rotation state of the display.
+ */
fun LayerTraceEntrySubject.splitAppLayerBoundsSnapToDivider(
component: IComponentMatcher,
landscapePosLeft: Boolean,
@@ -268,10 +278,12 @@ fun LayerTraceEntrySubject.splitAppLayerBoundsSnapToDivider(
visibleRegion(component).isNotEmpty()
visibleRegion(component)
.coversAtMost(
+ // TODO (b/403082705): Should use the new method for determining left/right split.
if (displayBounds.width() > displayBounds.height()) {
if (landscapePosLeft) {
Region(
- 0,
+ // TODO (b/403304310): Check if we're in an offscreen-enabled mode.
+ -displayBounds.right, // the receding app can go offscreen
0,
(dividerRegion.left + dividerRegion.right) / 2,
displayBounds.bottom
@@ -280,7 +292,7 @@ fun LayerTraceEntrySubject.splitAppLayerBoundsSnapToDivider(
Region(
(dividerRegion.left + dividerRegion.right) / 2,
0,
- displayBounds.right,
+ displayBounds.right * 2, // the receding app can go offscreen
displayBounds.bottom
)
}
@@ -288,7 +300,7 @@ fun LayerTraceEntrySubject.splitAppLayerBoundsSnapToDivider(
if (portraitPosTop) {
Region(
0,
- 0,
+ -displayBounds.bottom, // the receding app can go offscreen
displayBounds.right,
(dividerRegion.top + dividerRegion.bottom) / 2
)
@@ -297,7 +309,7 @@ fun LayerTraceEntrySubject.splitAppLayerBoundsSnapToDivider(
0,
(dividerRegion.top + dividerRegion.bottom) / 2,
displayBounds.right,
- displayBounds.bottom
+ displayBounds.bottom * 2 // the receding app can go offscreen
)
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/utils/SplitScreenUtils.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/utils/SplitScreenUtils.kt
index 49d6877a1654..e4183f16ba14 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/utils/SplitScreenUtils.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/utils/SplitScreenUtils.kt
@@ -310,12 +310,18 @@ object SplitScreenUtils {
}
}
+ /**
+ * Drags the divider, then releases, making it snap to a new snap point.
+ */
fun dragDividerToResizeAndWait(device: UiDevice, wmHelper: WindowManagerStateHelper) {
+ // Find the first display that is turned on (making the assumption that there is only one).
val displayBounds =
- wmHelper.currentState.layerState.displays.firstOrNull { !it.isVirtual }?.layerStackSpace
- ?: error("Display not found")
+ wmHelper.currentState.layerState.displays.firstOrNull { !it.isVirtual && it.isOn }
+ ?.layerStackSpace ?: error("Display not found")
val dividerBar = device.wait(Until.findObject(dividerBarSelector), TIMEOUT_MS)
- dividerBar.drag(Point(displayBounds.width() * 1 / 3, displayBounds.height() * 2 / 3), 200)
+ // Drag to a point on the lower left of the screen -- this will cause the divider to snap
+ // to the left- or bottom-side snap point, shrinking the "primary" test app.
+ dividerBar.drag(Point(displayBounds.width() * 1 / 4, displayBounds.height() * 3 / 4), 200)
wmHelper
.StateSyncBuilder()
diff --git a/libs/WindowManager/Shell/tests/unittest/res/values/dimen.xml b/libs/WindowManager/Shell/tests/unittest/res/values/dimen.xml
index aa1b24189274..33ea0baa4f6d 100644
--- a/libs/WindowManager/Shell/tests/unittest/res/values/dimen.xml
+++ b/libs/WindowManager/Shell/tests/unittest/res/values/dimen.xml
@@ -18,6 +18,8 @@
<!-- Resources used in WindowDecorationTests -->
<dimen name="test_freeform_decor_caption_height">32dp</dimen>
<dimen name="test_freeform_decor_caption_menu_width">216dp</dimen>
+ <dimen name="test_freeform_shadow_radius">20dp</dimen>
+ <dimen name="test_freeform_corner_radius">16dp</dimen>
<dimen name="test_window_decor_left_outset">10dp</dimen>
<dimen name="test_window_decor_top_outset">20dp</dimen>
<dimen name="test_window_decor_right_outset">30dp</dimen>
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackProgressAnimatorTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackProgressAnimatorTest.java
index 43bcc3b61124..2ef6c558b0b5 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackProgressAnimatorTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackProgressAnimatorTest.java
@@ -58,7 +58,8 @@ public class BackProgressAnimatorTest extends ShellTestCase {
/* frameTime = */ 0,
/* progress = */ progress,
/* triggerBack = */ false,
- /* swipeEdge = */ BackEvent.EDGE_LEFT);
+ /* swipeEdge = */ BackEvent.EDGE_LEFT,
+ /* departingAnimationTarget = */ null);
}
@Before
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/CustomCrossActivityBackAnimationTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/CustomCrossActivityBackAnimationTest.kt
index 9d4cc49a7a65..2cc52c5ab9ad 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/CustomCrossActivityBackAnimationTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/CustomCrossActivityBackAnimationTest.kt
@@ -224,7 +224,8 @@ class CustomCrossActivityBackAnimationTest : ShellTestCase() {
/* frameTime = */ 0,
/* progress = */ progress,
/* triggerBack = */ false,
- /* swipeEdge = */ BackEvent.EDGE_LEFT
+ /* swipeEdge = */ BackEvent.EDGE_LEFT,
+ /* departingAnimationTarget = */ null
)
private fun createAnimationTarget(open: Boolean): RemoteAnimationTarget {
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayControllerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayControllerTests.java
index 3d5e9495e29d..1e459d55ed77 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayControllerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayControllerTests.java
@@ -18,6 +18,7 @@ package com.android.wm.shell.common;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNotSame;
import static org.junit.Assert.assertNull;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
@@ -231,4 +232,24 @@ public class DisplayControllerTests extends ShellTestCase {
assertEquals(DISPLAY_ABS_BOUNDS_1,
mController.getDisplayLayout(DISPLAY_ID_1).globalBoundsDp());
}
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_CONNECTED_DISPLAYS_WINDOW_DRAG)
+ public void onDisplayConfigurationChanged_reInitDisplayLayout()
+ throws RemoteException {
+ ExtendedMockito.doReturn(true)
+ .when(() -> DesktopModeStatus.canEnterDesktopMode(any()));
+ mController.onInit();
+ mController.addDisplayWindowListener(mListener);
+
+ mCapturedTopologyListener.accept(mMockTopology);
+
+ DisplayLayout displayLayoutBefore = mController.getDisplayLayout(DISPLAY_ID_0);
+ mDisplayContainerListener.onDisplayConfigurationChanged(DISPLAY_ID_0, new Configuration());
+ DisplayLayout displayLayoutAfter = mController.getDisplayLayout(DISPLAY_ID_0);
+
+ assertNotSame(displayLayoutBefore, displayLayoutAfter);
+ assertEquals(DISPLAY_ABS_BOUNDS_0,
+ mController.getDisplayLayout(DISPLAY_ID_0).globalBoundsDp());
+ }
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/pip/PipDesktopStateTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/pip/PipDesktopStateTest.java
deleted file mode 100644
index 25dbc64f83de..000000000000
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/pip/PipDesktopStateTest.java
+++ /dev/null
@@ -1,184 +0,0 @@
-/*
- * Copyright (C) 2025 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.wm.shell.common.pip;
-
-import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
-import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
-
-import static com.android.window.flags.Flags.FLAG_ENABLE_CONNECTED_DISPLAYS_PIP;
-import static com.android.window.flags.Flags.FLAG_ENABLE_DESKTOP_WINDOWING_PIP;
-import static com.android.wm.shell.Flags.FLAG_ENABLE_PIP2;
-
-import static junit.framework.Assert.assertEquals;
-import static junit.framework.Assert.assertFalse;
-import static junit.framework.Assert.assertTrue;
-
-import static org.mockito.Mockito.when;
-
-import android.app.ActivityManager;
-import android.platform.test.annotations.EnableFlags;
-import android.testing.AndroidTestingRunner;
-import android.testing.TestableLooper;
-import android.window.DisplayAreaInfo;
-import android.window.WindowContainerToken;
-
-import androidx.test.filters.SmallTest;
-
-import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
-import com.android.wm.shell.desktopmode.DesktopRepository;
-import com.android.wm.shell.desktopmode.DesktopUserRepositories;
-import com.android.wm.shell.desktopmode.DragToDesktopTransitionHandler;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.Mockito;
-import org.mockito.MockitoAnnotations;
-
-import java.util.Optional;
-
-/**
- * Unit test against {@link PipDesktopState}.
- */
-@SmallTest
-@TestableLooper.RunWithLooper
-@RunWith(AndroidTestingRunner.class)
-@EnableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_PIP)
-public class PipDesktopStateTest {
- @Mock private PipDisplayLayoutState mMockPipDisplayLayoutState;
- @Mock private Optional<DesktopUserRepositories> mMockDesktopUserRepositoriesOptional;
- @Mock private DesktopUserRepositories mMockDesktopUserRepositories;
- @Mock private DesktopRepository mMockDesktopRepository;
- @Mock
- private Optional<DragToDesktopTransitionHandler> mMockDragToDesktopTransitionHandlerOptional;
- @Mock private DragToDesktopTransitionHandler mMockDragToDesktopTransitionHandler;
-
- @Mock private RootTaskDisplayAreaOrganizer mMockRootTaskDisplayAreaOrganizer;
- @Mock private ActivityManager.RunningTaskInfo mMockTaskInfo;
-
- private static final int DISPLAY_ID = 1;
- private DisplayAreaInfo mDefaultTda;
- private PipDesktopState mPipDesktopState;
-
- @Before
- public void setUp() {
- MockitoAnnotations.initMocks(this);
- when(mMockDesktopUserRepositoriesOptional.get()).thenReturn(mMockDesktopUserRepositories);
- when(mMockDesktopUserRepositories.getCurrent()).thenReturn(mMockDesktopRepository);
- when(mMockDesktopUserRepositoriesOptional.isPresent()).thenReturn(true);
-
- when(mMockDragToDesktopTransitionHandlerOptional.get()).thenReturn(
- mMockDragToDesktopTransitionHandler);
- when(mMockDragToDesktopTransitionHandlerOptional.isPresent()).thenReturn(true);
-
- when(mMockTaskInfo.getDisplayId()).thenReturn(DISPLAY_ID);
- when(mMockPipDisplayLayoutState.getDisplayId()).thenReturn(DISPLAY_ID);
-
- mDefaultTda = new DisplayAreaInfo(Mockito.mock(WindowContainerToken.class), DISPLAY_ID, 0);
- when(mMockRootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DISPLAY_ID)).thenReturn(
- mDefaultTda);
-
- mPipDesktopState = new PipDesktopState(mMockPipDisplayLayoutState,
- mMockDesktopUserRepositoriesOptional,
- mMockDragToDesktopTransitionHandlerOptional,
- mMockRootTaskDisplayAreaOrganizer);
- }
-
- @Test
- public void isDesktopWindowingPipEnabled_returnsTrue() {
- assertTrue(mPipDesktopState.isDesktopWindowingPipEnabled());
- }
-
- @Test
- public void isDesktopWindowingPipEnabled_desktopRepositoryEmpty_returnsFalse() {
- when(mMockDesktopUserRepositoriesOptional.isPresent()).thenReturn(false);
-
- assertFalse(mPipDesktopState.isDesktopWindowingPipEnabled());
- }
-
- @Test
- public void isDesktopWindowingPipEnabled_dragToDesktopTransitionHandlerEmpty_returnsFalse() {
- when(mMockDragToDesktopTransitionHandlerOptional.isPresent()).thenReturn(false);
-
- assertFalse(mPipDesktopState.isDesktopWindowingPipEnabled());
- }
-
- @Test
- @EnableFlags({
- FLAG_ENABLE_CONNECTED_DISPLAYS_PIP, FLAG_ENABLE_PIP2
- })
- public void isConnectedDisplaysPipEnabled_returnsTrue() {
- assertTrue(mPipDesktopState.isConnectedDisplaysPipEnabled());
- }
-
- @Test
- public void isPipInDesktopMode_anyDeskActive_returnsTrue() {
- when(mMockDesktopRepository.isAnyDeskActive(DISPLAY_ID)).thenReturn(true);
-
- assertTrue(mPipDesktopState.isPipInDesktopMode());
- }
-
- @Test
- public void isPipInDesktopMode_noDeskActive_returnsFalse() {
- when(mMockDesktopRepository.isAnyDeskActive(DISPLAY_ID)).thenReturn(false);
-
- assertFalse(mPipDesktopState.isPipInDesktopMode());
- }
-
- @Test
- public void getOutPipWindowingMode_exitToDesktop_displayFreeform_returnsUndefined() {
- when(mMockDesktopRepository.isAnyDeskActive(DISPLAY_ID)).thenReturn(true);
- setDisplayWindowingMode(WINDOWING_MODE_FREEFORM);
-
- assertEquals(WINDOWING_MODE_UNDEFINED, mPipDesktopState.getOutPipWindowingMode());
- }
-
- @Test
- public void getOutPipWindowingMode_exitToDesktop_displayFullscreen_returnsFreeform() {
- when(mMockDesktopRepository.isAnyDeskActive(DISPLAY_ID)).thenReturn(true);
- setDisplayWindowingMode(WINDOWING_MODE_FULLSCREEN);
-
- assertEquals(WINDOWING_MODE_FREEFORM, mPipDesktopState.getOutPipWindowingMode());
- }
-
- @Test
- public void getOutPipWindowingMode_exitToFullscreen_displayFullscreen_returnsUndefined() {
- setDisplayWindowingMode(WINDOWING_MODE_FULLSCREEN);
-
- assertEquals(WINDOWING_MODE_UNDEFINED, mPipDesktopState.getOutPipWindowingMode());
- }
-
- @Test
- public void isDragToDesktopInProgress_inProgress_returnsTrue() {
- when(mMockDragToDesktopTransitionHandler.getInProgress()).thenReturn(true);
-
- assertTrue(mPipDesktopState.isDragToDesktopInProgress());
- }
-
- @Test
- public void isDragToDesktopInProgress_notInProgress_returnsFalse() {
- when(mMockDragToDesktopTransitionHandler.getInProgress()).thenReturn(false);
-
- assertFalse(mPipDesktopState.isDragToDesktopInProgress());
- }
-
- private void setDisplayWindowingMode(int windowingMode) {
- mDefaultTda.configuration.windowConfiguration.setWindowingMode(windowingMode);
- }
-}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/pip/PipDesktopStateTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/pip/PipDesktopStateTest.kt
new file mode 100644
index 000000000000..2c50cd9d0c81
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/pip/PipDesktopStateTest.kt
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2025 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.wm.shell.common.pip
+
+import android.app.ActivityManager
+import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM
+import android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN
+import android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED
+import android.platform.test.annotations.EnableFlags
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper.RunWithLooper
+import android.window.DisplayAreaInfo
+import android.window.WindowContainerToken
+import androidx.test.filters.SmallTest
+import com.android.window.flags.Flags.FLAG_ENABLE_CONNECTED_DISPLAYS_PIP
+import com.android.window.flags.Flags.FLAG_ENABLE_DESKTOP_WINDOWING_PIP
+import com.android.wm.shell.Flags.FLAG_ENABLE_PIP2
+import com.android.wm.shell.RootTaskDisplayAreaOrganizer
+import com.android.wm.shell.ShellTestCase
+import com.android.wm.shell.desktopmode.DesktopRepository
+import com.android.wm.shell.desktopmode.DesktopUserRepositories
+import com.android.wm.shell.desktopmode.DragToDesktopTransitionHandler
+import com.google.common.truth.Truth.assertThat
+import java.util.Optional
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.whenever
+
+/**
+ * Unit test against [PipDesktopState].
+ */
+@SmallTest
+@RunWithLooper
+@RunWith(AndroidTestingRunner::class)
+@EnableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_PIP)
+class PipDesktopStateTest : ShellTestCase() {
+ private val mockPipDisplayLayoutState = mock<PipDisplayLayoutState>()
+ private val mockDesktopUserRepositories = mock<DesktopUserRepositories>()
+ private val mockDesktopRepository = mock<DesktopRepository>()
+ private val mockDragToDesktopTransitionHandler = mock<DragToDesktopTransitionHandler>()
+ private val mockRootTaskDisplayAreaOrganizer = mock<RootTaskDisplayAreaOrganizer>()
+ private val mockTaskInfo = mock<ActivityManager.RunningTaskInfo>()
+ private lateinit var defaultTda: DisplayAreaInfo
+ private lateinit var pipDesktopState: PipDesktopState
+
+ @Before
+ fun setUp() {
+ whenever(mockDesktopUserRepositories.current).thenReturn(mockDesktopRepository)
+ whenever(mockTaskInfo.getDisplayId()).thenReturn(DISPLAY_ID)
+ whenever(mockPipDisplayLayoutState.displayId).thenReturn(DISPLAY_ID)
+
+ defaultTda = DisplayAreaInfo(mock<WindowContainerToken>(), DISPLAY_ID, /* featureId = */ 0)
+ whenever(mockRootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DISPLAY_ID)).thenReturn(
+ defaultTda
+ )
+
+ pipDesktopState =
+ PipDesktopState(
+ mockPipDisplayLayoutState,
+ Optional.of(mockDesktopUserRepositories),
+ Optional.of(mockDragToDesktopTransitionHandler),
+ mockRootTaskDisplayAreaOrganizer
+ )
+ }
+
+ @Test
+ fun isDesktopWindowingPipEnabled_returnsTrue() {
+ assertThat(pipDesktopState.isDesktopWindowingPipEnabled()).isTrue()
+ }
+
+ @Test
+ @EnableFlags(
+ FLAG_ENABLE_CONNECTED_DISPLAYS_PIP,
+ FLAG_ENABLE_PIP2
+ )
+ fun isConnectedDisplaysPipEnabled_returnsTrue() {
+ assertThat(pipDesktopState.isConnectedDisplaysPipEnabled()).isTrue()
+ }
+
+ @Test
+ fun isPipInDesktopMode_anyDeskActive_returnsTrue() {
+ whenever(mockDesktopRepository.isAnyDeskActive(DISPLAY_ID)).thenReturn(true)
+
+ assertThat(pipDesktopState.isPipInDesktopMode()).isTrue()
+ }
+
+ @Test
+ fun isPipInDesktopMode_noDeskActive_returnsFalse() {
+ whenever(mockDesktopRepository.isAnyDeskActive(DISPLAY_ID)).thenReturn(false)
+
+ assertThat(pipDesktopState.isPipInDesktopMode()).isFalse()
+ }
+
+ @Test
+ fun outPipWindowingMode_exitToDesktop_displayFreeform_returnsUndefined() {
+ whenever(mockDesktopRepository.isAnyDeskActive(DISPLAY_ID)).thenReturn(true)
+ setDisplayWindowingMode(WINDOWING_MODE_FREEFORM)
+
+ assertThat(pipDesktopState.getOutPipWindowingMode()).isEqualTo(WINDOWING_MODE_UNDEFINED)
+ }
+
+ @Test
+ fun outPipWindowingMode_exitToDesktop_displayFullscreen_returnsFreeform() {
+ whenever(mockDesktopRepository.isAnyDeskActive(DISPLAY_ID)).thenReturn(true)
+ setDisplayWindowingMode(WINDOWING_MODE_FULLSCREEN)
+
+ assertThat(pipDesktopState.getOutPipWindowingMode()).isEqualTo(WINDOWING_MODE_FREEFORM)
+ }
+
+ @Test
+ fun outPipWindowingMode_exitToFullscreen_displayFullscreen_returnsUndefined() {
+ setDisplayWindowingMode(WINDOWING_MODE_FULLSCREEN)
+
+ assertThat(pipDesktopState.getOutPipWindowingMode()).isEqualTo(WINDOWING_MODE_UNDEFINED)
+ }
+
+ @Test
+ fun isDragToDesktopInProgress_inProgress_returnsTrue() {
+ whenever(mockDragToDesktopTransitionHandler.inProgress).thenReturn(true)
+
+ assertThat(pipDesktopState.isDragToDesktopInProgress()).isTrue()
+ }
+
+ @Test
+ fun isDragToDesktopInProgress_notInProgress_returnsFalse() {
+ whenever(mockDragToDesktopTransitionHandler.inProgress).thenReturn(false)
+
+ assertThat(pipDesktopState.isDragToDesktopInProgress()).isFalse()
+ }
+
+ private fun setDisplayWindowingMode(windowingMode: Int) {
+ defaultTda.configuration.windowConfiguration.windowingMode = windowingMode
+ }
+
+ companion object {
+ private const val DISPLAY_ID = 1
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/FlexParallaxSpecTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/FlexParallaxSpecTests.java
index 22a85fc49a4b..9f2534eb2662 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/FlexParallaxSpecTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/FlexParallaxSpecTests.java
@@ -71,6 +71,7 @@ public class FlexParallaxSpecTests {
when(mockSnapAlgorithm.getMiddleTarget()).thenReturn(mockMiddleTarget);
when(mockSnapAlgorithm.getLastSplitTarget()).thenReturn(mockLastTarget);
when(mockSnapAlgorithm.getDismissEndTarget()).thenReturn(mockEndEdge);
+ when(mockSnapAlgorithm.areOffscreenRatiosSupported()).thenReturn(true);
when(mockStartEdge.getPosition()).thenReturn(0);
when(mockFirstTarget.getPosition()).thenReturn(250);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java
index 597e4a55ed0e..9035df28aa7c 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java
@@ -680,7 +680,8 @@ public class CompatUIControllerTest extends ShellTestCase {
// Create transparent task
final TaskInfo taskInfo1 = createTaskInfo(DISPLAY_ID, newTaskId, /* hasSizeCompat= */ true,
- /* isVisible */ true, /* isFocused */ true, /* isTopActivityTransparent */ true);
+ /* isVisible */ true, /* isFocused */ true, /* isTopActivityTransparent */ true,
+ /* isRestartMenuEnabledForDisplayMove */ true);
// Simulate new task being shown
mController.updateActiveTaskInfo(taskInfo1);
@@ -742,32 +743,38 @@ public class CompatUIControllerTest extends ShellTestCase {
@Test
@RequiresFlagsDisabled(Flags.FLAG_APP_COMPAT_UI_FRAMEWORK)
public void testSendCompatUIRequest_createRestartDialog() {
- TaskInfo taskInfo = createTaskInfo(DISPLAY_ID, TASK_ID, /* hasSizeCompat= */ false);
- doReturn(true).when(mMockRestartDialogLayout)
- .needsToBeRecreated(any(TaskInfo.class),
- any(ShellTaskOrganizer.TaskListener.class));
+ final TaskInfo taskInfo = createTaskInfo(DISPLAY_ID, TASK_ID, /* hasSizeCompat= */ true,
+ /* isVisible */ true, /* isFocused */ true, /* isTopActivityTransparent */ false,
+ /* isRestartMenuEnabledForDisplayMove */ true);
doReturn(true).when(mCompatUIConfiguration).isRestartDialogEnabled();
doReturn(true).when(mCompatUIConfiguration).shouldShowRestartDialogAgain(eq(taskInfo));
- mController.sendCompatUIRequest(new CompatUIRequests.DisplayCompatShowRestartDialog(
- taskInfo, mMockTaskListener));
+ mController.onCompatInfoChanged(new CompatUIInfo(taskInfo, mMockTaskListener));
verify(mController).createRestartDialogWindowManager(any(), eq(taskInfo),
eq(mMockTaskListener));
+ verify(mMockRestartDialogLayout).setRequestRestartDialog(false);
+
+ mController.sendCompatUIRequest(
+ new CompatUIRequests.DisplayCompatShowRestartDialog(taskInfo.taskId));
+ verify(mMockRestartDialogLayout).setRequestRestartDialog(true);
}
private static TaskInfo createTaskInfo(int displayId, int taskId, boolean hasSizeCompat) {
return createTaskInfo(displayId, taskId, hasSizeCompat, /* isVisible */ false,
- /* isFocused */ false, /* isTopActivityTransparent */ false);
+ /* isFocused */ false, /* isTopActivityTransparent */ false,
+ /* isRestartMenuEnabledForDisplayMove */ false);
}
private static TaskInfo createTaskInfo(int displayId, int taskId, boolean hasSizeCompat,
boolean isVisible, boolean isFocused) {
return createTaskInfo(displayId, taskId, hasSizeCompat,
- isVisible, isFocused, /* isTopActivityTransparent */ false);
+ isVisible, isFocused, /* isTopActivityTransparent */ false,
+ /* isRestartMenuEnabledForDisplayMove */ false);
}
private static TaskInfo createTaskInfo(int displayId, int taskId, boolean hasSizeCompat,
- boolean isVisible, boolean isFocused, boolean isTopActivityTransparent) {
+ boolean isVisible, boolean isFocused, boolean isTopActivityTransparent,
+ boolean isRestartMenuEnabledForDisplayMove) {
RunningTaskInfo taskInfo = new RunningTaskInfo();
taskInfo.taskId = taskId;
taskInfo.displayId = displayId;
@@ -777,6 +784,8 @@ public class CompatUIControllerTest extends ShellTestCase {
taskInfo.isTopActivityTransparent = isTopActivityTransparent;
taskInfo.appCompatTaskInfo.setLetterboxEducationEnabled(true);
taskInfo.appCompatTaskInfo.setTopActivityLetterboxed(true);
+ taskInfo.appCompatTaskInfo.setRestartMenuEnabledForDisplayMove(
+ isRestartMenuEnabledForDisplayMove);
return taskInfo;
}
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopActivityOrientationChangeHandlerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopActivityOrientationChangeHandlerTest.kt
index d58f8a34c98e..94fe03084989 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopActivityOrientationChangeHandlerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopActivityOrientationChangeHandlerTest.kt
@@ -37,6 +37,8 @@ import com.android.window.flags.Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE
import com.android.window.flags.Flags.FLAG_RESPECT_ORIENTATION_CHANGE_FOR_UNRESIZEABLE
import com.android.wm.shell.ShellTaskOrganizer
import com.android.wm.shell.ShellTestCase
+import com.android.wm.shell.common.DisplayController
+import com.android.wm.shell.common.DisplayLayout
import com.android.wm.shell.common.ShellExecutor
import com.android.wm.shell.common.TaskStackListenerImpl
import com.android.wm.shell.desktopmode.DesktopTestHelpers.createFreeformTask
@@ -96,12 +98,15 @@ class DesktopActivityOrientationChangeHandlerTest : ShellTestCase() {
@Mock lateinit var repositoryInitializer: DesktopRepositoryInitializer
@Mock lateinit var userManager: UserManager
@Mock lateinit var shellController: ShellController
+ @Mock lateinit var displayController: DisplayController
+ @Mock lateinit var displayLayout: DisplayLayout
private lateinit var mockitoSession: StaticMockitoSession
private lateinit var handler: DesktopActivityOrientationChangeHandler
private lateinit var shellInit: ShellInit
private lateinit var userRepositories: DesktopUserRepositories
private lateinit var testScope: CoroutineScope
+
// Mock running tasks are registered here so we can get the list from mock shell task organizer.
private val runningTasks = mutableListOf<RunningTaskInfo>()
@@ -131,6 +136,7 @@ class DesktopActivityOrientationChangeHandlerTest : ShellTestCase() {
whenever(transitions.startTransition(anyInt(), any(), isNull())).thenAnswer { Binder() }
whenever(runBlocking { persistentRepository.readDesktop(any(), any()) })
.thenReturn(Desktop.getDefaultInstance())
+ whenever(displayController.getDisplayLayout(anyInt())).thenReturn(displayLayout)
handler =
DesktopActivityOrientationChangeHandler(
@@ -140,6 +146,7 @@ class DesktopActivityOrientationChangeHandlerTest : ShellTestCase() {
taskStackListener,
resizeTransitionHandler,
userRepositories,
+ displayController,
)
shellInit.init()
@@ -171,6 +178,7 @@ class DesktopActivityOrientationChangeHandlerTest : ShellTestCase() {
taskStackListener,
resizeTransitionHandler,
userRepositories,
+ displayController,
)
verify(shellInit, never())
@@ -251,6 +259,11 @@ class DesktopActivityOrientationChangeHandlerTest : ShellTestCase() {
val oldBounds = task.configuration.windowConfiguration.bounds
val newTask =
setUpFreeformTask(isResizeable = false, orientation = SCREEN_ORIENTATION_LANDSCAPE)
+ whenever(displayLayout.height()).thenReturn(800)
+ whenever(displayLayout.width()).thenReturn(2000)
+ whenever(displayLayout.getStableBounds(any())).thenAnswer { i ->
+ (i.arguments.first() as Rect).set(Rect(0, 0, 2000, 800))
+ }
handler.handleActivityOrientationChange(task, newTask)
@@ -279,6 +292,11 @@ class DesktopActivityOrientationChangeHandlerTest : ShellTestCase() {
bounds = oldBounds,
)
val newTask = setUpFreeformTask(isResizeable = false, bounds = oldBounds)
+ whenever(displayLayout.height()).thenReturn(2000)
+ whenever(displayLayout.width()).thenReturn(800)
+ whenever(displayLayout.getStableBounds(any())).thenAnswer { i ->
+ (i.arguments.first() as Rect).set(Rect(0, 0, 800, 2000))
+ }
handler.handleActivityOrientationChange(task, newTask)
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopDisplayEventHandlerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopDisplayEventHandlerTest.kt
index 2aebcdcc3bf5..9268db60aa51 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopDisplayEventHandlerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopDisplayEventHandlerTest.kt
@@ -24,13 +24,16 @@ import com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession
import com.android.dx.mockito.inline.extended.ExtendedMockito.never
import com.android.dx.mockito.inline.extended.StaticMockitoSession
import com.android.window.flags.Flags
+import com.android.wm.shell.RootTaskDisplayAreaOrganizer
import com.android.wm.shell.ShellTestCase
import com.android.wm.shell.common.DisplayController
import com.android.wm.shell.common.DisplayController.OnDisplaysChangedListener
import com.android.wm.shell.common.ShellExecutor
import com.android.wm.shell.desktopmode.persistence.DesktopRepositoryInitializer
import com.android.wm.shell.shared.desktopmode.DesktopModeStatus
+import com.android.wm.shell.sysui.ShellController
import com.android.wm.shell.sysui.ShellInit
+import com.android.wm.shell.sysui.UserChangeListener
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.cancel
import kotlinx.coroutines.flow.MutableStateFlow
@@ -46,6 +49,7 @@ import org.mockito.Mockito.spy
import org.mockito.Mockito.times
import org.mockito.Mockito.verify
import org.mockito.kotlin.argumentCaptor
+import org.mockito.kotlin.clearInvocations
import org.mockito.kotlin.whenever
import org.mockito.quality.Strictness
@@ -60,6 +64,8 @@ import org.mockito.quality.Strictness
class DesktopDisplayEventHandlerTest : ShellTestCase() {
@Mock lateinit var testExecutor: ShellExecutor
@Mock lateinit var displayController: DisplayController
+ @Mock private lateinit var mockShellController: ShellController
+ @Mock private lateinit var mockRootTaskDisplayAreaOrganizer: RootTaskDisplayAreaOrganizer
@Mock private lateinit var mockDesktopUserRepositories: DesktopUserRepositories
@Mock private lateinit var mockDesktopRepository: DesktopRepository
@Mock private lateinit var mockDesktopTasksController: DesktopTasksController
@@ -89,7 +95,9 @@ class DesktopDisplayEventHandlerTest : ShellTestCase() {
context,
shellInit,
testScope.backgroundScope,
+ mockShellController,
displayController,
+ mockRootTaskDisplayAreaOrganizer,
desktopRepositoryInitializer,
mockDesktopUserRepositories,
mockDesktopTasksController,
@@ -107,6 +115,7 @@ class DesktopDisplayEventHandlerTest : ShellTestCase() {
}
@Test
+ @EnableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
fun testDisplayAdded_supportsDesks_desktopRepositoryInitialized_createsDesk() =
testScope.runTest {
whenever(DesktopModeStatus.canEnterDesktopMode(context)).thenReturn(true)
@@ -119,6 +128,7 @@ class DesktopDisplayEventHandlerTest : ShellTestCase() {
}
@Test
+ @EnableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
fun testDisplayAdded_supportsDesks_desktopRepositoryNotInitialized_doesNotCreateDesk() =
testScope.runTest {
whenever(DesktopModeStatus.canEnterDesktopMode(context)).thenReturn(true)
@@ -130,6 +140,7 @@ class DesktopDisplayEventHandlerTest : ShellTestCase() {
}
@Test
+ @EnableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
fun testDisplayAdded_supportsDesks_desktopRepositoryInitializedTwice_createsDeskOnce() =
testScope.runTest {
whenever(DesktopModeStatus.canEnterDesktopMode(context)).thenReturn(true)
@@ -143,6 +154,7 @@ class DesktopDisplayEventHandlerTest : ShellTestCase() {
}
@Test
+ @EnableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
fun testDisplayAdded_supportsDesks_desktopRepositoryInitialized_deskExists_doesNotCreateDesk() =
testScope.runTest {
whenever(DesktopModeStatus.canEnterDesktopMode(context)).thenReturn(true)
@@ -156,33 +168,71 @@ class DesktopDisplayEventHandlerTest : ShellTestCase() {
}
@Test
- fun testDisplayAdded_cannotEnterDesktopMode_doesNotCreateDesk() {
- whenever(DesktopModeStatus.canEnterDesktopMode(context)).thenReturn(false)
+ @EnableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+ fun testDisplayAdded_cannotEnterDesktopMode_doesNotCreateDesk() =
+ testScope.runTest {
+ whenever(DesktopModeStatus.canEnterDesktopMode(context)).thenReturn(false)
+ desktopRepositoryInitializer.initialize(mockDesktopUserRepositories)
- onDisplaysChangedListenerCaptor.lastValue.onDisplayAdded(DEFAULT_DISPLAY)
+ onDisplaysChangedListenerCaptor.lastValue.onDisplayAdded(DEFAULT_DISPLAY)
+ runCurrent()
- verify(mockDesktopTasksController, never()).createDesk(DEFAULT_DISPLAY)
- }
+ verify(mockDesktopTasksController, never()).createDesk(DEFAULT_DISPLAY)
+ }
@Test
@EnableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
- fun testDeskRemoved_noDesksRemain_createsDesk() {
- whenever(mockDesktopRepository.getNumberOfDesks(DEFAULT_DISPLAY)).thenReturn(0)
+ fun testDeskRemoved_noDesksRemain_createsDesk() =
+ testScope.runTest {
+ whenever(DesktopModeStatus.canEnterDesktopMode(context)).thenReturn(true)
+ whenever(mockDesktopRepository.getNumberOfDesks(DEFAULT_DISPLAY)).thenReturn(0)
+ desktopRepositoryInitializer.initialize(mockDesktopUserRepositories)
- handler.onDeskRemoved(DEFAULT_DISPLAY, deskId = 1)
+ handler.onDeskRemoved(DEFAULT_DISPLAY, deskId = 1)
+ runCurrent()
- verify(mockDesktopTasksController).createDesk(DEFAULT_DISPLAY)
- }
+ verify(mockDesktopTasksController).createDesk(DEFAULT_DISPLAY)
+ }
@Test
@EnableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
- fun testDeskRemoved_desksRemain_doesNotCreateDesk() {
- whenever(mockDesktopRepository.getNumberOfDesks(DEFAULT_DISPLAY)).thenReturn(1)
+ fun testDeskRemoved_desksRemain_doesNotCreateDesk() =
+ testScope.runTest {
+ whenever(DesktopModeStatus.canEnterDesktopMode(context)).thenReturn(true)
+ whenever(mockDesktopRepository.getNumberOfDesks(DEFAULT_DISPLAY)).thenReturn(1)
+ desktopRepositoryInitializer.initialize(mockDesktopUserRepositories)
+
+ handler.onDeskRemoved(DEFAULT_DISPLAY, deskId = 1)
+ runCurrent()
- handler.onDeskRemoved(DEFAULT_DISPLAY, deskId = 1)
+ verify(mockDesktopTasksController, never()).createDesk(DEFAULT_DISPLAY)
+ }
- verify(mockDesktopTasksController, never()).createDesk(DEFAULT_DISPLAY)
- }
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+ fun testUserChanged_createsDeskWhenNeeded() =
+ testScope.runTest {
+ whenever(DesktopModeStatus.canEnterDesktopMode(context)).thenReturn(true)
+ val userChangeListenerCaptor = argumentCaptor<UserChangeListener>()
+ verify(mockShellController).addUserChangeListener(userChangeListenerCaptor.capture())
+ whenever(mockDesktopRepository.getNumberOfDesks(displayId = 2)).thenReturn(0)
+ whenever(mockDesktopRepository.getNumberOfDesks(displayId = 3)).thenReturn(0)
+ whenever(mockDesktopRepository.getNumberOfDesks(displayId = 4)).thenReturn(1)
+ whenever(mockRootTaskDisplayAreaOrganizer.displayIds).thenReturn(intArrayOf(2, 3, 4))
+ desktopRepositoryInitializer.initialize(mockDesktopUserRepositories)
+ handler.onDisplayAdded(displayId = 2)
+ handler.onDisplayAdded(displayId = 3)
+ handler.onDisplayAdded(displayId = 4)
+ runCurrent()
+
+ clearInvocations(mockDesktopTasksController)
+ userChangeListenerCaptor.lastValue.onUserChanged(1, context)
+ runCurrent()
+
+ verify(mockDesktopTasksController).createDesk(displayId = 2)
+ verify(mockDesktopTasksController).createDesk(displayId = 3)
+ verify(mockDesktopTasksController, never()).createDesk(displayId = 4)
+ }
@Test
fun testConnectExternalDisplay() {
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopDisplayModeControllerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopDisplayModeControllerTest.kt
index da6a67c679ff..96b826f93aae 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopDisplayModeControllerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopDisplayModeControllerTest.kt
@@ -33,6 +33,7 @@ import android.provider.Settings.Global.DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERN
import android.view.Display
import android.view.Display.DEFAULT_DISPLAY
import android.view.IWindowManager
+import android.view.InputDevice
import android.view.WindowManager.TRANSIT_CHANGE
import android.window.DisplayAreaInfo
import android.window.WindowContainerTransaction
@@ -103,6 +104,7 @@ class DesktopDisplayModeControllerTest(
private val wallpaperToken = MockToken().token()
private val defaultDisplay = mock<Display>()
private val externalDisplay = mock<Display>()
+ private val mouseDevice = mock<InputDevice>()
private lateinit var extendedDisplaySettingsRestoreSession:
ExtendedDisplaySettingsRestoreSession
@@ -150,6 +152,9 @@ class DesktopDisplayModeControllerTest(
defaultDisplay
)
).thenReturn(true)
+ whenever(mouseDevice.supportsSource(InputDevice.SOURCE_MOUSE)).thenReturn(true)
+ whenever(inputManager.getInputDevice(EXTERNAL_DEVICE_ID)).thenReturn(mouseDevice)
+ setMouseConnected(false)
}
@After
@@ -207,6 +212,7 @@ class DesktopDisplayModeControllerTest(
fun testTargetWindowingMode_formfactorDisabled(
@TestParameter param: ExternalDisplayBasedTargetModeTestCase,
@TestParameter tabletModeStatus: SwitchState,
+ @TestParameter hasAnyMouseDevice: Boolean,
) {
whenever(mockWindowManager.getWindowingMode(anyInt()))
.thenReturn(param.defaultWindowingMode)
@@ -216,6 +222,7 @@ class DesktopDisplayModeControllerTest(
disconnectExternalDisplay()
}
setTabletModeStatus(tabletModeStatus)
+ setMouseConnected(hasAnyMouseDevice)
setExtendedMode(param.extendedDisplayEnabled)
whenever(
DesktopModeStatus.isDesktopModeSupportedOnDisplay(
@@ -247,6 +254,7 @@ class DesktopDisplayModeControllerTest(
defaultDisplay
)
).thenReturn(param.isDefaultDisplayDesktopEligible)
+ setMouseConnected(param.hasAnyMouseDevice)
assertThat(controller.getTargetWindowingModeForDefaultDisplay())
.isEqualTo(param.expectedWindowingMode)
@@ -321,6 +329,11 @@ class DesktopDisplayModeControllerTest(
}
}
+ private fun setMouseConnected(connected: Boolean) {
+ whenever(inputManager.inputDeviceIds)
+ .thenReturn(if (connected) intArrayOf(EXTERNAL_DEVICE_ID) else intArrayOf())
+ }
+
private class ExtendedDisplaySettingsRestoreSession(
private val contentResolver: ContentResolver
) {
@@ -345,6 +358,7 @@ class DesktopDisplayModeControllerTest(
companion object {
const val EXTERNAL_DISPLAY_ID = 100
+ const val EXTERNAL_DEVICE_ID = 10
enum class SwitchState(val value: Int) {
UNKNOWN(InputManager.SWITCH_STATE_UNKNOWN),
@@ -478,174 +492,391 @@ class DesktopDisplayModeControllerTest(
val extendedDisplayEnabled: Boolean,
val tabletModeStatus: SwitchState,
val isDefaultDisplayDesktopEligible: Boolean,
+ val hasAnyMouseDevice: Boolean,
val expectedWindowingMode: Int,
) {
- EXTERNAL_EXTENDED_TABLET_NO_PROJECTED(
+ EXTERNAL_EXTENDED_TABLET_NO_PROJECTED_NO_MOUSE(
hasExternalDisplay = true,
extendedDisplayEnabled = true,
tabletModeStatus = SwitchState.ON,
isDefaultDisplayDesktopEligible = true,
+ hasAnyMouseDevice = false,
expectedWindowingMode = WINDOWING_MODE_FREEFORM,
),
- NO_EXTERNAL_EXTENDED_TABLET_NO_PROJECTED(
+ NO_EXTERNAL_EXTENDED_TABLET_NO_PROJECTED_NO_MOUSE(
hasExternalDisplay = false,
extendedDisplayEnabled = true,
tabletModeStatus = SwitchState.ON,
isDefaultDisplayDesktopEligible = true,
+ hasAnyMouseDevice = false,
expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
),
- EXTERNAL_MIRROR_TABLET_NO_PROJECTED(
+ EXTERNAL_MIRROR_TABLET_NO_PROJECTED_NO_MOUSE(
hasExternalDisplay = true,
extendedDisplayEnabled = false,
tabletModeStatus = SwitchState.ON,
isDefaultDisplayDesktopEligible = true,
+ hasAnyMouseDevice = false,
expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
),
- NO_EXTERNAL_MIRROR_TABLET_NO_PROJECTED(
+ NO_EXTERNAL_MIRROR_TABLET_NO_PROJECTED_NO_MOUSE(
hasExternalDisplay = false,
extendedDisplayEnabled = false,
tabletModeStatus = SwitchState.ON,
isDefaultDisplayDesktopEligible = true,
+ hasAnyMouseDevice = false,
expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
),
- EXTERNAL_EXTENDED_CLAMSHELL_NO_PROJECTED(
+ EXTERNAL_EXTENDED_CLAMSHELL_NO_PROJECTED_NO_MOUSE(
hasExternalDisplay = true,
extendedDisplayEnabled = true,
tabletModeStatus = SwitchState.OFF,
isDefaultDisplayDesktopEligible = true,
+ hasAnyMouseDevice = false,
expectedWindowingMode = WINDOWING_MODE_FREEFORM,
),
- NO_EXTERNAL_EXTENDED_CLAMSHELL_NO_PROJECTED(
+ NO_EXTERNAL_EXTENDED_CLAMSHELL_NO_PROJECTED_NO_MOUSE(
hasExternalDisplay = false,
extendedDisplayEnabled = true,
tabletModeStatus = SwitchState.OFF,
isDefaultDisplayDesktopEligible = true,
+ hasAnyMouseDevice = false,
expectedWindowingMode = WINDOWING_MODE_FREEFORM,
),
- EXTERNAL_MIRROR_CLAMSHELL_NO_PROJECTED(
+ EXTERNAL_MIRROR_CLAMSHELL_NO_PROJECTED_NO_MOUSE(
hasExternalDisplay = true,
extendedDisplayEnabled = false,
tabletModeStatus = SwitchState.OFF,
isDefaultDisplayDesktopEligible = true,
+ hasAnyMouseDevice = false,
expectedWindowingMode = WINDOWING_MODE_FREEFORM,
),
- NO_EXTERNAL_MIRROR_CLAMSHELL_NO_PROJECTED(
+ NO_EXTERNAL_MIRROR_CLAMSHELL_NO_PROJECTED_NO_MOUSE(
hasExternalDisplay = false,
extendedDisplayEnabled = false,
tabletModeStatus = SwitchState.OFF,
isDefaultDisplayDesktopEligible = true,
+ hasAnyMouseDevice = false,
expectedWindowingMode = WINDOWING_MODE_FREEFORM,
),
- EXTERNAL_EXTENDED_UNKNOWN_NO_PROJECTED(
+ EXTERNAL_EXTENDED_UNKNOWN_NO_PROJECTED_NO_MOUSE(
hasExternalDisplay = true,
extendedDisplayEnabled = true,
tabletModeStatus = SwitchState.UNKNOWN,
isDefaultDisplayDesktopEligible = true,
+ hasAnyMouseDevice = false,
expectedWindowingMode = WINDOWING_MODE_FREEFORM,
),
- NO_EXTERNAL_EXTENDED_UNKNOWN_NO_PROJECTED(
+ NO_EXTERNAL_EXTENDED_UNKNOWN_NO_PROJECTED_NO_MOUSE(
hasExternalDisplay = false,
extendedDisplayEnabled = true,
tabletModeStatus = SwitchState.UNKNOWN,
isDefaultDisplayDesktopEligible = true,
+ hasAnyMouseDevice = false,
expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
),
- EXTERNAL_MIRROR_UNKNOWN_NO_PROJECTED(
+ EXTERNAL_MIRROR_UNKNOWN_NO_PROJECTED_NO_MOUSE(
hasExternalDisplay = true,
extendedDisplayEnabled = false,
tabletModeStatus = SwitchState.UNKNOWN,
isDefaultDisplayDesktopEligible = true,
+ hasAnyMouseDevice = false,
expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
),
- NO_EXTERNAL_MIRROR_UNKNOWN_NO_PROJECTED(
+ NO_EXTERNAL_MIRROR_UNKNOWN_NO_PROJECTED_NO_MOUSE(
hasExternalDisplay = false,
extendedDisplayEnabled = false,
tabletModeStatus = SwitchState.UNKNOWN,
isDefaultDisplayDesktopEligible = true,
+ hasAnyMouseDevice = false,
+ expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
+ ),
+ EXTERNAL_EXTENDED_TABLET_PROJECTED_NO_MOUSE(
+ hasExternalDisplay = true,
+ extendedDisplayEnabled = true,
+ tabletModeStatus = SwitchState.ON,
+ isDefaultDisplayDesktopEligible = false,
+ hasAnyMouseDevice = false,
+ expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
+ ),
+ NO_EXTERNAL_EXTENDED_TABLET_PROJECTED_NO_MOUSE(
+ hasExternalDisplay = false,
+ extendedDisplayEnabled = true,
+ tabletModeStatus = SwitchState.ON,
+ isDefaultDisplayDesktopEligible = false,
+ hasAnyMouseDevice = false,
+ expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
+ ),
+ EXTERNAL_MIRROR_TABLET_PROJECTED_NO_MOUSE(
+ hasExternalDisplay = true,
+ extendedDisplayEnabled = false,
+ tabletModeStatus = SwitchState.ON,
+ isDefaultDisplayDesktopEligible = false,
+ hasAnyMouseDevice = false,
+ expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
+ ),
+ NO_EXTERNAL_MIRROR_TABLET_PROJECTED_NO_MOUSE(
+ hasExternalDisplay = false,
+ extendedDisplayEnabled = false,
+ tabletModeStatus = SwitchState.ON,
+ isDefaultDisplayDesktopEligible = false,
+ hasAnyMouseDevice = false,
+ expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
+ ),
+ EXTERNAL_EXTENDED_CLAMSHELL_PROJECTED_NO_MOUSE(
+ hasExternalDisplay = true,
+ extendedDisplayEnabled = true,
+ tabletModeStatus = SwitchState.OFF,
+ isDefaultDisplayDesktopEligible = false,
+ hasAnyMouseDevice = false,
+ expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
+ ),
+ NO_EXTERNAL_EXTENDED_CLAMSHELL_PROJECTED_NO_MOUSE(
+ hasExternalDisplay = false,
+ extendedDisplayEnabled = true,
+ tabletModeStatus = SwitchState.OFF,
+ isDefaultDisplayDesktopEligible = false,
+ hasAnyMouseDevice = false,
+ expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
+ ),
+ EXTERNAL_MIRROR_CLAMSHELL_PROJECTED_NO_MOUSE(
+ hasExternalDisplay = true,
+ extendedDisplayEnabled = false,
+ tabletModeStatus = SwitchState.OFF,
+ isDefaultDisplayDesktopEligible = false,
+ hasAnyMouseDevice = false,
+ expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
+ ),
+ NO_EXTERNAL_MIRROR_CLAMSHELL_PROJECTED_NO_MOUSE(
+ hasExternalDisplay = false,
+ extendedDisplayEnabled = false,
+ tabletModeStatus = SwitchState.OFF,
+ isDefaultDisplayDesktopEligible = false,
+ hasAnyMouseDevice = false,
+ expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
+ ),
+ EXTERNAL_EXTENDED_UNKNOWN_PROJECTED_NO_MOUSE(
+ hasExternalDisplay = true,
+ extendedDisplayEnabled = true,
+ tabletModeStatus = SwitchState.UNKNOWN,
+ isDefaultDisplayDesktopEligible = false,
+ hasAnyMouseDevice = false,
+ expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
+ ),
+ NO_EXTERNAL_EXTENDED_UNKNOWN_PROJECTED_NO_MOUSE(
+ hasExternalDisplay = false,
+ extendedDisplayEnabled = true,
+ tabletModeStatus = SwitchState.UNKNOWN,
+ isDefaultDisplayDesktopEligible = false,
+ hasAnyMouseDevice = false,
+ expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
+ ),
+ EXTERNAL_MIRROR_UNKNOWN_PROJECTED_NO_MOUSE(
+ hasExternalDisplay = true,
+ extendedDisplayEnabled = false,
+ tabletModeStatus = SwitchState.UNKNOWN,
+ isDefaultDisplayDesktopEligible = false,
+ hasAnyMouseDevice = false,
+ expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
+ ),
+ NO_EXTERNAL_MIRROR_UNKNOWN_PROJECTED_NO_MOUSE(
+ hasExternalDisplay = false,
+ extendedDisplayEnabled = false,
+ tabletModeStatus = SwitchState.UNKNOWN,
+ isDefaultDisplayDesktopEligible = false,
+ hasAnyMouseDevice = false,
expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
),
- EXTERNAL_EXTENDED_TABLET_PROJECTED(
+ EXTERNAL_EXTENDED_TABLET_NO_PROJECTED_MOUSE(
+ hasExternalDisplay = true,
+ extendedDisplayEnabled = true,
+ tabletModeStatus = SwitchState.ON,
+ isDefaultDisplayDesktopEligible = true,
+ hasAnyMouseDevice = true,
+ expectedWindowingMode = WINDOWING_MODE_FREEFORM,
+ ),
+ NO_EXTERNAL_EXTENDED_TABLET_NO_PROJECTED_MOUSE(
+ hasExternalDisplay = false,
+ extendedDisplayEnabled = true,
+ tabletModeStatus = SwitchState.ON,
+ isDefaultDisplayDesktopEligible = true,
+ hasAnyMouseDevice = true,
+ expectedWindowingMode = WINDOWING_MODE_FREEFORM,
+ ),
+ EXTERNAL_MIRROR_TABLET_NO_PROJECTED_MOUSE(
+ hasExternalDisplay = true,
+ extendedDisplayEnabled = false,
+ tabletModeStatus = SwitchState.ON,
+ isDefaultDisplayDesktopEligible = true,
+ hasAnyMouseDevice = true,
+ expectedWindowingMode = WINDOWING_MODE_FREEFORM,
+ ),
+ NO_EXTERNAL_MIRROR_TABLET_NO_PROJECTED_MOUSE(
+ hasExternalDisplay = false,
+ extendedDisplayEnabled = false,
+ tabletModeStatus = SwitchState.ON,
+ isDefaultDisplayDesktopEligible = true,
+ hasAnyMouseDevice = true,
+ expectedWindowingMode = WINDOWING_MODE_FREEFORM,
+ ),
+ EXTERNAL_EXTENDED_CLAMSHELL_NO_PROJECTED_MOUSE(
+ hasExternalDisplay = true,
+ extendedDisplayEnabled = true,
+ tabletModeStatus = SwitchState.OFF,
+ isDefaultDisplayDesktopEligible = true,
+ hasAnyMouseDevice = true,
+ expectedWindowingMode = WINDOWING_MODE_FREEFORM,
+ ),
+ NO_EXTERNAL_EXTENDED_CLAMSHELL_NO_PROJECTED_MOUSE(
+ hasExternalDisplay = false,
+ extendedDisplayEnabled = true,
+ tabletModeStatus = SwitchState.OFF,
+ isDefaultDisplayDesktopEligible = true,
+ hasAnyMouseDevice = true,
+ expectedWindowingMode = WINDOWING_MODE_FREEFORM,
+ ),
+ EXTERNAL_MIRROR_CLAMSHELL_NO_PROJECTED_MOUSE(
+ hasExternalDisplay = true,
+ extendedDisplayEnabled = false,
+ tabletModeStatus = SwitchState.OFF,
+ isDefaultDisplayDesktopEligible = true,
+ hasAnyMouseDevice = true,
+ expectedWindowingMode = WINDOWING_MODE_FREEFORM,
+ ),
+ NO_EXTERNAL_MIRROR_CLAMSHELL_NO_PROJECTED_MOUSE(
+ hasExternalDisplay = false,
+ extendedDisplayEnabled = false,
+ tabletModeStatus = SwitchState.OFF,
+ isDefaultDisplayDesktopEligible = true,
+ hasAnyMouseDevice = true,
+ expectedWindowingMode = WINDOWING_MODE_FREEFORM,
+ ),
+ EXTERNAL_EXTENDED_UNKNOWN_NO_PROJECTED_MOUSE(
+ hasExternalDisplay = true,
+ extendedDisplayEnabled = true,
+ tabletModeStatus = SwitchState.UNKNOWN,
+ isDefaultDisplayDesktopEligible = true,
+ hasAnyMouseDevice = true,
+ expectedWindowingMode = WINDOWING_MODE_FREEFORM,
+ ),
+ NO_EXTERNAL_EXTENDED_UNKNOWN_NO_PROJECTED_MOUSE(
+ hasExternalDisplay = false,
+ extendedDisplayEnabled = true,
+ tabletModeStatus = SwitchState.UNKNOWN,
+ isDefaultDisplayDesktopEligible = true,
+ hasAnyMouseDevice = true,
+ expectedWindowingMode = WINDOWING_MODE_FREEFORM,
+ ),
+ EXTERNAL_MIRROR_UNKNOWN_NO_PROJECTED_MOUSE(
+ hasExternalDisplay = true,
+ extendedDisplayEnabled = false,
+ tabletModeStatus = SwitchState.UNKNOWN,
+ isDefaultDisplayDesktopEligible = true,
+ hasAnyMouseDevice = true,
+ expectedWindowingMode = WINDOWING_MODE_FREEFORM,
+ ),
+ NO_EXTERNAL_MIRROR_UNKNOWN_NO_PROJECTED_MOUSE(
+ hasExternalDisplay = false,
+ extendedDisplayEnabled = false,
+ tabletModeStatus = SwitchState.UNKNOWN,
+ isDefaultDisplayDesktopEligible = true,
+ hasAnyMouseDevice = true,
+ expectedWindowingMode = WINDOWING_MODE_FREEFORM,
+ ),
+ EXTERNAL_EXTENDED_TABLET_PROJECTED_MOUSE(
hasExternalDisplay = true,
extendedDisplayEnabled = true,
tabletModeStatus = SwitchState.ON,
isDefaultDisplayDesktopEligible = false,
+ hasAnyMouseDevice = true,
expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
),
- NO_EXTERNAL_EXTENDED_TABLET_PROJECTED(
+ NO_EXTERNAL_EXTENDED_TABLET_PROJECTED_MOUSE(
hasExternalDisplay = false,
extendedDisplayEnabled = true,
tabletModeStatus = SwitchState.ON,
isDefaultDisplayDesktopEligible = false,
+ hasAnyMouseDevice = true,
expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
),
- EXTERNAL_MIRROR_TABLET_PROJECTED(
+ EXTERNAL_MIRROR_TABLET_PROJECTED_MOUSE(
hasExternalDisplay = true,
extendedDisplayEnabled = false,
tabletModeStatus = SwitchState.ON,
isDefaultDisplayDesktopEligible = false,
+ hasAnyMouseDevice = true,
expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
),
- NO_EXTERNAL_MIRROR_TABLET_PROJECTED(
+ NO_EXTERNAL_MIRROR_TABLET_PROJECTED_MOUSE(
hasExternalDisplay = false,
extendedDisplayEnabled = false,
tabletModeStatus = SwitchState.ON,
isDefaultDisplayDesktopEligible = false,
+ hasAnyMouseDevice = true,
expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
),
- EXTERNAL_EXTENDED_CLAMSHELL_PROJECTED(
+ EXTERNAL_EXTENDED_CLAMSHELL_PROJECTED_MOUSE(
hasExternalDisplay = true,
extendedDisplayEnabled = true,
tabletModeStatus = SwitchState.OFF,
isDefaultDisplayDesktopEligible = false,
+ hasAnyMouseDevice = true,
expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
),
- NO_EXTERNAL_EXTENDED_CLAMSHELL_PROJECTED(
+ NO_EXTERNAL_EXTENDED_CLAMSHELL_PROJECTED_MOUSE(
hasExternalDisplay = false,
extendedDisplayEnabled = true,
tabletModeStatus = SwitchState.OFF,
isDefaultDisplayDesktopEligible = false,
+ hasAnyMouseDevice = true,
expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
),
- EXTERNAL_MIRROR_CLAMSHELL_PROJECTED(
+ EXTERNAL_MIRROR_CLAMSHELL_PROJECTED_MOUSE(
hasExternalDisplay = true,
extendedDisplayEnabled = false,
tabletModeStatus = SwitchState.OFF,
isDefaultDisplayDesktopEligible = false,
+ hasAnyMouseDevice = true,
expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
),
- NO_EXTERNAL_MIRROR_CLAMSHELL_PROJECTED(
+ NO_EXTERNAL_MIRROR_CLAMSHELL_PROJECTED_MOUSE(
hasExternalDisplay = false,
extendedDisplayEnabled = false,
tabletModeStatus = SwitchState.OFF,
isDefaultDisplayDesktopEligible = false,
+ hasAnyMouseDevice = true,
expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
),
- EXTERNAL_EXTENDED_UNKNOWN_PROJECTED(
+ EXTERNAL_EXTENDED_UNKNOWN_PROJECTED_MOUSE(
hasExternalDisplay = true,
extendedDisplayEnabled = true,
tabletModeStatus = SwitchState.UNKNOWN,
isDefaultDisplayDesktopEligible = false,
+ hasAnyMouseDevice = true,
expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
),
- NO_EXTERNAL_EXTENDED_UNKNOWN_PROJECTED(
+ NO_EXTERNAL_EXTENDED_UNKNOWN_PROJECTED_MOUSE(
hasExternalDisplay = false,
extendedDisplayEnabled = true,
tabletModeStatus = SwitchState.UNKNOWN,
isDefaultDisplayDesktopEligible = false,
+ hasAnyMouseDevice = true,
expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
),
- EXTERNAL_MIRROR_UNKNOWN_PROJECTED(
+ EXTERNAL_MIRROR_UNKNOWN_PROJECTED_MOUSE(
hasExternalDisplay = true,
extendedDisplayEnabled = false,
tabletModeStatus = SwitchState.UNKNOWN,
isDefaultDisplayDesktopEligible = false,
+ hasAnyMouseDevice = true,
expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
),
- NO_EXTERNAL_MIRROR_UNKNOWN_PROJECTED(
+ NO_EXTERNAL_MIRROR_UNKNOWN_PROJECTED_MOUSE(
hasExternalDisplay = false,
extendedDisplayEnabled = false,
tabletModeStatus = SwitchState.UNKNOWN,
isDefaultDisplayDesktopEligible = false,
+ hasAnyMouseDevice = true,
expectedWindowingMode = WINDOWING_MODE_FULLSCREEN,
),
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeKeyGestureHandlerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeKeyGestureHandlerTest.kt
index d510570e8839..e40da5e8498d 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeKeyGestureHandlerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeKeyGestureHandlerTest.kt
@@ -50,7 +50,6 @@ import com.android.wm.shell.desktopmode.common.ToggleTaskSizeInteraction
import com.android.wm.shell.sysui.ShellInit
import com.android.wm.shell.transition.FocusTransitionObserver
import com.android.wm.shell.windowdecor.DesktopModeWindowDecorViewModel
-import com.google.common.truth.Truth.assertThat
import java.util.Optional
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
@@ -120,11 +119,11 @@ class DesktopModeKeyGestureHandlerTest : ShellTestCase() {
whenever(rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY)).thenReturn(tda)
doAnswer {
- keyGestureEventHandler = (it.arguments[0] as KeyGestureEventHandler)
+ keyGestureEventHandler = (it.arguments[1] as KeyGestureEventHandler)
null
}
.whenever(inputManager)
- .registerKeyGestureEventHandler(any())
+ .registerKeyGestureEventHandler(any(), any())
shellInit.init()
desktopModeKeyGestureHandler =
@@ -176,10 +175,9 @@ class DesktopModeKeyGestureHandlerTest : ShellTestCase() {
.setKeycodes(intArrayOf(KeyEvent.KEYCODE_D))
.setModifierState(KeyEvent.META_META_ON or KeyEvent.META_CTRL_ON)
.build()
- val result = keyGestureEventHandler.handleKeyGestureEvent(event, null)
+ keyGestureEventHandler.handleKeyGestureEvent(event, null)
testExecutor.flushAll()
- assertThat(result).isTrue()
verify(desktopTasksController).moveToNextDisplay(task.taskId)
}
@@ -197,10 +195,9 @@ class DesktopModeKeyGestureHandlerTest : ShellTestCase() {
.setKeycodes(intArrayOf(KeyEvent.KEYCODE_LEFT_BRACKET))
.setModifierState(KeyEvent.META_META_ON)
.build()
- val result = keyGestureEventHandler.handleKeyGestureEvent(event, null)
+ keyGestureEventHandler.handleKeyGestureEvent(event, null)
testExecutor.flushAll()
- assertThat(result).isTrue()
verify(desktopModeWindowDecorViewModel)
.onSnapResize(
task.taskId,
@@ -224,10 +221,9 @@ class DesktopModeKeyGestureHandlerTest : ShellTestCase() {
.setKeycodes(intArrayOf(KeyEvent.KEYCODE_RIGHT_BRACKET))
.setModifierState(KeyEvent.META_META_ON)
.build()
- val result = keyGestureEventHandler.handleKeyGestureEvent(event, null)
+ keyGestureEventHandler.handleKeyGestureEvent(event, null)
testExecutor.flushAll()
- assertThat(result).isTrue()
verify(desktopModeWindowDecorViewModel)
.onSnapResize(
task.taskId,
@@ -251,10 +247,9 @@ class DesktopModeKeyGestureHandlerTest : ShellTestCase() {
.setKeycodes(intArrayOf(KeyEvent.KEYCODE_EQUALS))
.setModifierState(KeyEvent.META_META_ON)
.build()
- val result = keyGestureEventHandler.handleKeyGestureEvent(event, null)
+ keyGestureEventHandler.handleKeyGestureEvent(event, null)
testExecutor.flushAll()
- assertThat(result).isTrue()
verify(desktopTasksController)
.toggleDesktopTaskSize(
task,
@@ -280,10 +275,9 @@ class DesktopModeKeyGestureHandlerTest : ShellTestCase() {
.setKeycodes(intArrayOf(KeyEvent.KEYCODE_MINUS))
.setModifierState(KeyEvent.META_META_ON)
.build()
- val result = keyGestureEventHandler.handleKeyGestureEvent(event, null)
+ keyGestureEventHandler.handleKeyGestureEvent(event, null)
testExecutor.flushAll()
- assertThat(result).isTrue()
verify(desktopTasksController).minimizeTask(task, MinimizeReason.KEY_GESTURE)
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeMoveToDisplayTransitionHandlerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeMoveToDisplayTransitionHandlerTest.kt
index 6a99d4770728..a7ea66e17363 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeMoveToDisplayTransitionHandlerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeMoveToDisplayTransitionHandlerTest.kt
@@ -43,7 +43,8 @@ class DesktopModeMoveToDisplayTransitionHandlerTest : ShellTestCase() {
@Before
fun setUp() {
- handler = DesktopModeMoveToDisplayTransitionHandler(StubTransaction())
+ handler =
+ DesktopModeMoveToDisplayTransitionHandler(StubTransaction(), mock(), mock(), mock())
}
@Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopRepositoryTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopRepositoryTest.kt
index a10aeca95bce..6ede990df15e 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopRepositoryTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopRepositoryTest.kt
@@ -58,6 +58,7 @@ import org.mockito.Mockito.spy
import org.mockito.kotlin.any
import org.mockito.kotlin.clearInvocations
import org.mockito.kotlin.eq
+import org.mockito.kotlin.isNull
import org.mockito.kotlin.never
import org.mockito.kotlin.times
import org.mockito.kotlin.verify
@@ -331,6 +332,8 @@ class DesktopRepositoryTest(flags: FlagsParameterization) : ShellTestCase() {
visibleTasks = ArraySet(arrayOf(1)),
minimizedTasks = ArraySet(),
freeformTasksInZOrder = arrayListOf(),
+ leftTiledTask = null,
+ rightTiledTask = null,
)
verify(persistentRepository)
.addOrUpdateDesktop(
@@ -339,6 +342,8 @@ class DesktopRepositoryTest(flags: FlagsParameterization) : ShellTestCase() {
visibleTasks = ArraySet(arrayOf(1, 2)),
minimizedTasks = ArraySet(),
freeformTasksInZOrder = arrayListOf(),
+ leftTiledTask = null,
+ rightTiledTask = null,
)
}
}
@@ -358,6 +363,52 @@ class DesktopRepositoryTest(flags: FlagsParameterization) : ShellTestCase() {
}
@Test
+ @EnableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_PERSISTENCE)
+ fun leftTiledTask_updatedInRepoAndPersisted() {
+ runTest(StandardTestDispatcher()) {
+ repo.addLeftTiledTask(displayId = DEFAULT_DISPLAY, taskId = 1)
+
+ assertThat(repo.getLeftTiledTask(displayId = DEFAULT_DISPLAY)).isEqualTo(1)
+ verify(persistentRepository)
+ .addOrUpdateDesktop(
+ DEFAULT_USER_ID,
+ DEFAULT_DESKTOP_ID,
+ visibleTasks = ArraySet(),
+ minimizedTasks = ArraySet(),
+ freeformTasksInZOrder = arrayListOf(),
+ leftTiledTask = 1,
+ rightTiledTask = null,
+ )
+
+ repo.removeRightTiledTask(displayId = DEFAULT_DISPLAY)
+ assertThat(repo.getRightTiledTask(displayId = DEFAULT_DISPLAY)).isNull()
+ }
+ }
+
+ @Test
+ @EnableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_PERSISTENCE)
+ fun rightTiledTask_updatedInRepoAndPersisted() {
+ runTest(StandardTestDispatcher()) {
+ repo.addRightTiledTask(displayId = DEFAULT_DISPLAY, taskId = 1)
+
+ assertThat(repo.getRightTiledTask(displayId = DEFAULT_DISPLAY)).isEqualTo(1)
+ verify(persistentRepository)
+ .addOrUpdateDesktop(
+ DEFAULT_USER_ID,
+ DEFAULT_DESKTOP_ID,
+ visibleTasks = ArraySet(),
+ minimizedTasks = ArraySet(),
+ freeformTasksInZOrder = arrayListOf(),
+ leftTiledTask = null,
+ rightTiledTask = 1,
+ )
+
+ repo.removeLeftTiledTask(displayId = DEFAULT_DISPLAY)
+ assertThat(repo.getLeftTiledTask(displayId = DEFAULT_DISPLAY)).isNull()
+ }
+ }
+
+ @Test
fun isOnlyVisibleNonClosingTask_singleVisibleMinimizedTask() {
val taskId = 1
repo.addTask(DEFAULT_DISPLAY, taskId, isVisible = true)
@@ -663,6 +714,8 @@ class DesktopRepositoryTest(flags: FlagsParameterization) : ShellTestCase() {
visibleTasks = ArraySet(),
minimizedTasks = ArraySet(),
freeformTasksInZOrder = arrayListOf(5),
+ leftTiledTask = null,
+ rightTiledTask = null,
)
verify(persistentRepository)
.addOrUpdateDesktop(
@@ -671,6 +724,8 @@ class DesktopRepositoryTest(flags: FlagsParameterization) : ShellTestCase() {
visibleTasks = ArraySet(arrayOf(5)),
minimizedTasks = ArraySet(),
freeformTasksInZOrder = arrayListOf(6, 5),
+ leftTiledTask = null,
+ rightTiledTask = null,
)
verify(persistentRepository)
.addOrUpdateDesktop(
@@ -679,6 +734,8 @@ class DesktopRepositoryTest(flags: FlagsParameterization) : ShellTestCase() {
visibleTasks = ArraySet(arrayOf(5, 6)),
minimizedTasks = ArraySet(),
freeformTasksInZOrder = arrayListOf(7, 6, 5),
+ leftTiledTask = null,
+ rightTiledTask = null,
)
}
}
@@ -729,6 +786,8 @@ class DesktopRepositoryTest(flags: FlagsParameterization) : ShellTestCase() {
visibleTasks = ArraySet(),
minimizedTasks = ArraySet(),
freeformTasksInZOrder = arrayListOf(5),
+ leftTiledTask = null,
+ rightTiledTask = null,
)
verify(persistentRepository)
.addOrUpdateDesktop(
@@ -737,6 +796,8 @@ class DesktopRepositoryTest(flags: FlagsParameterization) : ShellTestCase() {
visibleTasks = ArraySet(arrayOf(5)),
minimizedTasks = ArraySet(),
freeformTasksInZOrder = arrayListOf(6, 5),
+ leftTiledTask = null,
+ rightTiledTask = null,
)
verify(persistentRepository)
.addOrUpdateDesktop(
@@ -745,6 +806,8 @@ class DesktopRepositoryTest(flags: FlagsParameterization) : ShellTestCase() {
visibleTasks = ArraySet(arrayOf(5, 6)),
minimizedTasks = ArraySet(),
freeformTasksInZOrder = arrayListOf(7, 6, 5),
+ leftTiledTask = null,
+ rightTiledTask = null,
)
verify(persistentRepository, times(2))
.addOrUpdateDesktop(
@@ -753,6 +816,8 @@ class DesktopRepositoryTest(flags: FlagsParameterization) : ShellTestCase() {
visibleTasks = ArraySet(arrayOf(5, 7)),
minimizedTasks = ArraySet(arrayOf(6)),
freeformTasksInZOrder = arrayListOf(7, 6, 5),
+ leftTiledTask = null,
+ rightTiledTask = null,
)
}
}
@@ -793,6 +858,8 @@ class DesktopRepositoryTest(flags: FlagsParameterization) : ShellTestCase() {
visibleTasks = ArraySet(),
minimizedTasks = ArraySet(),
freeformTasksInZOrder = arrayListOf(1),
+ leftTiledTask = null,
+ rightTiledTask = null,
)
verify(persistentRepository)
.addOrUpdateDesktop(
@@ -801,6 +868,8 @@ class DesktopRepositoryTest(flags: FlagsParameterization) : ShellTestCase() {
visibleTasks = ArraySet(),
minimizedTasks = ArraySet(),
freeformTasksInZOrder = ArrayList(),
+ leftTiledTask = null,
+ rightTiledTask = null,
)
}
}
@@ -830,6 +899,8 @@ class DesktopRepositoryTest(flags: FlagsParameterization) : ShellTestCase() {
visibleTasks = ArraySet(),
minimizedTasks = ArraySet(),
freeformTasksInZOrder = arrayListOf(1),
+ leftTiledTask = null,
+ rightTiledTask = null,
)
verify(persistentRepository)
.addOrUpdateDesktop(
@@ -838,6 +909,8 @@ class DesktopRepositoryTest(flags: FlagsParameterization) : ShellTestCase() {
visibleTasks = ArraySet(),
minimizedTasks = ArraySet(),
freeformTasksInZOrder = ArrayList(),
+ leftTiledTask = null,
+ rightTiledTask = null,
)
}
}
@@ -869,6 +942,8 @@ class DesktopRepositoryTest(flags: FlagsParameterization) : ShellTestCase() {
visibleTasks = ArraySet(),
minimizedTasks = ArraySet(),
freeformTasksInZOrder = arrayListOf(1),
+ leftTiledTask = null,
+ rightTiledTask = null,
)
verify(persistentRepository, never())
.addOrUpdateDesktop(
@@ -877,6 +952,8 @@ class DesktopRepositoryTest(flags: FlagsParameterization) : ShellTestCase() {
visibleTasks = ArraySet(),
minimizedTasks = ArraySet(),
freeformTasksInZOrder = ArrayList(),
+ leftTiledTask = null,
+ rightTiledTask = null,
)
}
}
@@ -1203,6 +1280,18 @@ class DesktopRepositoryTest(flags: FlagsParameterization) : ShellTestCase() {
@Test
@EnableFlags(FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+ fun removeDesk_multipleDesks_active_marksInactiveInDisplay() {
+ repo.addDesk(displayId = DEFAULT_DISPLAY, deskId = 2)
+ repo.addDesk(displayId = DEFAULT_DISPLAY, deskId = 3)
+ repo.setActiveDesk(displayId = DEFAULT_DISPLAY, deskId = 3)
+
+ repo.removeDesk(deskId = 3)
+
+ assertThat(repo.getActiveDeskId(displayId = DEFAULT_DISPLAY)).isNull()
+ }
+
+ @Test
+ @EnableFlags(FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
fun removeDesk_multipleDesks_inactive_removes() {
repo.addDesk(displayId = DEFAULT_DISPLAY, deskId = 2)
repo.addDesk(displayId = DEFAULT_DISPLAY, deskId = 3)
@@ -1215,6 +1304,18 @@ class DesktopRepositoryTest(flags: FlagsParameterization) : ShellTestCase() {
}
@Test
+ @EnableFlags(FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+ fun removeDesk_multipleDesks_inactive_keepsOtherDeskActiveInDisplay() {
+ repo.addDesk(displayId = DEFAULT_DISPLAY, deskId = 2)
+ repo.addDesk(displayId = DEFAULT_DISPLAY, deskId = 3)
+ repo.setActiveDesk(displayId = DEFAULT_DISPLAY, deskId = 3)
+
+ repo.removeDesk(deskId = 2)
+
+ assertThat(repo.getActiveDeskId(displayId = DEFAULT_DISPLAY)).isEqualTo(3)
+ }
+
+ @Test
@EnableFlags(FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND, FLAG_ENABLE_DESKTOP_WINDOWING_PERSISTENCE)
fun removeDesk_removesFromPersistence() =
runTest(StandardTestDispatcher()) {
@@ -1391,6 +1492,8 @@ class DesktopRepositoryTest(flags: FlagsParameterization) : ShellTestCase() {
visibleTasks = any(),
minimizedTasks = any(),
freeformTasksInZOrder = any(),
+ leftTiledTask = isNull(),
+ rightTiledTask = isNull(),
)
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
index 71e46bcb0698..b577667d8279 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
@@ -1526,6 +1526,37 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
}
@Test
+ fun launchIntent_taskInDesktopMode_onSecondaryDisplay_transitionStarted() {
+ setUpLandscapeDisplay()
+ taskRepository.addDesk(SECOND_DISPLAY, deskId = 2)
+ val intent = Intent().setComponent(homeComponentName)
+ whenever(
+ desktopMixedTransitionHandler.startLaunchTransition(
+ eq(TRANSIT_OPEN),
+ any(),
+ anyOrNull(),
+ anyOrNull(),
+ anyOrNull(),
+ )
+ )
+ .thenReturn(Binder())
+
+ controller.startLaunchIntentTransition(intent, Bundle.EMPTY, SECOND_DISPLAY)
+
+ val wct = getLatestDesktopMixedTaskWct(type = TRANSIT_OPEN)
+ // We expect two actions: open the app and start the desk
+ assertThat(wct.hierarchyOps).hasSize(2)
+ val hOps0 = wct.hierarchyOps[0]
+ val hOps1 = wct.hierarchyOps[1]
+ assertThat(hOps0.type).isEqualTo(HIERARCHY_OP_TYPE_PENDING_INTENT)
+ val activityOptions0 = ActivityOptions.fromBundle(hOps0.launchOptions)
+ assertThat(activityOptions0.launchDisplayId).isEqualTo(SECOND_DISPLAY)
+ assertThat(hOps1.type).isEqualTo(HIERARCHY_OP_TYPE_PENDING_INTENT)
+ val activityOptions1 = ActivityOptions.fromBundle(hOps1.launchOptions)
+ assertThat(activityOptions1.launchDisplayId).isEqualTo(SECOND_DISPLAY)
+ }
+
+ @Test
@EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
fun addMoveToDeskTaskChanges_landscapeDevice_userFullscreenOverride_defaultPortraitBounds() {
setUpLandscapeDisplay()
@@ -7615,6 +7646,106 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
assertNull(latestWct.hierarchyOps.find { op -> op.container == wallpaperToken.asBinder() })
}
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+ fun onRecentsInDesktopAnimationFinishing_returningToApp_noDeskDeactivation() {
+ val deskId = 0
+ taskRepository.setActiveDesk(DEFAULT_DISPLAY, deskId)
+
+ val transition = Binder()
+ val finishWct = WindowContainerTransaction()
+ controller.onRecentsInDesktopAnimationFinishing(
+ transition = transition,
+ finishWct = finishWct,
+ returnToApp = true,
+ )
+
+ verify(desksOrganizer, never()).deactivateDesk(finishWct, deskId)
+ verify(desksTransitionsObserver, never())
+ .addPendingTransition(
+ argThat { t -> t.token == transition && t is DeskTransition.DeactivateDesk }
+ )
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+ fun onRecentsInDesktopAnimationFinishing_noActiveDesk_noDeskDeactivation() {
+ val deskId = 0
+ taskRepository.setDeskInactive(deskId)
+
+ val transition = Binder()
+ val finishWct = WindowContainerTransaction()
+ controller.onRecentsInDesktopAnimationFinishing(
+ transition = transition,
+ finishWct = finishWct,
+ returnToApp = false,
+ )
+
+ verify(desksOrganizer, never()).deactivateDesk(finishWct, deskId)
+ verify(desksTransitionsObserver, never())
+ .addPendingTransition(
+ argThat { t -> t.token == transition && t is DeskTransition.DeactivateDesk }
+ )
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+ fun onRecentsInDesktopAnimationFinishing_activeDesk_notReturningToDesk_deactivatesDesk() {
+ val deskId = 0
+ taskRepository.setActiveDesk(DEFAULT_DISPLAY, deskId)
+
+ val transition = Binder()
+ val finishWct = WindowContainerTransaction()
+ controller.onRecentsInDesktopAnimationFinishing(
+ transition = transition,
+ finishWct = finishWct,
+ returnToApp = false,
+ )
+
+ verify(desksOrganizer).deactivateDesk(finishWct, deskId)
+ verify(desksTransitionsObserver)
+ .addPendingTransition(DeskTransition.DeactivateDesk(transition, deskId))
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+ fun onRecentsInDesktopAnimationFinishing_activeDesk_notReturningToDesk_notifiesDesktopExit() {
+ val deskId = 0
+ taskRepository.setActiveDesk(DEFAULT_DISPLAY, deskId)
+
+ val transition = Binder()
+ val finishWct = WindowContainerTransaction()
+ controller.onRecentsInDesktopAnimationFinishing(
+ transition = transition,
+ finishWct = finishWct,
+ returnToApp = false,
+ )
+
+ verify(desktopModeEnterExitTransitionListener).onExitDesktopModeTransitionStarted(any())
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+ fun onRecentsInDesktopAnimationFinishing_activeDesk_notReturningToDesk_doesNotBringUpWallpaperOrHome() {
+ val deskId = 0
+ taskRepository.setActiveDesk(DEFAULT_DISPLAY, deskId)
+
+ val transition = Binder()
+ val finishWct = WindowContainerTransaction()
+ controller.onRecentsInDesktopAnimationFinishing(
+ transition = transition,
+ finishWct = finishWct,
+ returnToApp = false,
+ )
+
+ finishWct.assertWithoutHop { hop ->
+ hop.type == HIERARCHY_OP_TYPE_REORDER &&
+ hop.container == wallpaperToken.asBinder() &&
+ !hop.toTop
+ }
+ finishWct.assertWithoutHop { hop -> hop.type == HIERARCHY_OP_TYPE_PENDING_INTENT }
+ }
+
private class RunOnStartTransitionCallback : ((IBinder) -> Unit) {
var invocations = 0
private set
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/VisualIndicatorViewContainerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/VisualIndicatorViewContainerTest.kt
index 3983bfbb2080..75f8d9e819cb 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/VisualIndicatorViewContainerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/VisualIndicatorViewContainerTest.kt
@@ -16,6 +16,7 @@
package com.android.wm.shell.desktopmode
+import android.animation.AnimatorTestRule
import android.app.ActivityManager
import android.app.ActivityManager.RunningTaskInfo
import android.graphics.Rect
@@ -29,6 +30,7 @@ import android.view.Display.DEFAULT_DISPLAY
import android.view.SurfaceControl
import android.view.SurfaceControlViewHost
import android.view.View
+import android.widget.FrameLayout
import androidx.test.filters.SmallTest
import com.android.window.flags.Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE
import com.android.wm.shell.ShellTestCase
@@ -43,6 +45,7 @@ import com.android.wm.shell.windowdecor.tiling.SnapEventHandler
import com.google.common.truth.Truth.assertThat
import kotlin.test.Test
import org.junit.Before
+import org.junit.Rule
import org.junit.runner.RunWith
import org.mockito.ArgumentMatchers.anyInt
import org.mockito.Mock
@@ -67,6 +70,9 @@ import org.mockito.kotlin.whenever
@RunWith(AndroidTestingRunner::class)
@EnableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
class VisualIndicatorViewContainerTest : ShellTestCase() {
+
+ @JvmField @Rule val animatorTestRule = AnimatorTestRule(this)
+
@Mock private lateinit var view: View
@Mock private lateinit var displayLayout: DisplayLayout
@Mock private lateinit var displayController: DisplayController
@@ -297,6 +303,95 @@ class VisualIndicatorViewContainerTest : ShellTestCase() {
verify(spyViewContainer, never()).fadeInIndicatorInternal(any(), any(), any(), any())
}
+ @Test
+ @EnableFlags(
+ com.android.wm.shell.Flags.FLAG_ENABLE_BUBBLE_TO_FULLSCREEN,
+ com.android.wm.shell.Flags.FLAG_ENABLE_CREATE_ANY_BUBBLE,
+ )
+ fun testCreateView_bubblesEnabled_indicatorIsFrameLayout() {
+ val spyViewContainer = setupSpyViewContainer()
+ assertThat(spyViewContainer.indicatorView).isInstanceOf(FrameLayout::class.java)
+ }
+
+ @Test
+ @EnableFlags(
+ com.android.wm.shell.Flags.FLAG_ENABLE_BUBBLE_TO_FULLSCREEN,
+ com.android.wm.shell.Flags.FLAG_ENABLE_CREATE_ANY_BUBBLE,
+ )
+ fun testFadeInOutBubbleIndicator_addAndRemoveBarIndicator() {
+ setUpBubbleBoundsProvider()
+ val spyViewContainer = setupSpyViewContainer()
+ spyViewContainer.fadeInIndicator(
+ displayLayout,
+ DesktopModeVisualIndicator.IndicatorType.TO_BUBBLE_RIGHT_INDICATOR,
+ DEFAULT_DISPLAY,
+ )
+ desktopExecutor.flushAll()
+ animatorTestRule.advanceTimeBy(200)
+ assertThat((spyViewContainer.indicatorView as FrameLayout).getChildAt(0)).isNotNull()
+
+ spyViewContainer.fadeOutIndicator(
+ displayLayout,
+ DesktopModeVisualIndicator.IndicatorType.TO_BUBBLE_RIGHT_INDICATOR,
+ finishCallback = null,
+ DEFAULT_DISPLAY,
+ snapEventHandler,
+ )
+ desktopExecutor.flushAll()
+ animatorTestRule.advanceTimeBy(250)
+ assertThat((spyViewContainer.indicatorView as FrameLayout).getChildAt(0)).isNull()
+ }
+
+ @Test
+ @EnableFlags(
+ com.android.wm.shell.Flags.FLAG_ENABLE_BUBBLE_TO_FULLSCREEN,
+ com.android.wm.shell.Flags.FLAG_ENABLE_CREATE_ANY_BUBBLE,
+ )
+ fun testTransitionIndicator_fullscreenToBubble_addBarIndicator() {
+ setUpBubbleBoundsProvider()
+ val spyViewContainer = setupSpyViewContainer()
+
+ spyViewContainer.transitionIndicator(
+ taskInfo,
+ displayController,
+ DesktopModeVisualIndicator.IndicatorType.TO_FULLSCREEN_INDICATOR,
+ DesktopModeVisualIndicator.IndicatorType.TO_BUBBLE_RIGHT_INDICATOR,
+ )
+ desktopExecutor.flushAll()
+ animatorTestRule.advanceTimeBy(200)
+
+ assertThat((spyViewContainer.indicatorView as FrameLayout).getChildAt(0)).isNotNull()
+ }
+
+ @Test
+ @EnableFlags(
+ com.android.wm.shell.Flags.FLAG_ENABLE_BUBBLE_TO_FULLSCREEN,
+ com.android.wm.shell.Flags.FLAG_ENABLE_CREATE_ANY_BUBBLE,
+ )
+ fun testTransitionIndicator_bubbleToFullscreen_removeBarIndicator() {
+ setUpBubbleBoundsProvider()
+ val spyViewContainer = setupSpyViewContainer()
+ spyViewContainer.fadeInIndicator(
+ displayLayout,
+ DesktopModeVisualIndicator.IndicatorType.TO_BUBBLE_RIGHT_INDICATOR,
+ DEFAULT_DISPLAY,
+ )
+ desktopExecutor.flushAll()
+ animatorTestRule.advanceTimeBy(200)
+ assertThat((spyViewContainer.indicatorView as FrameLayout).getChildAt(0)).isNotNull()
+
+ spyViewContainer.transitionIndicator(
+ taskInfo,
+ displayController,
+ DesktopModeVisualIndicator.IndicatorType.TO_BUBBLE_RIGHT_INDICATOR,
+ DesktopModeVisualIndicator.IndicatorType.TO_FULLSCREEN_INDICATOR,
+ )
+ desktopExecutor.flushAll()
+ animatorTestRule.advanceTimeBy(200)
+
+ assertThat((spyViewContainer.indicatorView as FrameLayout).getChildAt(0)).isNull()
+ }
+
private fun setupSpyViewContainer(): VisualIndicatorViewContainer {
val viewContainer =
VisualIndicatorViewContainer(
@@ -331,7 +426,22 @@ class VisualIndicatorViewContainerTest : ShellTestCase() {
.build()
}
+ private fun setUpBubbleBoundsProvider() {
+ bubbleDropTargetBoundsProvider =
+ object : BubbleDropTargetBoundsProvider {
+ override fun getBubbleBarExpandedViewDropTargetBounds(onLeft: Boolean): Rect {
+ return BUBBLE_INDICATOR_BOUNDS
+ }
+
+ override fun getBarDropTargetBounds(onLeft: Boolean): Rect {
+ return BAR_INDICATOR_BOUNDS
+ }
+ }
+ }
+
companion object {
private val DISPLAY_BOUNDS = Rect(0, 0, 1000, 1000)
+ private val BUBBLE_INDICATOR_BOUNDS = Rect(800, 200, 900, 900)
+ private val BAR_INDICATOR_BOUNDS = Rect(880, 950, 900, 960)
}
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/multidesks/DesksTransitionObserverTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/multidesks/DesksTransitionObserverTest.kt
index 409ca57715fc..e55d7cbb73e1 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/multidesks/DesksTransitionObserverTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/multidesks/DesksTransitionObserverTest.kt
@@ -264,6 +264,36 @@ class DesksTransitionObserverTest : ShellTestCase() {
@Test
@EnableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+ fun onTransitionFinish_deactivateDesk_updatesRepository() {
+ val transition = Binder()
+ val deactivateTransition = DeskTransition.DeactivateDesk(transition, deskId = 5)
+ repository.addDesk(DEFAULT_DISPLAY, deskId = 5)
+ repository.setActiveDesk(DEFAULT_DISPLAY, deskId = 5)
+
+ observer.addPendingTransition(deactivateTransition)
+ observer.onTransitionFinished(transition)
+
+ assertThat(repository.getActiveDeskId(DEFAULT_DISPLAY)).isNull()
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
+ fun onTransitionMergedAndFinished_deactivateDesk_updatesRepository() {
+ val transition = Binder()
+ val deactivateTransition = DeskTransition.DeactivateDesk(transition, deskId = 5)
+ repository.addDesk(DEFAULT_DISPLAY, deskId = 5)
+ repository.setActiveDesk(DEFAULT_DISPLAY, deskId = 5)
+
+ observer.addPendingTransition(deactivateTransition)
+ val bookEndTransition = Binder()
+ observer.onTransitionMerged(merged = transition, playing = bookEndTransition)
+ observer.onTransitionFinished(bookEndTransition)
+
+ assertThat(repository.getActiveDeskId(DEFAULT_DISPLAY)).isNull()
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND)
fun onTransitionReady_twoPendingTransitions_handlesBoth() {
val transition = Binder()
// Active one desk and deactivate another in different displays, such as in some
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/persistence/DesktopPersistentRepositoryTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/persistence/DesktopPersistentRepositoryTest.kt
index 5f92326b5e5e..6039b8f0edc5 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/persistence/DesktopPersistentRepositoryTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/persistence/DesktopPersistentRepositoryTest.kt
@@ -115,6 +115,8 @@ class DesktopPersistentRepositoryTest : ShellTestCase() {
minimizedTasks = minimizedTasks,
freeformTasksInZOrder = freeformTasksInZOrder,
userId = DEFAULT_USER_ID,
+ leftTiledTask = null,
+ rightTiledTask = null,
)
val actualDesktop = datastoreRepository.readDesktop(DEFAULT_USER_ID, DEFAULT_DESKTOP_ID)
@@ -124,6 +126,50 @@ class DesktopPersistentRepositoryTest : ShellTestCase() {
}
@Test
+ fun addTiledTasks_addsTiledTasksToDesktop() {
+ runTest(StandardTestDispatcher()) {
+ // Create a basic repository state
+ val task = createDesktopTask(1)
+ val desktopPersistentRepositories = createRepositoryWithOneDesk(task)
+ testDatastore.updateData { desktopPersistentRepositories }
+ // Create a new state to be initialized
+ val visibleTasks = ArraySet(listOf(1, 2))
+
+ // Update with new state
+ datastoreRepository.addOrUpdateDesktop(
+ visibleTasks = visibleTasks,
+ minimizedTasks = ArraySet(),
+ freeformTasksInZOrder = ArrayList(),
+ userId = DEFAULT_USER_ID,
+ leftTiledTask = 1,
+ rightTiledTask = null,
+ )
+
+ var actualDesktop = datastoreRepository.readDesktop(DEFAULT_USER_ID, DEFAULT_DESKTOP_ID)
+ assertThat(actualDesktop?.tasksByTaskIdMap?.get(1)?.desktopTaskTilingState)
+ .isEqualTo(DesktopTaskTilingState.LEFT)
+ assertThat(actualDesktop?.tasksByTaskIdMap?.get(2)?.desktopTaskTilingState)
+ .isEqualTo(DesktopTaskTilingState.NONE)
+
+ // Update with new state
+ datastoreRepository.addOrUpdateDesktop(
+ visibleTasks = visibleTasks,
+ minimizedTasks = ArraySet(),
+ freeformTasksInZOrder = ArrayList(),
+ userId = DEFAULT_USER_ID,
+ leftTiledTask = null,
+ rightTiledTask = 2,
+ )
+
+ actualDesktop = datastoreRepository.readDesktop(DEFAULT_USER_ID, DEFAULT_DESKTOP_ID)
+ assertThat(actualDesktop?.tasksByTaskIdMap?.get(1)?.desktopTaskTilingState)
+ .isEqualTo(DesktopTaskTilingState.NONE)
+ assertThat(actualDesktop?.tasksByTaskIdMap?.get(2)?.desktopTaskTilingState)
+ .isEqualTo(DesktopTaskTilingState.RIGHT)
+ }
+ }
+
+ @Test
fun removeUsers_removesUsersData() {
runTest(StandardTestDispatcher()) {
val task = createDesktopTask(1)
@@ -138,12 +184,16 @@ class DesktopPersistentRepositoryTest : ShellTestCase() {
minimizedTasks = minimizedTasks,
freeformTasksInZOrder = freeformTasksInZOrder,
userId = DEFAULT_USER_ID,
+ leftTiledTask = null,
+ rightTiledTask = null,
)
datastoreRepository.addOrUpdateDesktop(
visibleTasks = visibleTasks,
minimizedTasks = minimizedTasks,
freeformTasksInZOrder = freeformTasksInZOrder,
userId = USER_ID_2,
+ leftTiledTask = null,
+ rightTiledTask = null,
)
datastoreRepository.removeUsers(mutableListOf(USER_ID_2))
@@ -175,6 +225,8 @@ class DesktopPersistentRepositoryTest : ShellTestCase() {
minimizedTasks = minimizedTasks,
freeformTasksInZOrder = freeformTasksInZOrder,
userId = DEFAULT_USER_ID,
+ leftTiledTask = null,
+ rightTiledTask = null,
)
val actualDesktop = datastoreRepository.readDesktop(DEFAULT_USER_ID, DEFAULT_DESKTOP_ID)
@@ -200,6 +252,8 @@ class DesktopPersistentRepositoryTest : ShellTestCase() {
minimizedTasks = minimizedTasks,
freeformTasksInZOrder = freeformTasksInZOrder,
userId = DEFAULT_USER_ID,
+ leftTiledTask = null,
+ rightTiledTask = null,
)
val actualDesktop = datastoreRepository.readDesktop(DEFAULT_USER_ID, DEFAULT_DESKTOP_ID)
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/persistence/DesktopRepositoryInitializerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/persistence/DesktopRepositoryInitializerTest.kt
index 4440d4e801fe..601eb315f394 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/persistence/DesktopRepositoryInitializerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/persistence/DesktopRepositoryInitializerTest.kt
@@ -27,6 +27,7 @@ import com.android.window.flags.Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND
import com.android.wm.shell.ShellTestCase
import com.android.wm.shell.common.ShellExecutor
import com.android.wm.shell.desktopmode.DesktopUserRepositories
+import com.android.wm.shell.desktopmode.persistence.DesktopRepositoryInitializer.DeskRecreationFactory
import com.android.wm.shell.sysui.ShellController
import com.android.wm.shell.sysui.ShellInit
import com.google.common.truth.Truth.assertThat
@@ -242,6 +243,36 @@ class DesktopRepositoryInitializerTest : ShellTestCase() {
.inOrder()
}
+ @Test
+ @EnableFlags(
+ FLAG_ENABLE_DESKTOP_WINDOWING_PERSISTENCE,
+ FLAG_ENABLE_DESKTOP_WINDOWING_HSUM,
+ FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND,
+ )
+ fun initWithPersistence_deskRecreationFailed_deskNotAdded() =
+ runTest(StandardTestDispatcher()) {
+ whenever(persistentRepository.getUserDesktopRepositoryMap())
+ .thenReturn(mapOf(USER_ID_1 to desktopRepositoryState1))
+ whenever(persistentRepository.getDesktopRepositoryState(USER_ID_1))
+ .thenReturn(desktopRepositoryState1)
+ whenever(persistentRepository.readDesktop(USER_ID_1, DESKTOP_ID_1)).thenReturn(desktop1)
+ whenever(persistentRepository.readDesktop(USER_ID_1, DESKTOP_ID_2)).thenReturn(desktop2)
+
+ // Make [DESKTOP_ID_2] re-creation fail.
+ repositoryInitializer.deskRecreationFactory =
+ DeskRecreationFactory { userId, destinationDisplayId, deskId ->
+ if (deskId == DESKTOP_ID_2) {
+ null
+ } else {
+ deskId
+ }
+ }
+ repositoryInitializer.initialize(desktopUserRepositories)
+
+ assertThat(desktopUserRepositories.getProfile(USER_ID_1).getDeskIds(DEFAULT_DISPLAY))
+ .containsExactly(DESKTOP_ID_1)
+ }
+
@After
fun tearDown() {
datastoreScope.cancel()
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserverTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserverTest.java
index 714e5f486285..69a42164071b 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserverTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserverTest.java
@@ -373,6 +373,25 @@ public class FreeformTaskTransitionObserverTest extends ShellTestCase {
verify(mDesksTransitionObserver).onTransitionReady(transition, info);
}
+ @Test
+ public void onTransitionMerged_forwardsToDesksTransitionObserver() {
+ final IBinder merged = mock(IBinder.class);
+ final IBinder playing = mock(IBinder.class);
+
+ mTransitionObserver.onTransitionMerged(merged, playing);
+
+ verify(mDesksTransitionObserver).onTransitionMerged(merged, playing);
+ }
+
+ @Test
+ public void onTransitionFinished_forwardsToDesksTransitionObserver() {
+ final IBinder transition = mock(IBinder.class);
+
+ mTransitionObserver.onTransitionFinished(transition, /* aborted = */ false);
+
+ verify(mDesksTransitionObserver).onTransitionFinished(transition);
+ }
+
private static TransitionInfo.Change createChange(int mode, int taskId, int windowingMode) {
final ActivityManager.RunningTaskInfo taskInfo = new ActivityManager.RunningTaskInfo();
taskInfo.taskId = taskId;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/phone/PipTaskListenerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/phone/PipTaskListenerTest.java
index b6894fd0a9fa..a760890c9db3 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/phone/PipTaskListenerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip2/phone/PipTaskListenerTest.java
@@ -196,6 +196,7 @@ public class PipTaskListenerTest {
verifyNoMoreInteractions(mMockPipParamsChangedCallback);
verify(mMockPipTransitionState, times(0))
.setOnIdlePipTransitionStateRunnable(any(Runnable.class));
+ assertTrue(mPipTaskListener.getPictureInPictureParams().empty());
}
@Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/GroupedTaskInfoTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/GroupedTaskInfoTest.kt
index 75f6bda4d750..4e8812d34ef4 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/GroupedTaskInfoTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/GroupedTaskInfoTest.kt
@@ -21,6 +21,7 @@ import android.app.TaskInfo
import android.graphics.Rect
import android.os.Parcel
import android.testing.AndroidTestingRunner
+import android.view.Display.DEFAULT_DISPLAY
import android.window.IWindowContainerToken
import android.window.WindowContainerToken
import androidx.test.filters.SmallTest
@@ -281,7 +282,8 @@ class GroupedTaskInfoTest : ShellTestCase() {
val task2 = createTaskInfo(id = 2)
val taskInfo = GroupedTaskInfo.forDeskTasks(
- /* deskId = */ 500, listOf(task1, task2), setOf())
+ /* deskId = */ 500, DEFAULT_DISPLAY, listOf(task1, task2), setOf()
+ )
assertThat(taskInfo.deskId).isEqualTo(500)
assertThat(taskInfo.getTaskById(1)).isEqualTo(task1)
@@ -335,6 +337,7 @@ class GroupedTaskInfoTest : ShellTestCase() {
): GroupedTaskInfo {
return GroupedTaskInfo.forDeskTasks(
deskId,
+ DEFAULT_DISPLAY,
freeformTaskIds.map { createTaskInfo(it) }.toList(),
minimizedTaskIds.toSet())
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java
index 077b35583e04..f6e49853eddf 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java
@@ -629,9 +629,9 @@ public class StageCoordinatorTests extends ShellTestCase {
private Transitions createTestTransitions() {
ShellInit shellInit = new ShellInit(mMainExecutor);
final Transitions t = new Transitions(mContext, shellInit, mock(ShellController.class),
- mTaskOrganizer, mTransactionPool, mock(DisplayController.class), mMainExecutor,
- mMainHandler, mAnimExecutor, mock(HomeTransitionObserver.class),
- mock(FocusTransitionObserver.class));
+ mTaskOrganizer, mTransactionPool, mock(DisplayController.class),
+ mDisplayInsetsController, mMainExecutor, mMainHandler, mAnimExecutor,
+ mock(HomeTransitionObserver.class), mock(FocusTransitionObserver.class));
shellInit.init();
return t;
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/DefaultTransitionHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/DefaultTransitionHandlerTest.java
index 18fdbeff40f4..6996d44af034 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/DefaultTransitionHandlerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/DefaultTransitionHandlerTest.java
@@ -53,6 +53,7 @@ import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.TestShellExecutor;
import com.android.wm.shell.common.DisplayController;
+import com.android.wm.shell.common.DisplayInsetsController;
import com.android.wm.shell.shared.TransactionPool;
import com.android.wm.shell.sysui.ShellInit;
@@ -78,6 +79,8 @@ public class DefaultTransitionHandlerTest extends ShellTestCase {
InstrumentationRegistry.getInstrumentation().getTargetContext();
private final DisplayController mDisplayController = mock(DisplayController.class);
+ private final DisplayInsetsController mDisplayInsetsController =
+ mock(DisplayInsetsController.class);
private final TransactionPool mTransactionPool = new MockTransactionPool();
private final TestShellExecutor mMainExecutor = new TestShellExecutor();
private final TestShellExecutor mAnimExecutor = new TestShellExecutor();
@@ -95,7 +98,7 @@ public class DefaultTransitionHandlerTest extends ShellTestCase {
mContext,
mShellInit);
mTransitionHandler = new DefaultTransitionHandler(
- mContext, mShellInit, mDisplayController,
+ mContext, mShellInit, mDisplayController, mDisplayInsetsController,
mTransactionPool, mMainExecutor, mMainHandler, mAnimExecutor,
mRootTaskDisplayAreaOrganizer, mock(InteractionJankMonitor.class));
mShellInit.init();
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/HomeTransitionObserverTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/HomeTransitionObserverTest.java
index a122c3820dcb..52634c08dafd 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/HomeTransitionObserverTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/HomeTransitionObserverTest.java
@@ -61,6 +61,7 @@ import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.TestShellExecutor;
import com.android.wm.shell.common.DisplayController;
+import com.android.wm.shell.common.DisplayInsetsController;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.shared.IHomeTransitionListener;
import com.android.wm.shell.shared.TransactionPool;
@@ -88,6 +89,8 @@ public class HomeTransitionObserverTest extends ShellTestCase {
private final TestShellExecutor mMainExecutor = new TestShellExecutor();
private final Handler mMainHandler = new Handler(Looper.getMainLooper());
private final DisplayController mDisplayController = mock(DisplayController.class);
+ private final DisplayInsetsController mDisplayInsetsController =
+ mock(DisplayInsetsController.class);
private IHomeTransitionListener mListener;
private Transitions mTransition;
@@ -98,10 +101,11 @@ public class HomeTransitionObserverTest extends ShellTestCase {
mListener = mock(IHomeTransitionListener.class);
when(mListener.asBinder()).thenReturn(mock(IBinder.class));
- mHomeTransitionObserver = new HomeTransitionObserver(mContext, mMainExecutor);
+ mHomeTransitionObserver = new HomeTransitionObserver(mContext, mMainExecutor,
+ mDisplayInsetsController, mock(ShellInit.class));
mTransition = new Transitions(mContext, mock(ShellInit.class), mock(ShellController.class),
- mOrganizer, mTransactionPool, mDisplayController, mMainExecutor,
- mMainHandler, mAnimExecutor, mHomeTransitionObserver,
+ mOrganizer, mTransactionPool, mDisplayController, mDisplayInsetsController,
+ mMainExecutor, mMainHandler, mAnimExecutor, mHomeTransitionObserver,
mock(FocusTransitionObserver.class));
mHomeTransitionObserver.setHomeTransitionListener(mTransition, mListener);
}
@@ -222,7 +226,7 @@ public class HomeTransitionObserverTest extends ShellTestCase {
@Test
@EnableFlags({FLAG_ENABLE_DRAG_TO_DESKTOP_INCOMING_TRANSITIONS_BUGFIX})
- public void startDragToDesktopAborted_doesNotTriggerCallback() throws RemoteException {
+ public void startDragToDesktopAborted_triggersCallback() throws RemoteException {
TransitionInfo info = mock(TransitionInfo.class);
TransitionInfo.Change change = mock(TransitionInfo.Change.class);
ActivityManager.RunningTaskInfo taskInfo = mock(ActivityManager.RunningTaskInfo.class);
@@ -239,7 +243,7 @@ public class HomeTransitionObserverTest extends ShellTestCase {
mHomeTransitionObserver.onTransitionFinished(transition, /* aborted= */ true);
- verify(mListener, never()).onHomeVisibilityChanged(/* isVisible= */ anyBoolean());
+ verify(mListener).onHomeVisibilityChanged(/* isVisible= */ true);
}
@Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java
index 677330790bab..44bb2154f170 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java
@@ -37,6 +37,7 @@ import static android.view.WindowManager.TRANSIT_TO_BACK;
import static android.view.WindowManager.TRANSIT_TO_FRONT;
import static android.window.TransitionInfo.FLAG_DISPLAY_HAS_ALERT_WINDOWS;
import static android.window.TransitionInfo.FLAG_IS_DISPLAY;
+import static android.window.TransitionInfo.FLAG_MOVED_TO_TOP;
import static android.window.TransitionInfo.FLAG_SYNC;
import static android.window.TransitionInfo.FLAG_TRANSLUCENT;
@@ -106,6 +107,7 @@ import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.TestShellExecutor;
import com.android.wm.shell.common.DisplayController;
+import com.android.wm.shell.common.DisplayInsetsController;
import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.recents.IRecentsAnimationRunner;
@@ -144,6 +146,8 @@ public class ShellTransitionTests extends ShellTestCase {
private final ShellExecutor mAnimExecutor = new TestShellExecutor();
private final TestTransitionHandler mDefaultHandler = new TestTransitionHandler();
private final Handler mMainHandler = new Handler(Looper.getMainLooper());
+ private final DisplayInsetsController mDisplayInsets =
+ mock(DisplayInsetsController.class);
@Before
public void setUp() {
@@ -155,9 +159,9 @@ public class ShellTransitionTests extends ShellTestCase {
public void instantiate_addInitCallback() {
ShellInit shellInit = mock(ShellInit.class);
final Transitions t = new Transitions(mContext, shellInit, mock(ShellController.class),
- mOrganizer, mTransactionPool, createTestDisplayController(), mMainExecutor,
- mMainHandler, mAnimExecutor, mock(HomeTransitionObserver.class),
- mock(FocusTransitionObserver.class));
+ mOrganizer, mTransactionPool, createTestDisplayController(), mDisplayInsets,
+ mMainExecutor, mMainHandler, mAnimExecutor,
+ mock(HomeTransitionObserver.class), mock(FocusTransitionObserver.class));
// One from Transitions, one from RootTaskDisplayAreaOrganizer
verify(shellInit).addInitCallback(any(), eq(t));
verify(shellInit).addInitCallback(any(), isA(RootTaskDisplayAreaOrganizer.class));
@@ -168,9 +172,9 @@ public class ShellTransitionTests extends ShellTestCase {
ShellInit shellInit = new ShellInit(mMainExecutor);
ShellController shellController = mock(ShellController.class);
final Transitions t = new Transitions(mContext, shellInit, shellController,
- mOrganizer, mTransactionPool, createTestDisplayController(), mMainExecutor,
- mMainHandler, mAnimExecutor, mock(HomeTransitionObserver.class),
- mock(FocusTransitionObserver.class));
+ mOrganizer, mTransactionPool, createTestDisplayController(), mDisplayInsets,
+ mMainExecutor, mMainHandler, mAnimExecutor,
+ mock(HomeTransitionObserver.class), mock(FocusTransitionObserver.class));
shellInit.init();
verify(shellController, times(1)).addExternalInterface(
eq(IShellTransitions.DESCRIPTOR), any(), any());
@@ -1313,8 +1317,9 @@ public class ShellTransitionTests extends ShellTestCase {
ShellInit shellInit = new ShellInit(mMainExecutor);
final Transitions transitions =
new Transitions(mContext, shellInit, mock(ShellController.class), mOrganizer,
- mTransactionPool, createTestDisplayController(), mMainExecutor,
- mMainHandler, mAnimExecutor, mock(HomeTransitionObserver.class),
+ mTransactionPool, createTestDisplayController(), mDisplayInsets,
+ mMainExecutor, mMainHandler, mAnimExecutor,
+ mock(HomeTransitionObserver.class),
mock(FocusTransitionObserver.class));
final RecentTasksController mockRecentsTaskController = mock(RecentTasksController.class);
doReturn(mContext).when(mockRecentsTaskController).getContext();
@@ -1742,6 +1747,53 @@ public class ShellTransitionTests extends ShellTestCase {
eq(R.styleable.WindowAnimation_activityCloseEnterAnimation), anyBoolean());
}
+ @Test
+ public void testTransientHideWithMoveToTop() {
+ Transitions transitions = createTestTransitions();
+ transitions.replaceDefaultHandlerForTest(mDefaultHandler);
+ final TransitionAnimation transitionAnimation = new TransitionAnimation(mContext, false,
+ Transitions.TAG);
+ spyOn(transitionAnimation);
+
+ // Prepare for a TO_BACK transition
+ final RunningTaskInfo taskInfo = createTaskInfo(1);
+ final IBinder closeTransition = new Binder();
+ final SurfaceControl.Transaction closeTransitionFinishT =
+ mock(SurfaceControl.Transaction.class);
+
+ // Start a TO_BACK transition
+ transitions.requestStartTransition(closeTransition,
+ new TransitionRequestInfo(TRANSIT_TO_BACK, null /* trigger */, null /* remote */));
+ TransitionInfo closeInfo = new TransitionInfoBuilder(TRANSIT_TO_BACK)
+ .addChange(TRANSIT_TO_BACK, taskInfo)
+ .build();
+ transitions.onTransitionReady(closeTransition, closeInfo, new StubTransaction(),
+ closeTransitionFinishT);
+
+ // Verify that the transition hides the task surface in the finish transaction
+ verify(closeTransitionFinishT).hide(any());
+
+ // Prepare for a CHANGE transition
+ final IBinder changeTransition = new Binder();
+ final SurfaceControl.Transaction changeTransitionFinishT =
+ mock(SurfaceControl.Transaction.class);
+
+ // Start a CHANGE transition w/ MOVE_TO_FRONT that is merged into the TO_BACK
+ mDefaultHandler.setShouldMerge(changeTransition);
+ transitions.requestStartTransition(changeTransition,
+ new TransitionRequestInfo(TRANSIT_OPEN, null /* trigger */, null /* remote */));
+ TransitionInfo changeInfo = new TransitionInfoBuilder(TRANSIT_OPEN)
+ .addChange(TRANSIT_CHANGE, FLAG_MOVED_TO_TOP, taskInfo)
+ .build();
+ transitions.onTransitionReady(changeTransition, changeInfo, new StubTransaction(),
+ changeTransitionFinishT);
+
+ // Verify that the transition shows the task surface in the finish transaction so that the
+ // when the original transition finishes, the finish transaction does not clobber the
+ // visibility of the merged transition
+ verify(changeTransitionFinishT).show(any());
+ }
+
class TestTransitionHandler implements Transitions.TransitionHandler {
ArrayList<Pair<IBinder, Transitions.TransitionFinishCallback>> mFinishes =
new ArrayList<>();
@@ -1861,8 +1913,8 @@ public class ShellTransitionTests extends ShellTestCase {
private Transitions createTestTransitions() {
ShellInit shellInit = new ShellInit(mMainExecutor);
final Transitions t = new Transitions(mContext, shellInit, mock(ShellController.class),
- mOrganizer, mTransactionPool, createTestDisplayController(), mMainExecutor,
- mMainHandler, mAnimExecutor, mock(HomeTransitionObserver.class),
+ mOrganizer, mTransactionPool, createTestDisplayController(), mDisplayInsets,
+ mMainExecutor, mMainHandler, mAnimExecutor, mock(HomeTransitionObserver.class),
mock(FocusTransitionObserver.class));
shellInit.init();
return t;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTestsBase.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTestsBase.kt
index 4c9c2f14d805..2126d1d9b986 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTestsBase.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTestsBase.kt
@@ -55,6 +55,7 @@ import com.android.wm.shell.common.DisplayLayout
import com.android.wm.shell.common.MultiDisplayDragMoveIndicatorController
import com.android.wm.shell.common.MultiInstanceHelper
import com.android.wm.shell.common.SyncTransactionQueue
+import com.android.wm.shell.compatui.api.CompatUIHandler
import com.android.wm.shell.desktopmode.DesktopActivityOrientationChangeHandler
import com.android.wm.shell.desktopmode.DesktopImmersiveController
import com.android.wm.shell.desktopmode.DesktopModeEventLogger
@@ -144,6 +145,7 @@ open class DesktopModeWindowDecorViewModelTestsBase : ShellTestCase() {
mock<DesktopActivityOrientationChangeHandler>()
protected val mockMultiDisplayDragMoveIndicatorController =
mock<MultiDisplayDragMoveIndicatorController>()
+ protected val mockCompatUIHandler = mock<CompatUIHandler>()
protected val mockInputManager = mock<InputManager>()
private val mockTaskPositionerFactory =
mock<DesktopModeWindowDecorViewModel.TaskPositionerFactory>()
@@ -243,6 +245,7 @@ open class DesktopModeWindowDecorViewModelTestsBase : ShellTestCase() {
desktopModeCompatPolicy,
mockTilingWindowDecoration,
mockMultiDisplayDragMoveIndicatorController,
+ mockCompatUIHandler,
)
desktopModeWindowDecorViewModel.setSplitScreenController(mockSplitScreenController)
whenever(mockDisplayController.getDisplayLayout(any())).thenReturn(mockDisplayLayout)
@@ -334,7 +337,7 @@ open class DesktopModeWindowDecorViewModelTestsBase : ShellTestCase() {
mockDesktopModeWindowDecorFactory.create(
any(), any(), any(), any(), any(), any(), any(), eq(task), any(), any(), any(),
any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any(),
- any(), any(), any(), any())
+ any(), any(), any(), any(), any())
).thenReturn(decoration)
decoration.mTaskInfo = task
whenever(decoration.user).thenReturn(mockUserHandle)
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java
index f37f2fb14bea..0908f56e1cfb 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java
@@ -59,6 +59,7 @@ import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
+import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Point;
import android.graphics.Rect;
@@ -105,6 +106,7 @@ import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.desktopmode.CaptionState;
import com.android.wm.shell.desktopmode.DesktopModeEventLogger;
+import com.android.wm.shell.desktopmode.DesktopModeUiEventLogger;
import com.android.wm.shell.desktopmode.DesktopRepository;
import com.android.wm.shell.desktopmode.DesktopUserRepositories;
import com.android.wm.shell.desktopmode.WindowDecorCaptionHandleRepository;
@@ -245,6 +247,8 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
@Mock
private DesktopModeEventLogger mDesktopModeEventLogger;
@Mock
+ private DesktopModeUiEventLogger mDesktopModeUiEventLogger;
+ @Mock
private DesktopRepository mDesktopRepository;
@Mock
private WindowDecorTaskResourceLoader mMockTaskResourceLoader;
@@ -302,15 +306,15 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
doReturn(mInsetsState).when(mMockDisplayController).getInsetsState(anyInt());
when(mMockHandleMenuFactory.create(any(), any(), any(), any(), any(), anyInt(), any(),
anyBoolean(), anyBoolean(), anyBoolean(), anyBoolean(), anyBoolean(), anyBoolean(),
- any(), anyInt(), anyInt(), anyInt(), anyInt()))
+ anyBoolean(), any(), any(), anyInt(), anyInt(), anyInt(), anyInt()))
.thenReturn(mMockHandleMenu);
when(mMockMultiInstanceHelper.supportsMultiInstanceSplit(any(), anyInt()))
.thenReturn(false);
when(mMockAppHeaderViewHolderFactory
- .create(any(), any(), any(), any(), any(), any(), any(), any(), any()))
+ .create(any(), any(), any(), any(), any(), any(), any(), any(), any(), any()))
.thenReturn(mMockAppHeaderViewHolder);
when(mMockAppHandleViewHolderFactory
- .create(any(), any(), any(), any(), any()))
+ .create(any(), any(), any(), any(), any(), any()))
.thenReturn(mMockAppHandleViewHolder);
when(mMockDesktopUserRepositories.getCurrent()).thenReturn(mDesktopRepository);
when(mMockDesktopUserRepositories.getProfile(anyInt())).thenReturn(mDesktopRepository);
@@ -341,7 +345,8 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
}
@Test
- public void updateRelayoutParams_noSysPropFlagsSet_windowShadowsAreSetForFreeform() {
+ @DisableFlags(Flags.FLAG_ENABLE_DYNAMIC_RADIUS_COMPUTATION_BUGFIX)
+ public void updateRelayoutParams_noSysPropFlagsSet_windowShadowsAreSetForFreeform_dynamicDisabled() {
final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FREEFORM);
RelayoutParams relayoutParams = new RelayoutParams();
@@ -353,7 +358,8 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
}
@Test
- public void updateRelayoutParams_noSysPropFlagsSet_windowShadowsAreNotSetForFullscreen() {
+ @DisableFlags(Flags.FLAG_ENABLE_DYNAMIC_RADIUS_COMPUTATION_BUGFIX)
+ public void updateRelayoutParams_noSysPropFlagsSet_windowShadowsAreNotSetForFullscreen_dynamicDisabled() {
final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
RelayoutParams relayoutParams = new RelayoutParams();
@@ -364,7 +370,8 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
}
@Test
- public void updateRelayoutParams_noSysPropFlagsSet_windowShadowsAreNotSetForSplit() {
+ @DisableFlags(Flags.FLAG_ENABLE_DYNAMIC_RADIUS_COMPUTATION_BUGFIX)
+ public void updateRelayoutParams_noSysPropFlagsSet_windowShadowsAreNotSetForSplit_dynamicDisabled() {
final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
RelayoutParams relayoutParams = new RelayoutParams();
@@ -375,7 +382,8 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
}
@Test
- public void updateRelayoutParams_noSysPropFlagsSet_roundedCornersSetForFreeform() {
+ @DisableFlags(Flags.FLAG_ENABLE_DYNAMIC_RADIUS_COMPUTATION_BUGFIX)
+ public void updateRelayoutParams_noSysPropFlagsSet_roundedCornersSetForFreeform_dynamicDisabled() {
final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FREEFORM);
fillRoundedCornersResources(/* fillValue= */ 30);
@@ -387,7 +395,8 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
}
@Test
- public void updateRelayoutParams_noSysPropFlagsSet_roundedCornersNotSetForFullscreen() {
+ @DisableFlags(Flags.FLAG_ENABLE_DYNAMIC_RADIUS_COMPUTATION_BUGFIX)
+ public void updateRelayoutParams_noSysPropFlagsSet_roundedCornersNotSetForFullscreen_dynamicDisabled() {
final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
fillRoundedCornersResources(/* fillValue= */ 30);
@@ -399,7 +408,8 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
}
@Test
- public void updateRelayoutParams_noSysPropFlagsSet_roundedCornersNotSetForSplit() {
+ @DisableFlags(Flags.FLAG_ENABLE_DYNAMIC_RADIUS_COMPUTATION_BUGFIX)
+ public void updateRelayoutParams_noSysPropFlagsSet_roundedCornersNotSetForSplit_dynamicDisabled() {
final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
fillRoundedCornersResources(/* fillValue= */ 30);
@@ -411,7 +421,8 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
}
@Test
- public void updateRelayoutParams_shouldIgnoreCornerRadius_roundedCornersNotSet() {
+ @DisableFlags(Flags.FLAG_ENABLE_DYNAMIC_RADIUS_COMPUTATION_BUGFIX)
+ public void updateRelayoutParams_shouldIgnoreCornerRadius_roundedCornersNotSet_dynamicDisabled() {
final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FREEFORM);
fillRoundedCornersResources(/* fillValue= */ 30);
@@ -440,6 +451,107 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
}
@Test
+ @EnableFlags(Flags.FLAG_ENABLE_DYNAMIC_RADIUS_COMPUTATION_BUGFIX)
+ public void updateRelayoutParams_noSysPropFlagsSet_windowShadowsAreSetForFreeform() {
+ final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
+ taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FREEFORM);
+ RelayoutParams relayoutParams = new RelayoutParams();
+
+ updateRelayoutParams(relayoutParams, taskInfo);
+
+ assertThat(relayoutParams.mShadowRadiusId).isNotEqualTo(Resources.ID_NULL);
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_DYNAMIC_RADIUS_COMPUTATION_BUGFIX)
+ public void updateRelayoutParams_noSysPropFlagsSet_windowShadowsAreNotSetForFullscreen() {
+ final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
+ taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+ RelayoutParams relayoutParams = new RelayoutParams();
+
+ updateRelayoutParams(relayoutParams, taskInfo);
+
+ assertThat(relayoutParams.mShadowRadiusId).isEqualTo(Resources.ID_NULL);
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_DYNAMIC_RADIUS_COMPUTATION_BUGFIX)
+ public void updateRelayoutParams_noSysPropFlagsSet_windowShadowsAreNotSetForSplit() {
+ final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
+ taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
+ RelayoutParams relayoutParams = new RelayoutParams();
+
+ updateRelayoutParams(relayoutParams, taskInfo);
+
+ assertThat(relayoutParams.mShadowRadiusId).isEqualTo(Resources.ID_NULL);
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_DYNAMIC_RADIUS_COMPUTATION_BUGFIX)
+ public void updateRelayoutParams_noSysPropFlagsSet_roundedCornersSetForFreeform() {
+ final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
+ taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FREEFORM);
+ RelayoutParams relayoutParams = new RelayoutParams();
+
+ updateRelayoutParams(relayoutParams, taskInfo);
+
+ assertThat(relayoutParams.mShadowRadiusId).isNotEqualTo(Resources.ID_NULL);
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_DYNAMIC_RADIUS_COMPUTATION_BUGFIX)
+ public void updateRelayoutParams_noSysPropFlagsSet_roundedCornersNotSetForFullscreen() {
+ final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
+ taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+ RelayoutParams relayoutParams = new RelayoutParams();
+
+ updateRelayoutParams(relayoutParams, taskInfo);
+
+ assertThat(relayoutParams.mCornerRadiusId).isEqualTo(Resources.ID_NULL);
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_DYNAMIC_RADIUS_COMPUTATION_BUGFIX)
+ public void updateRelayoutParams_noSysPropFlagsSet_roundedCornersNotSetForSplit() {
+ final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
+ taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
+ RelayoutParams relayoutParams = new RelayoutParams();
+
+ updateRelayoutParams(relayoutParams, taskInfo);
+
+ assertThat(relayoutParams.mCornerRadiusId).isEqualTo(Resources.ID_NULL);
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_DYNAMIC_RADIUS_COMPUTATION_BUGFIX)
+ public void updateRelayoutParams_shouldIgnoreCornerRadius_roundedCornersNotSet() {
+ final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
+ taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FREEFORM);
+ RelayoutParams relayoutParams = new RelayoutParams();
+
+ DesktopModeWindowDecoration.updateRelayoutParams(
+ relayoutParams,
+ mTestableContext,
+ taskInfo,
+ mMockSplitScreenController,
+ DEFAULT_APPLY_START_TRANSACTION_ON_DRAW,
+ DEFAULT_SHOULD_SET_TASK_POSITIONING_AND_CROP,
+ DEFAULT_IS_STATUSBAR_VISIBLE,
+ DEFAULT_IS_KEYGUARD_VISIBLE_AND_OCCLUDED,
+ DEFAULT_IS_IN_FULL_IMMERSIVE_MODE,
+ DEFAULT_IS_DRAGGING,
+ new InsetsState(),
+ DEFAULT_HAS_GLOBAL_FOCUS,
+ mExclusionRegion,
+ /* shouldIgnoreCornerRadius= */ true,
+ DEFAULT_SHOULD_EXCLUDE_CAPTION_FROM_APP_BOUNDS,
+ DEFAULT_IS_RECENTS_TRANSITION_RUNNING,
+ DEFAULT_IS_MOVING_TO_BACK);
+
+ assertThat(relayoutParams.mCornerRadiusId).isEqualTo(Resources.ID_NULL);
+ }
+
+ @Test
@EnableFlags(Flags.FLAG_ENABLE_APP_HEADER_WITH_TASK_DENSITY)
public void updateRelayoutParams_appHeader_usesTaskDensity() {
final int systemDensity = mTestableContext.getOrCreateTestableResources().getResources()
@@ -1341,6 +1453,7 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
any(),
any(),
any(),
+ any(),
anyBoolean()
);
// Run runnable to set captured link to used
@@ -1378,6 +1491,7 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
any(),
any(),
any(),
+ any(),
anyBoolean()
);
openInBrowserCaptor.getValue().invoke(new Intent(Intent.ACTION_MAIN, TEST_URI1));
@@ -1410,6 +1524,7 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
any(),
any(),
any(),
+ any(),
anyBoolean()
);
@@ -1475,6 +1590,7 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
any(),
any(),
any(),
+ any(),
closeClickListener.capture(),
any(),
anyBoolean()
@@ -1508,6 +1624,7 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
any(),
any(),
any(),
+ any(),
/* forceShowSystemBars= */ eq(true)
);
}
@@ -1681,9 +1798,9 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
private void verifyHandleMenuCreated(@Nullable Uri uri) {
verify(mMockHandleMenuFactory).create(any(), any(), any(), any(), any(), anyInt(),
any(), anyBoolean(), anyBoolean(), anyBoolean(), anyBoolean(), anyBoolean(),
- anyBoolean(), argThat(intent ->
+ anyBoolean(), anyBoolean(), argThat(intent ->
(uri == null && intent == null) || intent.getData().equals(uri)),
- anyInt(), anyInt(), anyInt(), anyInt());
+ any(), anyInt(), anyInt(), anyInt(), anyInt());
}
private void createMaximizeMenu(DesktopModeWindowDecoration decoration) {
@@ -1780,7 +1897,7 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
new WindowManagerWrapper(mMockWindowManager), mMockSurfaceControlViewHostFactory,
mMockWindowDecorViewHostSupplier, maximizeMenuFactory, mMockHandleMenuFactory,
mMockMultiInstanceHelper, mMockCaptionHandleRepository, mDesktopModeEventLogger,
- mDesktopModeCompatPolicy);
+ mDesktopModeUiEventLogger, mDesktopModeCompatPolicy);
windowDecor.setCaptionListeners(mMockTouchEventListener, mMockTouchEventListener,
mMockTouchEventListener, mMockTouchEventListener);
windowDecor.setExclusionRegionListener(mMockExclusionRegionListener);
@@ -1807,6 +1924,7 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
.setTaskDescriptionBuilder(taskDescriptionBuilder)
.setVisible(visible)
.build();
+ taskInfo.isVisibleRequested = visible;
taskInfo.realActivity = new ComponentName("com.android.wm.shell.windowdecor",
"DesktopModeWindowDecorationTests");
taskInfo.baseActivity = new ComponentName("com.android.wm.shell.windowdecor",
@@ -1905,7 +2023,8 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase {
@NonNull Context decorWindowContext,
@NonNull Function2<? super Integer,? super Integer,? extends Point>
positionSupplier,
- @NonNull Supplier<SurfaceControl.Transaction> transactionSupplier) {
+ @NonNull Supplier<SurfaceControl.Transaction> transactionSupplier,
+ @NonNull DesktopModeUiEventLogger desktopModeUiEventLogger) {
return mMaximizeMenu;
}
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/HandleMenuTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/HandleMenuTest.kt
index 2e46f6312d03..b0b95c97ae18 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/HandleMenuTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/HandleMenuTest.kt
@@ -43,6 +43,7 @@ import com.android.wm.shell.ShellTestCase
import com.android.wm.shell.TestRunningTaskInfoBuilder
import com.android.wm.shell.common.DisplayController
import com.android.wm.shell.common.DisplayLayout
+import com.android.wm.shell.desktopmode.DesktopModeUiEventLogger
import com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT
import com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT
import com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_POSITION_UNDEFINED
@@ -95,6 +96,8 @@ class HandleMenuTest : ShellTestCase() {
private lateinit var mockTaskResourceLoader: WindowDecorTaskResourceLoader
@Mock
private lateinit var mockAppIcon: Bitmap
+ @Mock
+ private lateinit var mockDesktopModeUiEventLogger: DesktopModeUiEventLogger
private lateinit var handleMenu: HandleMenu
@@ -287,8 +290,10 @@ class HandleMenuTest : ShellTestCase() {
shouldShowManageWindowsButton = false,
shouldShowChangeAspectRatioButton = false,
shouldShowDesktopModeButton = true,
+ shouldShowRestartButton = true,
isBrowserApp = false,
null /* openInAppOrBrowserIntent */,
+ mockDesktopModeUiEventLogger,
captionWidth = HANDLE_WIDTH,
captionHeight = 50,
captionX = captionX,
@@ -304,6 +309,7 @@ class HandleMenuTest : ShellTestCase() {
onChangeAspectRatioClickListener = mock(),
openInAppOrBrowserClickListener = mock(),
onOpenByDefaultClickListener = mock(),
+ onRestartClickListener = mock(),
onCloseMenuClickListener = mock(),
onOutsideTouchListener = mock(),
forceShowSystemBars = forceShowSystemBars
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/MultiDisplayVeiledResizeTaskPositionerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/MultiDisplayVeiledResizeTaskPositionerTest.kt
index 0798613ed632..24a46aacde15 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/MultiDisplayVeiledResizeTaskPositionerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/MultiDisplayVeiledResizeTaskPositionerTest.kt
@@ -63,6 +63,7 @@ import org.mockito.Mockito.mock
import org.mockito.Mockito.never
import org.mockito.Mockito.times
import org.mockito.Mockito.verify
+import org.mockito.Mockito.verifyNoInteractions
import org.mockito.Mockito.`when`
import org.mockito.Mockito.`when` as whenever
import org.mockito.MockitoAnnotations
@@ -210,6 +211,7 @@ class MultiDisplayVeiledResizeTaskPositionerTest : ShellTestCase() {
eq(taskPositioner),
)
verify(mockDesktopWindowDecoration, never()).hideResizeVeil()
+ verifyNoInteractions(mockMultiDisplayDragMoveIndicatorController)
}
@Test
@@ -248,6 +250,7 @@ class MultiDisplayVeiledResizeTaskPositionerTest : ShellTestCase() {
verify(mockDesktopWindowDecoration, never()).showResizeVeil(any())
verify(mockDesktopWindowDecoration, never()).hideResizeVeil()
+ verify(mockMultiDisplayDragMoveIndicatorController).onDragEnd(eq(TASK_ID), any())
Assert.assertEquals(rectAfterEnd, endBounds)
}
@@ -268,6 +271,7 @@ class MultiDisplayVeiledResizeTaskPositionerTest : ShellTestCase() {
verify(spyDisplayLayout0, never()).localPxToGlobalDp(any(), any())
verify(spyDisplayLayout0, never()).globalDpToLocalPx(any(), any())
+ verify(mockMultiDisplayDragMoveIndicatorController).onDragEnd(eq(TASK_ID), any())
}
@Test
@@ -290,6 +294,7 @@ class MultiDisplayVeiledResizeTaskPositionerTest : ShellTestCase() {
verify(mockDesktopWindowDecoration, never()).showResizeVeil(any())
verify(mockDesktopWindowDecoration, never()).hideResizeVeil()
+ verify(mockMultiDisplayDragMoveIndicatorController).onDragEnd(eq(TASK_ID), any())
Assert.assertEquals(rectAfterEnd, endBounds)
}
@@ -346,6 +351,7 @@ class MultiDisplayVeiledResizeTaskPositionerTest : ShellTestCase() {
},
eq(taskPositioner),
)
+ verifyNoInteractions(mockMultiDisplayDragMoveIndicatorController)
}
@Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java
index 2e95a979220c..c691dc72b1ea 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java
@@ -61,7 +61,8 @@ import android.graphics.Rect;
import android.graphics.Region;
import android.os.Handler;
import android.os.LocaleList;
-import android.testing.AndroidTestingRunner;
+import android.platform.test.annotations.UsesFlags;
+import android.platform.test.flag.junit.FlagsParameterization;
import android.util.DisplayMetrics;
import android.view.AttachedSurfaceControl;
import android.view.Display;
@@ -78,6 +79,7 @@ import android.window.WindowContainerTransaction;
import androidx.test.filters.SmallTest;
+import com.android.window.flags.Flags;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.TestRunningTaskInfoBuilder;
@@ -96,6 +98,9 @@ import org.mockito.InOrder;
import org.mockito.Mock;
import org.mockito.Mockito;
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4;
+import platform.test.runner.parameterized.Parameters;
+
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
@@ -108,7 +113,8 @@ import java.util.function.Supplier;
* atest WMShellUnitTests:WindowDecorationTests
*/
@SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(ParameterizedAndroidJunit4.class)
+@UsesFlags(com.android.window.flags.Flags.class)
public class WindowDecorationTests extends ShellTestCase {
private static final Rect TASK_BOUNDS = new Rect(100, 300, 400, 400);
private static final Point TASK_POSITION_IN_PARENT = new Point(40, 60);
@@ -116,6 +122,12 @@ public class WindowDecorationTests extends ShellTestCase {
private static final int SHADOW_RADIUS = 10;
private static final int STATUS_BAR_INSET_SOURCE_ID = 0;
+ @Parameters(name = "{0}")
+ public static List<FlagsParameterization> getParams() {
+ return FlagsParameterization.allCombinationsOf(
+ Flags.FLAG_ENABLE_DYNAMIC_RADIUS_COMPUTATION_BUGFIX);
+ }
+
private final WindowDecoration.RelayoutResult<TestView> mRelayoutResult =
new WindowDecoration.RelayoutResult<>();
@@ -156,6 +168,10 @@ public class WindowDecorationTests extends ShellTestCase {
private WindowDecoration.RelayoutParams mRelayoutParams = new WindowDecoration.RelayoutParams();
private int mCaptionMenuWidthId;
+ public WindowDecorationTests(FlagsParameterization flags) {
+ mSetFlagsRule.setFlagsParameterization(flags);
+ }
+
@Before
public void setUp() {
mMockSurfaceControlStartT = createMockSurfaceControlTransaction();
@@ -165,8 +181,13 @@ public class WindowDecorationTests extends ShellTestCase {
mRelayoutParams.mLayoutResId = 0;
mRelayoutParams.mCaptionHeightId = R.dimen.test_freeform_decor_caption_height;
mCaptionMenuWidthId = R.dimen.test_freeform_decor_caption_menu_width;
- mRelayoutParams.mShadowRadius = SHADOW_RADIUS;
- mRelayoutParams.mCornerRadius = CORNER_RADIUS;
+ if (Flags.enableDynamicRadiusComputationBugfix()) {
+ mRelayoutParams.mShadowRadiusId = R.dimen.test_freeform_shadow_radius;
+ mRelayoutParams.mCornerRadiusId = R.dimen.test_freeform_corner_radius;
+ } else {
+ mRelayoutParams.mShadowRadius = SHADOW_RADIUS;
+ mRelayoutParams.mCornerRadius = CORNER_RADIUS;
+ }
when(mMockDisplayController.getDisplay(Display.DEFAULT_DISPLAY))
.thenReturn(mock(Display.class));
@@ -282,9 +303,21 @@ public class WindowDecorationTests extends ShellTestCase {
any(),
anyInt());
- verify(mMockSurfaceControlStartT).setCornerRadius(mMockTaskSurface, CORNER_RADIUS);
- verify(mMockSurfaceControlFinishT).setCornerRadius(mMockTaskSurface, CORNER_RADIUS);
- verify(mMockSurfaceControlStartT).setShadowRadius(mMockTaskSurface, SHADOW_RADIUS);
+ if (Flags.enableDynamicRadiusComputationBugfix()) {
+ final int cornerRadius = WindowDecoration.loadDimensionPixelSize(
+ windowDecor.mDecorWindowContext.getResources(),
+ mRelayoutParams.mCornerRadiusId);
+ verify(mMockSurfaceControlStartT).setCornerRadius(mMockTaskSurface, cornerRadius);
+ verify(mMockSurfaceControlFinishT).setCornerRadius(mMockTaskSurface, cornerRadius);
+ final int shadowRadius = WindowDecoration.loadDimensionPixelSize(
+ windowDecor.mDecorWindowContext.getResources(),
+ mRelayoutParams.mShadowRadiusId);
+ verify(mMockSurfaceControlStartT).setShadowRadius(mMockTaskSurface, shadowRadius);
+ } else {
+ verify(mMockSurfaceControlStartT).setCornerRadius(mMockTaskSurface, CORNER_RADIUS);
+ verify(mMockSurfaceControlFinishT).setCornerRadius(mMockTaskSurface, CORNER_RADIUS);
+ verify(mMockSurfaceControlStartT).setShadowRadius(mMockTaskSurface, SHADOW_RADIUS);
+ }
assertEquals(300, mRelayoutResult.mWidth);
assertEquals(100, mRelayoutResult.mHeight);
@@ -1198,7 +1231,8 @@ public class WindowDecorationTests extends ShellTestCase {
}
@Override
- public void setTaskFocusState(boolean focused) {}
+ public void setTaskFocusState(boolean focused) {
+ }
}
private class TestWindowDecoration extends WindowDecoration<TestView> {
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingDecorViewModelTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingDecorViewModelTest.kt
index 646ec21cab9b..9e148e57260b 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingDecorViewModelTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingDecorViewModelTest.kt
@@ -28,6 +28,7 @@ import com.android.wm.shell.common.DisplayLayout
import com.android.wm.shell.common.ShellExecutor
import com.android.wm.shell.common.SyncTransactionQueue
import com.android.wm.shell.desktopmode.DesktopModeEventLogger
+import com.android.wm.shell.desktopmode.DesktopRepository
import com.android.wm.shell.desktopmode.DesktopTasksController
import com.android.wm.shell.desktopmode.DesktopTestHelpers.createFreeformTask
import com.android.wm.shell.desktopmode.DesktopUserRepositories
@@ -63,6 +64,7 @@ class DesktopTilingDecorViewModelTest : ShellTestCase() {
private val transitionsMock: Transitions = mock()
private val shellTaskOrganizerMock: ShellTaskOrganizer = mock()
private val userRepositories: DesktopUserRepositories = mock()
+ private val desktopRepository: DesktopRepository = mock()
private val desktopModeEventLogger: DesktopModeEventLogger = mock()
private val toggleResizeDesktopTaskTransitionHandlerMock:
ToggleResizeDesktopTaskTransitionHandler =
@@ -105,6 +107,7 @@ class DesktopTilingDecorViewModelTest : ShellTestCase() {
whenever(contextMock.createContextAsUser(any(), any())).thenReturn(context)
whenever(contextMock.resources).thenReturn(resourcesMock)
whenever(resourcesMock.getDimensionPixelSize(any())).thenReturn(10)
+ whenever(userRepositories.current).thenReturn(desktopRepository)
}
@Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingWindowDecorationTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingWindowDecorationTest.kt
index e4424f3c57f2..0adca5e864bf 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingWindowDecorationTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingWindowDecorationTest.kt
@@ -587,6 +587,31 @@ class DesktopTilingWindowDecorationTest : ShellTestCase() {
}
@Test
+ fun tilingDivider_shouldBeShown_whenTiledTasksBecomeVisible() {
+ val task1 = createVisibleTask()
+ val task2 = createVisibleTask()
+ val additionalTaskHelper: DesktopTilingWindowDecoration.AppResizingHelper = mock()
+ whenever(tiledTaskHelper.taskInfo).thenReturn(task1)
+ whenever(tiledTaskHelper.desktopModeWindowDecoration).thenReturn(desktopWindowDecoration)
+ whenever(additionalTaskHelper.taskInfo).thenReturn(task2)
+ whenever(additionalTaskHelper.desktopModeWindowDecoration)
+ .thenReturn(desktopWindowDecoration)
+
+ tilingDecoration.leftTaskResizingHelper = tiledTaskHelper
+ tilingDecoration.rightTaskResizingHelper = additionalTaskHelper
+ tilingDecoration.desktopTilingDividerWindowManager = desktopTilingDividerWindowManager
+ val changeInfo = createTransitFrontTransition(task1, task2)
+ tilingDecoration.onTransitionReady(
+ transition = mock(),
+ info = changeInfo,
+ startTransaction = mock(),
+ finishTransaction = mock(),
+ )
+
+ verify(desktopTilingDividerWindowManager, times(1)).showDividerBar()
+ }
+
+ @Test
fun taskNotTiled_shouldNotBeRemoved_whenNotTiled() {
val task1 = createVisibleTask()
val task2 = createVisibleTask()
@@ -653,6 +678,64 @@ class DesktopTilingWindowDecorationTest : ShellTestCase() {
verify(context, never()).getApplicationContext()
}
+ @Test
+ fun addLeftTiledTask_updatesTaskRepository_whenLeftTileInitializedOrBroken() {
+ val task1 = createVisibleTask()
+ val stableBounds = STABLE_BOUNDS_MOCK
+ whenever(displayController.getDisplayLayout(any())).thenReturn(displayLayout)
+ whenever(displayLayout.getStableBounds(any())).thenAnswer { i ->
+ (i.arguments.first() as Rect).set(stableBounds)
+ }
+ whenever(context.resources).thenReturn(resources)
+ whenever(resources.getDimensionPixelSize(any())).thenReturn(split_divider_width)
+ whenever(tiledTaskHelper.taskInfo).thenReturn(task1)
+ whenever(tiledTaskHelper.desktopModeWindowDecoration).thenReturn(desktopWindowDecoration)
+
+ tilingDecoration.onAppTiled(
+ task1,
+ desktopWindowDecoration,
+ DesktopTasksController.SnapPosition.LEFT,
+ BOUNDS,
+ )
+
+ verify(desktopRepository, times(1)).addLeftTiledTask(displayId, task1.taskId)
+ verify(desktopRepository, never()).addRightTiledTask(displayId, task1.taskId)
+
+ tilingDecoration.removeTaskIfTiled(task1.taskId)
+
+ verify(desktopRepository, times(1)).removeLeftTiledTask(displayId)
+ verify(desktopRepository, never()).removeRightTiledTask(displayId)
+ }
+
+ @Test
+ fun addRightTiledTask_updatesTaskRepository_whenRightTileInitializedOrBroken() {
+ val task1 = createVisibleTask()
+ val stableBounds = STABLE_BOUNDS_MOCK
+ whenever(displayController.getDisplayLayout(any())).thenReturn(displayLayout)
+ whenever(displayLayout.getStableBounds(any())).thenAnswer { i ->
+ (i.arguments.first() as Rect).set(stableBounds)
+ }
+ whenever(context.resources).thenReturn(resources)
+ whenever(resources.getDimensionPixelSize(any())).thenReturn(split_divider_width)
+ whenever(tiledTaskHelper.taskInfo).thenReturn(task1)
+ whenever(tiledTaskHelper.desktopModeWindowDecoration).thenReturn(desktopWindowDecoration)
+
+ tilingDecoration.onAppTiled(
+ task1,
+ desktopWindowDecoration,
+ DesktopTasksController.SnapPosition.RIGHT,
+ BOUNDS,
+ )
+
+ verify(desktopRepository, times(1)).addRightTiledTask(displayId, task1.taskId)
+ verify(desktopRepository, never()).addLeftTiledTask(displayId, task1.taskId)
+
+ tilingDecoration.removeTaskIfTiled(task1.taskId)
+
+ verify(desktopRepository, times(1)).removeRightTiledTask(displayId)
+ verify(desktopRepository, never()).removeLeftTiledTask(displayId)
+ }
+
private fun initTiledTaskHelperMock(taskInfo: ActivityManager.RunningTaskInfo) {
whenever(tiledTaskHelper.bounds).thenReturn(BOUNDS)
whenever(tiledTaskHelper.taskInfo).thenReturn(taskInfo)
@@ -704,6 +787,30 @@ class DesktopTilingWindowDecorationTest : ShellTestCase() {
)
}
+ private fun createTransitFrontTransition(
+ task1: RunningTaskInfo?,
+ task2: RunningTaskInfo?,
+ type: Int = TRANSIT_TO_FRONT,
+ ) =
+ TransitionInfo(type, /* flags= */ 0).apply {
+ addChange(
+ Change(mock(), mock()).apply {
+ mode = TRANSIT_TO_FRONT
+ parent = null
+ taskInfo = task1
+ flags = flags
+ }
+ )
+ addChange(
+ Change(mock(), mock()).apply {
+ mode = TRANSIT_TO_FRONT
+ parent = null
+ taskInfo = task2
+ flags = flags
+ }
+ )
+ }
+
companion object {
private val NON_STABLE_BOUNDS_MOCK = Rect(50, 55, 100, 100)
private val STABLE_BOUNDS_MOCK = Rect(0, 0, 100, 100)
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/viewholder/AppHandleViewHolderTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/viewholder/AppHandleViewHolderTest.kt
index bc4865a07f7f..e23019a59307 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/viewholder/AppHandleViewHolderTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/viewholder/AppHandleViewHolderTest.kt
@@ -26,6 +26,7 @@ import androidx.test.filters.SmallTest
import com.android.internal.policy.SystemBarUtils
import com.android.wm.shell.R
import com.android.wm.shell.ShellTestCase
+import com.android.wm.shell.desktopmode.DesktopModeUiEventLogger
import com.android.wm.shell.windowdecor.WindowManagerWrapper
import org.junit.Before
import org.junit.runner.RunWith
@@ -57,6 +58,7 @@ class AppHandleViewHolderTest : ShellTestCase() {
private val mockWindowManagerWrapper = mock<WindowManagerWrapper>()
private val mockHandler = mock<Handler>()
private val mockTaskInfo = mock<RunningTaskInfo>()
+ private val mockDesktopModeUiEventLogger = mock<DesktopModeUiEventLogger>()
@Before
fun setup() {
@@ -78,7 +80,8 @@ class AppHandleViewHolderTest : ShellTestCase() {
position = captionPosition,
width = CAPTION_WIDTH,
height = CAPTION_HEIGHT,
- showInputLayer = false
+ showInputLayer = false,
+ isCaptionVisible = true
)
)
@@ -91,7 +94,8 @@ class AppHandleViewHolderTest : ShellTestCase() {
mockOnTouchListener,
mockOnClickListener,
mockWindowManagerWrapper,
- mockHandler
+ mockHandler,
+ mockDesktopModeUiEventLogger,
)
}
}
diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp
index 714f6e41ff05..e09ab5fd1643 100644
--- a/libs/androidfw/AssetManager2.cpp
+++ b/libs/androidfw/AssetManager2.cpp
@@ -879,10 +879,10 @@ base::expected<FindEntryResult, NullOrIOError> AssetManager2::FindEntry(
// if we don't have a result yet
if (!final_result ||
// or this config is better before the locale than the existing result
- result->config.isBetterThanBeforeLocale(final_result->config, desired_config) ||
+ result->config.isBetterThanBeforeLocale(final_result->config, *desired_config) ||
// or the existing config isn't better before locale and this one specifies a locale
// whereas the existing one doesn't
- (!final_result->config.isBetterThanBeforeLocale(result->config, desired_config)
+ (!final_result->config.isBetterThanBeforeLocale(result->config, *desired_config)
&& has_locale && !final_has_locale)) {
final_result = result.value();
final_overlaid = overlaid;
diff --git a/libs/androidfw/CursorWindow.cpp b/libs/androidfw/CursorWindow.cpp
index a592749c5398..6e11d430c5ea 100644
--- a/libs/androidfw/CursorWindow.cpp
+++ b/libs/androidfw/CursorWindow.cpp
@@ -55,7 +55,7 @@ status_t CursorWindow::create(const String8 &name, size_t inflatedSize, CursorWi
window->mName = name;
window->mSize = std::min(kInlineSize, inflatedSize);
window->mInflatedSize = inflatedSize;
- window->mData = malloc(window->mSize);
+ window->mData = calloc(window->mSize, 1);
if (!window->mData) goto fail;
window->mReadOnly = false;
diff --git a/libs/androidfw/LocaleDataLookup.cpp b/libs/androidfw/LocaleDataLookup.cpp
index 9aacdcb9ca92..ed645826234d 100644
--- a/libs/androidfw/LocaleDataLookup.cpp
+++ b/libs/androidfw/LocaleDataLookup.cpp
@@ -5774,7 +5774,6 @@ const char* lookupLikelyScript(uint32_t packed_lang_region) {
case 0xD2120000u: // squ -> Latn
case 0x73724D45u: // sr-ME -> Latn
case 0x7372524Fu: // sr-RO -> Latn
- case 0x73725255u: // sr-RU -> Latn
case 0x73725452u: // sr-TR -> Latn
case 0x82320000u: // sra -> Latn
case 0x92320000u: // sre -> Latn
@@ -7265,7 +7264,6 @@ const char* lookupLikelyScript(uint32_t packed_lang_region) {
return SCRIPT_CODES[73u];
case 0x8ACD0000u: // nwc -> Newa
return SCRIPT_CODES[74u];
- case 0xB40C474Eu: // man-GN -> Nkoo
case 0xBA0D0000u: // nqo -> Nkoo
return SCRIPT_CODES[75u];
case 0xDCF90000u: // zhx -> Nshu
diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp
index 8ecd6ba9b253..6ec605c2ced5 100644
--- a/libs/androidfw/ResourceTypes.cpp
+++ b/libs/androidfw/ResourceTypes.cpp
@@ -2615,16 +2615,14 @@ bool ResTable_config::isLocaleBetterThan(const ResTable_config& o,
}
bool ResTable_config::isBetterThanBeforeLocale(const ResTable_config& o,
- const ResTable_config* requested) const {
- if (requested) {
- if (imsi || o.imsi) {
- if ((mcc != o.mcc) && requested->mcc) {
- return (mcc);
- }
+ const ResTable_config& requested) const {
+ if (imsi || o.imsi) {
+ if ((mcc != o.mcc) && requested.mcc) {
+ return mcc;
+ }
- if ((mnc != o.mnc) && requested->mnc) {
- return (mnc);
- }
+ if ((mnc != o.mnc) && requested.mnc) {
+ return mnc;
}
}
return false;
diff --git a/libs/androidfw/include/androidfw/ResourceTypes.h b/libs/androidfw/include/androidfw/ResourceTypes.h
index 63b28da075cd..bd72d3741460 100644
--- a/libs/androidfw/include/androidfw/ResourceTypes.h
+++ b/libs/androidfw/include/androidfw/ResourceTypes.h
@@ -1416,7 +1416,10 @@ struct ResTable_config
// match the requested configuration at all.
bool isLocaleBetterThan(const ResTable_config& o, const ResTable_config* requested) const;
- bool isBetterThanBeforeLocale(const ResTable_config& o, const ResTable_config* requested) const;
+ // The first part of isBetterThan() that only compares the fields that are higher priority than
+ // the locale. Use it when you need to do custom locale matching to filter out the configs prior
+ // to that.
+ bool isBetterThanBeforeLocale(const ResTable_config& o, const ResTable_config& requested) const;
String8 toString() const;
diff --git a/libs/hostgraphics/include/gui/BufferItemConsumer.h b/libs/hostgraphics/include/gui/BufferItemConsumer.h
index 5c96c82e061c..b9ff0a774805 100644
--- a/libs/hostgraphics/include/gui/BufferItemConsumer.h
+++ b/libs/hostgraphics/include/gui/BufferItemConsumer.h
@@ -48,6 +48,10 @@ public:
return mConsumer->acquireBuffer(item, presentWhen, 0);
}
+ status_t attachBuffer(BufferItem*, const sp<GraphicBuffer>&) {
+ return INVALID_OPERATION;
+ }
+
status_t releaseBuffer(const BufferItem& item,
const sp<Fence>& releaseFence = Fence::NO_FENCE) {
return OK;
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index a892e887bd43..ab1be7e6128d 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -139,6 +139,7 @@ cc_defaults {
"libandroidfw",
"libcrypto",
"libsync",
+ "libgui",
"libui",
"aconfig_text_flags_c_lib",
"aconfig_view_accessibility_flags_c_lib",
diff --git a/libs/hwui/WebViewFunctorManager.cpp b/libs/hwui/WebViewFunctorManager.cpp
index 9d16ee86739e..7e1eb70ca02b 100644
--- a/libs/hwui/WebViewFunctorManager.cpp
+++ b/libs/hwui/WebViewFunctorManager.cpp
@@ -16,6 +16,7 @@
#include "WebViewFunctorManager.h"
+#include <gui/SurfaceComposerClient.h>
#include <log/log.h>
#include <private/hwui/WebViewFunctor.h>
#include <utils/Trace.h>
@@ -43,7 +44,7 @@ public:
static ASurfaceControl* getSurfaceControl() {
ALOG_ASSERT(sCurrentFunctor);
- return sCurrentFunctor->getSurfaceControl();
+ return reinterpret_cast<ASurfaceControl*>(sCurrentFunctor->getSurfaceControl());
}
static void mergeTransaction(ASurfaceTransaction* transaction) {
ALOG_ASSERT(sCurrentFunctor);
@@ -129,12 +130,12 @@ bool WebViewFunctor::prepareRootSurfaceControl() {
renderthread::CanvasContext* activeContext = renderthread::CanvasContext::getActiveContext();
if (!activeContext) return false;
- ASurfaceControl* rootSurfaceControl = activeContext->getSurfaceControl();
+ sp<SurfaceControl> rootSurfaceControl = activeContext->getSurfaceControl();
if (!rootSurfaceControl) return false;
int32_t rgid = activeContext->getSurfaceControlGenerationId();
if (mParentSurfaceControlGenerationId != rgid) {
- reparentSurfaceControl(rootSurfaceControl);
+ reparentSurfaceControl(reinterpret_cast<ASurfaceControl*>(rootSurfaceControl.get()));
mParentSurfaceControlGenerationId = rgid;
}
@@ -210,33 +211,35 @@ void WebViewFunctor::removeOverlays() {
mCallbacks.removeOverlays(mFunctor, mData, currentFunctor.mergeTransaction);
if (mSurfaceControl) {
reparentSurfaceControl(nullptr);
- auto funcs = renderthread::RenderThread::getInstance().getASurfaceControlFunctions();
- funcs.releaseFunc(mSurfaceControl);
mSurfaceControl = nullptr;
}
}
ASurfaceControl* WebViewFunctor::getSurfaceControl() {
ATRACE_NAME("WebViewFunctor::getSurfaceControl");
- if (mSurfaceControl != nullptr) return mSurfaceControl;
+ if (mSurfaceControl != nullptr) {
+ return reinterpret_cast<ASurfaceControl*>(mSurfaceControl.get());
+ }
renderthread::CanvasContext* activeContext = renderthread::CanvasContext::getActiveContext();
LOG_ALWAYS_FATAL_IF(activeContext == nullptr, "Null active canvas context!");
- ASurfaceControl* rootSurfaceControl = activeContext->getSurfaceControl();
+ sp<SurfaceControl> rootSurfaceControl = activeContext->getSurfaceControl();
LOG_ALWAYS_FATAL_IF(rootSurfaceControl == nullptr, "Null root surface control!");
- auto funcs = renderthread::RenderThread::getInstance().getASurfaceControlFunctions();
mParentSurfaceControlGenerationId = activeContext->getSurfaceControlGenerationId();
- mSurfaceControl = funcs.createFunc(rootSurfaceControl, "Webview Overlay SurfaceControl");
- ASurfaceTransaction* transaction = funcs.transactionCreateFunc();
+
+ SurfaceComposerClient* client = rootSurfaceControl->getClient().get();
+ mSurfaceControl = client->createSurface(
+ String8("Webview Overlay SurfaceControl"), 0 /* width */, 0 /* height */,
+ // Format is only relevant for buffer queue layers.
+ PIXEL_FORMAT_UNKNOWN /* format */, ISurfaceComposerClient::eFXSurfaceBufferState,
+ rootSurfaceControl->getHandle());
+
activeContext->prepareSurfaceControlForWebview();
- funcs.transactionSetZOrderFunc(transaction, mSurfaceControl, -1);
- funcs.transactionSetVisibilityFunc(transaction, mSurfaceControl,
- ASURFACE_TRANSACTION_VISIBILITY_SHOW);
- funcs.transactionApplyFunc(transaction);
- funcs.transactionDeleteFunc(transaction);
- return mSurfaceControl;
+ SurfaceComposerClient::Transaction transaction;
+ transaction.setLayer(mSurfaceControl, -1).show(mSurfaceControl).apply();
+ return reinterpret_cast<ASurfaceControl*>(mSurfaceControl.get());
}
void WebViewFunctor::mergeTransaction(ASurfaceTransaction* transaction) {
@@ -249,8 +252,7 @@ void WebViewFunctor::mergeTransaction(ASurfaceTransaction* transaction) {
done = activeContext->mergeTransaction(transaction, mSurfaceControl);
}
if (!done) {
- auto funcs = renderthread::RenderThread::getInstance().getASurfaceControlFunctions();
- funcs.transactionApplyFunc(transaction);
+ reinterpret_cast<SurfaceComposerClient::Transaction*>(transaction)->apply();
}
}
@@ -258,11 +260,10 @@ void WebViewFunctor::reparentSurfaceControl(ASurfaceControl* parent) {
ATRACE_NAME("WebViewFunctor::reparentSurfaceControl");
if (mSurfaceControl == nullptr) return;
- auto funcs = renderthread::RenderThread::getInstance().getASurfaceControlFunctions();
- ASurfaceTransaction* transaction = funcs.transactionCreateFunc();
- funcs.transactionReparentFunc(transaction, mSurfaceControl, parent);
- mergeTransaction(transaction);
- funcs.transactionDeleteFunc(transaction);
+ SurfaceComposerClient::Transaction transaction;
+ transaction.reparent(mSurfaceControl, sp<SurfaceControl>::fromExisting(
+ reinterpret_cast<SurfaceControl*>(parent)));
+ mergeTransaction(reinterpret_cast<ASurfaceTransaction*>(&transaction));
}
void WebViewFunctor::reportRenderingThreads(const pid_t* thread_ids, size_t size) {
diff --git a/libs/hwui/WebViewFunctorManager.h b/libs/hwui/WebViewFunctorManager.h
index ec17640f9b5e..ac16f9138384 100644
--- a/libs/hwui/WebViewFunctorManager.h
+++ b/libs/hwui/WebViewFunctorManager.h
@@ -25,7 +25,11 @@
#include <mutex>
#include <vector>
-namespace android::uirenderer {
+namespace android {
+
+class SurfaceControl;
+
+namespace uirenderer {
class WebViewFunctorManager;
@@ -100,7 +104,9 @@ private:
bool mHasContext = false;
bool mCreatedHandle = false;
int32_t mParentSurfaceControlGenerationId = 0;
- ASurfaceControl* mSurfaceControl = nullptr;
+#ifdef __ANDROID__
+ sp<SurfaceControl> mSurfaceControl = nullptr;
+#endif
std::vector<pid_t> mRenderingThreads;
};
@@ -126,4 +132,5 @@ private:
std::vector<sp<WebViewFunctor::Handle>> mActiveFunctors;
};
-} // namespace android::uirenderer
+} // namespace uirenderer
+} // namespace android
diff --git a/libs/hwui/aconfig/hwui_flags.aconfig b/libs/hwui/aconfig/hwui_flags.aconfig
index d3fc91b65829..b3badd0bd51d 100644
--- a/libs/hwui/aconfig/hwui_flags.aconfig
+++ b/libs/hwui/aconfig/hwui_flags.aconfig
@@ -203,4 +203,11 @@ flag {
description: "Initialize GraphicBufferAllocater on ViewRootImpl init, to avoid blocking on init during buffer allocation, improving app launch latency."
bug: "389908734"
is_fixed_read_only: true
+}
+
+flag {
+ name: "bitmap_parcel_ashmem_as_immutable"
+ namespace: "system_performance"
+ description: "Whether to parcel implicit copies of bitmaps to ashmem as immutable"
+ bug: "400807118"
} \ No newline at end of file
diff --git a/libs/hwui/hwui/Bitmap.cpp b/libs/hwui/hwui/Bitmap.cpp
index 3ef970830dc4..46dcc1f4c50b 100644
--- a/libs/hwui/hwui/Bitmap.cpp
+++ b/libs/hwui/hwui/Bitmap.cpp
@@ -164,8 +164,13 @@ std::string Bitmap::getAshmemId(const char* tag, uint64_t bitmapId,
android::base::ReadFileToString("/proc/self/cmdline", &temp);
return temp;
}();
- return std::format("bitmap/{}-id_{}-{}x{}-size_{}-{}",
- tag, bitmapId, width, height, size, sCmdline);
+ /* counter is to ensure the uniqueness of the ashmem filename,
+ * e.g. a bitmap with same mId could be sent multiple times, an
+ * ashmem region is created each time
+ */
+ static std::atomic<uint32_t> counter{0};
+ return std::format("bitmap/{}_{}_{}x{}_size-{}_id-{}_{}",
+ tag, counter.fetch_add(1), width, height, size, bitmapId, sCmdline);
}
sk_sp<Bitmap> Bitmap::allocateAshmemBitmap(SkBitmap* bitmap) {
diff --git a/libs/hwui/jni/Bitmap.cpp b/libs/hwui/jni/Bitmap.cpp
index 27d4ac7cef4b..b6a2ad7064a9 100644
--- a/libs/hwui/jni/Bitmap.cpp
+++ b/libs/hwui/jni/Bitmap.cpp
@@ -28,8 +28,20 @@
#include "SkRefCnt.h"
#include "SkStream.h"
#include "SkTypes.h"
+#ifdef __linux__ // Only Linux support parcel
+#include "android/binder_parcel.h"
+#endif
#include "android_nio_utils.h"
+#ifdef __ANDROID__
+#include <com_android_graphics_hwui_flags.h>
+namespace hwui_flags = com::android::graphics::hwui::flags;
+#else
+namespace hwui_flags {
+constexpr bool bitmap_parcel_ashmem_as_immutable() { return false; }
+}
+#endif
+
#define DEBUG_PARCEL 0
static jclass gBitmap_class;
@@ -841,6 +853,25 @@ static jobject Bitmap_createFromParcel(JNIEnv* env, jobject, jobject parcel) {
#endif
}
+#ifdef __linux__ // Only Linux support parcel
+// Returns whether this bitmap should be written to the parcel as mutable.
+static bool shouldParcelAsMutable(SkBitmap& bitmap, AParcel* parcel) {
+ // If the bitmap is immutable, then parcel as immutable.
+ if (bitmap.isImmutable()) {
+ return false;
+ }
+
+ if (!hwui_flags::bitmap_parcel_ashmem_as_immutable()) {
+ return true;
+ }
+
+ // If we're going to copy the bitmap to ashmem and write that to the parcel,
+ // then parcel as immutable, since we won't be mutating the bitmap after
+ // writing it to the parcel.
+ return !shouldUseAshmem(parcel, bitmap.computeByteSize());
+}
+#endif
+
static jboolean Bitmap_writeToParcel(JNIEnv* env, jobject, jlong bitmapHandle, jint density,
jobject parcel) {
#ifdef __linux__ // Only Linux support parcel
@@ -855,7 +886,7 @@ static jboolean Bitmap_writeToParcel(JNIEnv* env, jobject, jlong bitmapHandle, j
auto bitmapWrapper = reinterpret_cast<BitmapWrapper*>(bitmapHandle);
bitmapWrapper->getSkBitmap(&bitmap);
- p.writeInt32(!bitmap.isImmutable());
+ p.writeInt32(shouldParcelAsMutable(bitmap, p.get()));
p.writeInt32(bitmap.colorType());
p.writeInt32(bitmap.alphaType());
SkColorSpace* colorSpace = bitmap.colorSpace();
diff --git a/libs/hwui/jni/GIFMovie.cpp b/libs/hwui/jni/GIFMovie.cpp
index 476b6fda5007..6c82aa1ca27d 100644
--- a/libs/hwui/jni/GIFMovie.cpp
+++ b/libs/hwui/jni/GIFMovie.cpp
@@ -63,7 +63,7 @@ GIFMovie::GIFMovie(SkStream* stream)
}
fCurrIndex = -1;
fLastDrawIndex = -1;
- fPaintingColor = SK_AlphaTRANSPARENT;
+ fPaintingColor = SkPackARGB32(0, 0, 0, 0);
}
GIFMovie::~GIFMovie()
@@ -127,7 +127,7 @@ static void copyLine(uint32_t* dst, const unsigned char* src, const ColorMapObje
for (; width > 0; width--, src++, dst++) {
if (*src != transparent && *src < cmap->ColorCount) {
const GifColorType& col = cmap->Colors[*src];
- *dst = SkColorSetRGB(col.Red, col.Green, col.Blue);
+ *dst = SkPackARGB32(0xFF, col.Red, col.Green, col.Blue);
}
}
}
@@ -395,10 +395,10 @@ bool GIFMovie::onGetBitmap(SkBitmap* bm)
lastIndex = fGIF->ImageCount - 1;
}
- SkColor bgColor = SK_ColorTRANSPARENT;
+ SkColor bgColor = SkPackARGB32(0, 0, 0, 0);
if (gif->SColorMap != nullptr && gif->SBackGroundColor < gif->SColorMap->ColorCount) {
const GifColorType& col = gif->SColorMap->Colors[gif->SBackGroundColor];
- bgColor = SkColorSetRGB(col.Red, col.Green, col.Blue);
+ bgColor = SkColorSetARGB(0xFF, col.Red, col.Green, col.Blue);
}
// draw each frames - not intelligent way
@@ -411,7 +411,7 @@ bool GIFMovie::onGetBitmap(SkBitmap* bm)
if (!trans && gif->SColorMap != nullptr) {
fPaintingColor = bgColor;
} else {
- fPaintingColor = SK_ColorTRANSPARENT;
+ fPaintingColor = SkColorSetARGB(0, 0, 0, 0);
}
bm->eraseColor(fPaintingColor);
diff --git a/libs/hwui/jni/android_graphics_HardwareRenderer.cpp b/libs/hwui/jni/android_graphics_HardwareRenderer.cpp
index cfec24b17cd4..009974b3c8de 100644
--- a/libs/hwui/jni/android_graphics_HardwareRenderer.cpp
+++ b/libs/hwui/jni/android_graphics_HardwareRenderer.cpp
@@ -53,6 +53,7 @@
#include <src/image/SkImage_Base.h>
#include <thread/CommonPool.h>
#ifdef __ANDROID__
+#include <gui/SurfaceControl.h>
#include <ui/GraphicBufferAllocator.h>
#endif
#include <utils/Color.h>
@@ -217,9 +218,11 @@ static void android_view_ThreadedRenderer_setSurface(JNIEnv* env, jobject clazz,
static void android_view_ThreadedRenderer_setSurfaceControl(JNIEnv* env, jobject clazz,
jlong proxyPtr, jlong surfaceControlPtr) {
+#ifdef __ANDROID__
RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
- ASurfaceControl* surfaceControl = reinterpret_cast<ASurfaceControl*>(surfaceControlPtr);
- proxy->setSurfaceControl(surfaceControl);
+ SurfaceControl* surfaceControl = reinterpret_cast<SurfaceControl*>(surfaceControlPtr);
+ proxy->setSurfaceControl(sp<SurfaceControl>::fromExisting(surfaceControl));
+#endif
}
static jboolean android_view_ThreadedRenderer_pause(JNIEnv* env, jobject clazz,
@@ -684,7 +687,7 @@ static void android_view_ThreadedRenderer_setFrameCompleteCallback(JNIEnv* env,
class CopyRequestAdapter : public CopyRequest {
public:
- CopyRequestAdapter(JavaVM* vm, jobject jCopyRequest, Rect srcRect)
+ CopyRequestAdapter(JavaVM* vm, jobject jCopyRequest, ::android::uirenderer::Rect srcRect)
: CopyRequest(srcRect), mRefHolder(vm, jCopyRequest) {}
virtual SkBitmap getDestinationBitmap(int srcWidth, int srcHeight) override {
@@ -710,8 +713,9 @@ static void android_view_ThreadedRenderer_copySurfaceInto(JNIEnv* env, jobject c
jobject jCopyRequest) {
JavaVM* vm = nullptr;
LOG_ALWAYS_FATAL_IF(env->GetJavaVM(&vm) != JNI_OK, "Unable to get Java VM");
- auto copyRequest = std::make_shared<CopyRequestAdapter>(vm, env->NewGlobalRef(jCopyRequest),
- Rect(left, top, right, bottom));
+ auto copyRequest = std::make_shared<CopyRequestAdapter>(
+ vm, env->NewGlobalRef(jCopyRequest),
+ ::android::uirenderer::Rect(left, top, right, bottom));
ANativeWindow* window = fromSurface(env, jsurface);
RenderProxy::copySurfaceInto(window, std::move(copyRequest));
ANativeWindow_release(window);
diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.cpp b/libs/hwui/pipeline/skia/SkiaPipeline.cpp
index dc669a5eca73..aa8cbd1f0703 100644
--- a/libs/hwui/pipeline/skia/SkiaPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaPipeline.cpp
@@ -175,6 +175,8 @@ bool SkiaPipeline::setupMultiFrameCapture() {
if (stream->isValid()) {
mOpenMultiPicStream = std::move(stream);
mSerialContext.reset(new SkSharingSerialContext());
+ // passing the GrDirectContext to the SerialContext allows us to raster/serialize GPU images
+ mSerialContext->setDirectContext(mRenderThread.getGrContext());
SkSerialProcs procs;
procs.fImageProc = SkSharingSerialContext::serializeImage;
procs.fImageCtx = mSerialContext.get();
diff --git a/libs/hwui/platform/host/WebViewFunctorManager.cpp b/libs/hwui/platform/host/WebViewFunctorManager.cpp
index 4ba206b41b39..66646b2da2ef 100644
--- a/libs/hwui/platform/host/WebViewFunctorManager.cpp
+++ b/libs/hwui/platform/host/WebViewFunctorManager.cpp
@@ -45,7 +45,7 @@ void WebViewFunctor::destroyContext() {}
void WebViewFunctor::removeOverlays() {}
ASurfaceControl* WebViewFunctor::getSurfaceControl() {
- return mSurfaceControl;
+ return nullptr;
}
void WebViewFunctor::mergeTransaction(ASurfaceTransaction* transaction) {}
diff --git a/libs/hwui/platform/host/renderthread/RenderThread.cpp b/libs/hwui/platform/host/renderthread/RenderThread.cpp
index f9d0f4704e08..ece45304e6d5 100644
--- a/libs/hwui/platform/host/renderthread/RenderThread.cpp
+++ b/libs/hwui/platform/host/renderthread/RenderThread.cpp
@@ -27,8 +27,6 @@ namespace renderthread {
static bool gHasRenderThreadInstance = false;
static JVMAttachHook gOnStartHook = nullptr;
-ASurfaceControlFunctions::ASurfaceControlFunctions() {}
-
bool RenderThread::hasInstance() {
return gHasRenderThreadInstance;
}
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index b248c4bc9ade..d5ac99389d87 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -18,6 +18,12 @@
#include <apex/window.h>
#include <fcntl.h>
+
+#ifdef __ANDROID__
+#include <gui/ITransactionCompletedListener.h>
+#include <gui/SurfaceComposerClient.h>
+#endif
+
#include <gui/TraceUtils.h>
#include <strings.h>
#include <sys/stat.h>
@@ -165,7 +171,9 @@ void CanvasContext::destroy() {
stopDrawing();
setHardwareBuffer(nullptr);
setSurface(nullptr);
+#ifdef __ANDROID__
setSurfaceControl(nullptr);
+#endif
freePrefetchedLayers();
destroyHardwareResources();
mAnimationContext->destroy();
@@ -220,10 +228,15 @@ void CanvasContext::setSurface(ANativeWindow* window, bool enableTimeout) {
setupPipelineSurface();
}
-void CanvasContext::setSurfaceControl(ASurfaceControl* surfaceControl) {
- if (surfaceControl == mSurfaceControl) return;
+#ifdef __ANDROID__
+sp<SurfaceControl> CanvasContext::getSurfaceControl() const {
+ return mSurfaceControl;
+}
+#endif
- auto funcs = mRenderThread.getASurfaceControlFunctions();
+void CanvasContext::setSurfaceControl(sp<SurfaceControl> surfaceControl) {
+#ifdef __ANDROID__
+ if (surfaceControl == mSurfaceControl) return;
if (surfaceControl == nullptr) {
setASurfaceTransactionCallback(nullptr);
@@ -231,17 +244,23 @@ void CanvasContext::setSurfaceControl(ASurfaceControl* surfaceControl) {
}
if (mSurfaceControl != nullptr) {
- funcs.unregisterListenerFunc(this, &onSurfaceStatsAvailable);
- funcs.releaseFunc(mSurfaceControl);
+ TransactionCompletedListener::getInstance()->removeSurfaceStatsListener(
+ this, reinterpret_cast<void*>(onSurfaceStatsAvailable));
}
- mSurfaceControl = surfaceControl;
+
+ mSurfaceControl = std::move(surfaceControl);
mSurfaceControlGenerationId++;
- mExpectSurfaceStats = surfaceControl != nullptr;
+ mExpectSurfaceStats = mSurfaceControl != nullptr;
if (mExpectSurfaceStats) {
- funcs.acquireFunc(mSurfaceControl);
- funcs.registerListenerFunc(surfaceControl, mSurfaceControlGenerationId, this,
- &onSurfaceStatsAvailable);
+ SurfaceStatsCallback callback = [generationId = mSurfaceControlGenerationId](
+ void* callback_context, nsecs_t, const sp<Fence>&,
+ const SurfaceStats& surfaceStats) {
+ onSurfaceStatsAvailable(callback_context, generationId, surfaceStats);
+ };
+ TransactionCompletedListener::getInstance()->addSurfaceStatsListener(
+ this, reinterpret_cast<void*>(onSurfaceStatsAvailable), mSurfaceControl, callback);
}
+#endif
}
void CanvasContext::setupPipelineSurface() {
@@ -896,17 +915,26 @@ FrameInfo* CanvasContext::getFrameInfoFromLastFew(uint64_t frameNumber, uint32_t
}
void CanvasContext::onSurfaceStatsAvailable(void* context, int32_t surfaceControlId,
- ASurfaceControlStats* stats) {
+ const SurfaceStats& stats) {
+#ifdef __ANDROID__
auto* instance = static_cast<CanvasContext*>(context);
- const ASurfaceControlFunctions& functions =
- instance->mRenderThread.getASurfaceControlFunctions();
+ nsecs_t gpuCompleteTime = -1L;
+ if (const auto* fence = std::get_if<sp<Fence>>(&stats.acquireTimeOrFence)) {
+ // We got a fence instead of the acquire time due to latching unsignaled.
+ // Ideally the client could just get the acquire time directly from
+ // the fence instead of calling this function which needs to block.
+ (*fence)->waitForever("acquireFence");
+ gpuCompleteTime = (*fence)->getSignalTime();
+ } else {
+ gpuCompleteTime = std::get<int64_t>(stats.acquireTimeOrFence);
+ }
- nsecs_t gpuCompleteTime = functions.getAcquireTimeFunc(stats);
if (gpuCompleteTime == Fence::SIGNAL_TIME_PENDING) {
gpuCompleteTime = -1;
}
- uint64_t frameNumber = functions.getFrameNumberFunc(stats);
+
+ uint64_t frameNumber = stats.eventStats.frameNumber;
FrameInfo* frameInfo = instance->getFrameInfoFromLastFew(frameNumber, surfaceControlId);
@@ -919,6 +947,7 @@ void CanvasContext::onSurfaceStatsAvailable(void* context, int32_t surfaceContro
instance->mJankTracker.finishFrame(*frameInfo, instance->mFrameMetricsReporter, frameNumber,
surfaceControlId);
}
+#endif
}
// Called by choreographer to do an RT-driven animation
@@ -1140,10 +1169,11 @@ CanvasContext* CanvasContext::getActiveContext() {
return ScopedActiveContext::getActiveContext();
}
-bool CanvasContext::mergeTransaction(ASurfaceTransaction* transaction, ASurfaceControl* control) {
+bool CanvasContext::mergeTransaction(ASurfaceTransaction* transaction,
+ const sp<SurfaceControl>& control) {
if (!mASurfaceTransactionCallback) return false;
return std::invoke(mASurfaceTransactionCallback, reinterpret_cast<int64_t>(transaction),
- reinterpret_cast<int64_t>(control), getFrameNumber());
+ reinterpret_cast<int64_t>(control.get()), getFrameNumber());
}
void CanvasContext::prepareSurfaceControlForWebview() {
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
index 3de8e0516070..655aebada954 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -50,6 +50,9 @@
#include "utils/RingBuffer.h"
namespace android {
+
+class SurfaceStats;
+
namespace uirenderer {
class AnimationContext;
@@ -121,7 +124,9 @@ public:
*/
GrDirectContext* getGrContext() const { return mRenderThread.getGrContext(); }
- ASurfaceControl* getSurfaceControl() const { return mSurfaceControl; }
+#ifdef __ANDROID__
+ sp<SurfaceControl> getSurfaceControl() const;
+#endif
int32_t getSurfaceControlGenerationId() const { return mSurfaceControlGenerationId; }
// Won't take effect until next EGLSurface creation
@@ -129,7 +134,7 @@ public:
void setHardwareBuffer(AHardwareBuffer* buffer);
void setSurface(ANativeWindow* window, bool enableTimeout = true);
- void setSurfaceControl(ASurfaceControl* surfaceControl);
+ void setSurfaceControl(sp<SurfaceControl> surfaceControl);
bool pauseSurface();
void setStopped(bool stopped);
bool isStopped() { return mStopped || !hasOutputTarget(); }
@@ -207,7 +212,7 @@ public:
// Called when SurfaceStats are available.
static void onSurfaceStatsAvailable(void* context, int32_t surfaceControlId,
- ASurfaceControlStats* stats);
+ const SurfaceStats& stats);
void setASurfaceTransactionCallback(
const std::function<bool(int64_t, int64_t, int64_t)>& callback) {
@@ -218,7 +223,7 @@ public:
mBufferParams = params;
}
- bool mergeTransaction(ASurfaceTransaction* transaction, ASurfaceControl* control);
+ bool mergeTransaction(ASurfaceTransaction* transaction, const sp<SurfaceControl>& control);
void setPrepareSurfaceControlForWebviewCallback(const std::function<void()>& callback) {
mPrepareSurfaceControlForWebviewCallback = callback;
@@ -286,7 +291,9 @@ private:
std::unique_ptr<ReliableSurface> mNativeSurface;
// The SurfaceControl reference is passed from ViewRootImpl, can be set to
// NULL to remove the reference
- ASurfaceControl* mSurfaceControl = nullptr;
+#ifdef __ANDROID__
+ sp<SurfaceControl> mSurfaceControl = nullptr;
+#endif
// id to track surface control changes and WebViewFunctor uses it to determine
// whether reparenting is needed also used by FrameMetricsReporter to determine
// if a frame is from an "old" surface (i.e. one that existed before the
diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp
index ebfd8fde91f6..e4be5fa8d39e 100644
--- a/libs/hwui/renderthread/RenderProxy.cpp
+++ b/libs/hwui/renderthread/RenderProxy.cpp
@@ -21,6 +21,11 @@
#include <SkPicture.h>
#include <gui/TraceUtils.h>
#include <pthread.h>
+
+#ifdef __ANDROID__
+#include <gui/SurfaceControl.h>
+#endif
+
#include <ui/GraphicBufferAllocator.h>
#include "DeferredLayerUpdater.h"
@@ -115,17 +120,12 @@ void RenderProxy::setSurface(ANativeWindow* window, bool enableTimeout) {
});
}
-void RenderProxy::setSurfaceControl(ASurfaceControl* surfaceControl) {
- auto funcs = mRenderThread.getASurfaceControlFunctions();
- if (surfaceControl) {
- funcs.acquireFunc(surfaceControl);
- }
- mRenderThread.queue().post([this, control = surfaceControl, funcs]() mutable {
- mContext->setSurfaceControl(control);
- if (control) {
- funcs.releaseFunc(control);
- }
+void RenderProxy::setSurfaceControl(sp<SurfaceControl> surfaceControl) {
+#ifdef __ANDROID__
+ mRenderThread.queue().post([this, control = std::move(surfaceControl)]() mutable {
+ mContext->setSurfaceControl(std::move(control));
});
+#endif
}
void RenderProxy::allocateBuffers() {
diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h
index ad6d54bfcf91..23b3ebd4b360 100644
--- a/libs/hwui/renderthread/RenderProxy.h
+++ b/libs/hwui/renderthread/RenderProxy.h
@@ -20,7 +20,6 @@
#include <SkRefCnt.h>
#include <android/hardware_buffer.h>
#include <android/native_window.h>
-#include <android/surface_control.h>
#include <cutils/compiler.h>
#include <utils/Functor.h>
@@ -39,6 +38,7 @@ class SkImage;
namespace android {
class GraphicBuffer;
+class SurfaceControl;
class Surface;
namespace uirenderer {
@@ -80,7 +80,7 @@ public:
void setName(const char* name);
void setHardwareBuffer(AHardwareBuffer* buffer);
void setSurface(ANativeWindow* window, bool enableTimeout = true);
- void setSurfaceControl(ASurfaceControl* surfaceControl);
+ void setSurfaceControl(sp<SurfaceControl> surfaceControl);
void allocateBuffers();
bool pause();
void setStopped(bool stopped);
diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp
index 6ab8e4e0e2ab..5e404247376f 100644
--- a/libs/hwui/renderthread/RenderThread.cpp
+++ b/libs/hwui/renderthread/RenderThread.cpp
@@ -55,66 +55,6 @@ static bool gHasRenderThreadInstance = false;
static JVMAttachHook gOnStartHook = nullptr;
-ASurfaceControlFunctions::ASurfaceControlFunctions() {
- void* handle_ = dlopen("libandroid.so", RTLD_NOW | RTLD_NODELETE);
- createFunc = (ASC_create)dlsym(handle_, "ASurfaceControl_create");
- LOG_ALWAYS_FATAL_IF(createFunc == nullptr,
- "Failed to find required symbol ASurfaceControl_create!");
-
- acquireFunc = (ASC_acquire) dlsym(handle_, "ASurfaceControl_acquire");
- LOG_ALWAYS_FATAL_IF(acquireFunc == nullptr,
- "Failed to find required symbol ASurfaceControl_acquire!");
-
- releaseFunc = (ASC_release) dlsym(handle_, "ASurfaceControl_release");
- LOG_ALWAYS_FATAL_IF(releaseFunc == nullptr,
- "Failed to find required symbol ASurfaceControl_release!");
-
- registerListenerFunc = (ASC_registerSurfaceStatsListener) dlsym(handle_,
- "ASurfaceControl_registerSurfaceStatsListener");
- LOG_ALWAYS_FATAL_IF(registerListenerFunc == nullptr,
- "Failed to find required symbol ASurfaceControl_registerSurfaceStatsListener!");
-
- unregisterListenerFunc = (ASC_unregisterSurfaceStatsListener) dlsym(handle_,
- "ASurfaceControl_unregisterSurfaceStatsListener");
- LOG_ALWAYS_FATAL_IF(unregisterListenerFunc == nullptr,
- "Failed to find required symbol ASurfaceControl_unregisterSurfaceStatsListener!");
-
- getAcquireTimeFunc = (ASCStats_getAcquireTime) dlsym(handle_,
- "ASurfaceControlStats_getAcquireTime");
- LOG_ALWAYS_FATAL_IF(getAcquireTimeFunc == nullptr,
- "Failed to find required symbol ASurfaceControlStats_getAcquireTime!");
-
- getFrameNumberFunc = (ASCStats_getFrameNumber) dlsym(handle_,
- "ASurfaceControlStats_getFrameNumber");
- LOG_ALWAYS_FATAL_IF(getFrameNumberFunc == nullptr,
- "Failed to find required symbol ASurfaceControlStats_getFrameNumber!");
-
- transactionCreateFunc = (AST_create)dlsym(handle_, "ASurfaceTransaction_create");
- LOG_ALWAYS_FATAL_IF(transactionCreateFunc == nullptr,
- "Failed to find required symbol ASurfaceTransaction_create!");
-
- transactionDeleteFunc = (AST_delete)dlsym(handle_, "ASurfaceTransaction_delete");
- LOG_ALWAYS_FATAL_IF(transactionDeleteFunc == nullptr,
- "Failed to find required symbol ASurfaceTransaction_delete!");
-
- transactionApplyFunc = (AST_apply)dlsym(handle_, "ASurfaceTransaction_apply");
- LOG_ALWAYS_FATAL_IF(transactionApplyFunc == nullptr,
- "Failed to find required symbol ASurfaceTransaction_apply!");
-
- transactionReparentFunc = (AST_reparent)dlsym(handle_, "ASurfaceTransaction_reparent");
- LOG_ALWAYS_FATAL_IF(transactionReparentFunc == nullptr,
- "Failed to find required symbol transactionReparentFunc!");
-
- transactionSetVisibilityFunc =
- (AST_setVisibility)dlsym(handle_, "ASurfaceTransaction_setVisibility");
- LOG_ALWAYS_FATAL_IF(transactionSetVisibilityFunc == nullptr,
- "Failed to find required symbol ASurfaceTransaction_setVisibility!");
-
- transactionSetZOrderFunc = (AST_setZOrder)dlsym(handle_, "ASurfaceTransaction_setZOrder");
- LOG_ALWAYS_FATAL_IF(transactionSetZOrderFunc == nullptr,
- "Failed to find required symbol ASurfaceTransaction_setZOrder!");
-}
-
void RenderThread::extendedFrameCallback(const AChoreographerFrameCallbackData* cbData,
void* data) {
RenderThread* rt = reinterpret_cast<RenderThread*>(data);
diff --git a/libs/hwui/renderthread/RenderThread.h b/libs/hwui/renderthread/RenderThread.h
index 86fddbae0831..f733c7c58ef3 100644
--- a/libs/hwui/renderthread/RenderThread.h
+++ b/libs/hwui/renderthread/RenderThread.h
@@ -77,49 +77,6 @@ struct VsyncSource {
virtual ~VsyncSource() {}
};
-typedef ASurfaceControl* (*ASC_create)(ASurfaceControl* parent, const char* debug_name);
-typedef void (*ASC_acquire)(ASurfaceControl* control);
-typedef void (*ASC_release)(ASurfaceControl* control);
-
-typedef void (*ASC_registerSurfaceStatsListener)(ASurfaceControl* control, int32_t id,
- void* context,
- ASurfaceControl_SurfaceStatsListener func);
-typedef void (*ASC_unregisterSurfaceStatsListener)(void* context,
- ASurfaceControl_SurfaceStatsListener func);
-
-typedef int64_t (*ASCStats_getAcquireTime)(ASurfaceControlStats* stats);
-typedef uint64_t (*ASCStats_getFrameNumber)(ASurfaceControlStats* stats);
-
-typedef ASurfaceTransaction* (*AST_create)();
-typedef void (*AST_delete)(ASurfaceTransaction* transaction);
-typedef void (*AST_apply)(ASurfaceTransaction* transaction);
-typedef void (*AST_reparent)(ASurfaceTransaction* aSurfaceTransaction,
- ASurfaceControl* aSurfaceControl,
- ASurfaceControl* newParentASurfaceControl);
-typedef void (*AST_setVisibility)(ASurfaceTransaction* transaction,
- ASurfaceControl* surface_control, int8_t visibility);
-typedef void (*AST_setZOrder)(ASurfaceTransaction* transaction, ASurfaceControl* surface_control,
- int32_t z_order);
-
-struct ASurfaceControlFunctions {
- ASurfaceControlFunctions();
-
- ASC_create createFunc;
- ASC_acquire acquireFunc;
- ASC_release releaseFunc;
- ASC_registerSurfaceStatsListener registerListenerFunc;
- ASC_unregisterSurfaceStatsListener unregisterListenerFunc;
- ASCStats_getAcquireTime getAcquireTimeFunc;
- ASCStats_getFrameNumber getFrameNumberFunc;
-
- AST_create transactionCreateFunc;
- AST_delete transactionDeleteFunc;
- AST_apply transactionApplyFunc;
- AST_reparent transactionReparentFunc;
- AST_setVisibility transactionSetVisibilityFunc;
- AST_setZOrder transactionSetZOrderFunc;
-};
-
class ChoreographerSource;
class DummyVsyncSource;
@@ -166,10 +123,6 @@ public:
void preload();
- const ASurfaceControlFunctions& getASurfaceControlFunctions() {
- return mASurfaceControlFunctions;
- }
-
void trimMemory(TrimLevel level);
void trimCaches(CacheTrimLevel level);
@@ -244,7 +197,6 @@ private:
CacheManager* mCacheManager;
sp<VulkanManager> mVkManager;
- ASurfaceControlFunctions mASurfaceControlFunctions;
std::mutex mJankDataMutex;
};
diff --git a/libs/hwui/renderthread/VulkanManager.cpp b/libs/hwui/renderthread/VulkanManager.cpp
index a67aea466c1c..e5a6260cfd98 100644
--- a/libs/hwui/renderthread/VulkanManager.cpp
+++ b/libs/hwui/renderthread/VulkanManager.cpp
@@ -44,7 +44,7 @@ namespace uirenderer {
namespace renderthread {
// Not all of these are strictly required, but are all enabled if present.
-static std::array<std::string_view, 23> sEnableExtensions{
+static std::array<std::string_view, 25> sEnableExtensions{
VK_KHR_BIND_MEMORY_2_EXTENSION_NAME,
VK_KHR_DEDICATED_ALLOCATION_EXTENSION_NAME,
VK_KHR_EXTERNAL_MEMORY_CAPABILITIES_EXTENSION_NAME,
@@ -68,6 +68,8 @@ static std::array<std::string_view, 23> sEnableExtensions{
VK_EXT_GLOBAL_PRIORITY_QUERY_EXTENSION_NAME,
VK_KHR_GLOBAL_PRIORITY_EXTENSION_NAME,
VK_EXT_DEVICE_FAULT_EXTENSION_NAME,
+ VK_EXT_FRAME_BOUNDARY_EXTENSION_NAME,
+ VK_ANDROID_FRAME_BOUNDARY_EXTENSION_NAME,
};
static bool shouldEnableExtension(const std::string_view& extension) {
@@ -238,6 +240,7 @@ void VulkanManager::setupDevice(skgpu::VulkanExtensions& grExtensions,
for (uint32_t i = 0; i < queueCount; i++) {
queuePriorityProps[i].sType = VK_STRUCTURE_TYPE_QUEUE_FAMILY_GLOBAL_PRIORITY_PROPERTIES_EXT;
queuePriorityProps[i].pNext = nullptr;
+ queueProps[i].sType = VK_STRUCTURE_TYPE_QUEUE_FAMILY_PROPERTIES_2;
queueProps[i].pNext = &queuePriorityProps[i];
}
mGetPhysicalDeviceQueueFamilyProperties2(mPhysicalDevice, &queueCount, queueProps.get());
@@ -745,7 +748,14 @@ VulkanManager::VkDrawResult VulkanManager::finishFrame(SkSurface* surface) {
ALOGE_IF(!context, "Surface is not backed by gpu");
GrSemaphoresSubmitted submitted = context->flush(
surface, SkSurfaces::BackendSurfaceAccess::kPresent, flushInfo);
- context->submit();
+
+ static uint64_t currentFrameID = 0;
+ GrSubmitInfo submitInfo;
+ if (!mFrameBoundaryANDROID) {
+ submitInfo.fMarkBoundary = GrMarkFrameBoundary::kYes;
+ submitInfo.fFrameID = currentFrameID++;
+ }
+ context->submit(submitInfo);
VkDrawResult drawResult{
.submissionTime = systemTime(),
};
diff --git a/libs/hwui/renderthread/VulkanManager.h b/libs/hwui/renderthread/VulkanManager.h
index a593ec6f8351..744211e39f79 100644
--- a/libs/hwui/renderthread/VulkanManager.h
+++ b/libs/hwui/renderthread/VulkanManager.h
@@ -30,14 +30,10 @@
// VK_ANDROID_frame_boundary is a bespoke extension defined by AGI
// (https://github.com/google/agi) to enable profiling of apps rendering via
// HWUI. This extension is not defined in Khronos, hence the need to declare it
-// manually here. There's a superseding extension (VK_EXT_frame_boundary) being
-// discussed in Khronos, but in the meantime we use the bespoke
-// VK_ANDROID_frame_boundary. This is a device extension that is implemented by
+// manually here. There's an extension (VK_EXT_frame_boundary) which we will use
+// instead if available. This is a device extension that is implemented by
// AGI's Vulkan capture layer, such that it is only supported by devices when
// AGI is doing a capture of the app.
-//
-// TODO(b/182165045): use the Khronos blessed VK_EXT_frame_boudary once it has
-// landed in the spec.
typedef void(VKAPI_PTR* PFN_vkFrameBoundaryANDROID)(VkDevice device, VkSemaphore semaphore,
VkImage image);
#define VK_ANDROID_FRAME_BOUNDARY_EXTENSION_NAME "VK_ANDROID_frame_boundary"
diff --git a/location/api/system-current.txt b/location/api/system-current.txt
index eb19ba84ee62..47984745fafc 100644
--- a/location/api/system-current.txt
+++ b/location/api/system-current.txt
@@ -96,8 +96,8 @@ package android.location {
public static final class BeidouSatelliteEphemeris.BeidouSatelliteEphemerisTime implements android.os.Parcelable {
method public int describeContents();
+ method @IntRange(from=0, to=31) public int getAode();
method @IntRange(from=0) public int getBeidouWeekNumber();
- method @IntRange(from=0, to=31) public int getIode();
method @IntRange(from=0, to=604792) public int getToeSeconds();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.location.BeidouSatelliteEphemeris.BeidouSatelliteEphemerisTime> CREATOR;
@@ -106,8 +106,8 @@ package android.location {
public static final class BeidouSatelliteEphemeris.BeidouSatelliteEphemerisTime.Builder {
ctor public BeidouSatelliteEphemeris.BeidouSatelliteEphemerisTime.Builder();
method @NonNull public android.location.BeidouSatelliteEphemeris.BeidouSatelliteEphemerisTime build();
+ method @NonNull public android.location.BeidouSatelliteEphemeris.BeidouSatelliteEphemerisTime.Builder setAode(int);
method @NonNull public android.location.BeidouSatelliteEphemeris.BeidouSatelliteEphemerisTime.Builder setBeidouWeekNumber(@IntRange(from=0) int);
- method @NonNull public android.location.BeidouSatelliteEphemeris.BeidouSatelliteEphemerisTime.Builder setIode(int);
method @NonNull public android.location.BeidouSatelliteEphemeris.BeidouSatelliteEphemerisTime.Builder setToeSeconds(@IntRange(from=0, to=604792) int);
}
@@ -177,7 +177,7 @@ package android.location {
method public int describeContents();
method @Nullable public android.location.GnssAlmanac getAlmanac();
method @Nullable public android.location.AuxiliaryInformation getAuxiliaryInformation();
- method @Nullable public android.location.KlobucharIonosphericModel getIonosphericModel();
+ method @Nullable public android.location.GalileoIonosphericModel getIonosphericModel();
method @Nullable public android.location.LeapSecondsModel getLeapSecondsModel();
method @NonNull public java.util.List<android.location.RealTimeIntegrityModel> getRealTimeIntegrityModels();
method @NonNull public java.util.List<android.location.GnssAssistance.GnssSatelliteCorrections> getSatelliteCorrections();
@@ -193,7 +193,7 @@ package android.location {
method @NonNull public android.location.GalileoAssistance build();
method @NonNull public android.location.GalileoAssistance.Builder setAlmanac(@Nullable android.location.GnssAlmanac);
method @NonNull public android.location.GalileoAssistance.Builder setAuxiliaryInformation(@Nullable android.location.AuxiliaryInformation);
- method @NonNull public android.location.GalileoAssistance.Builder setIonosphericModel(@Nullable android.location.KlobucharIonosphericModel);
+ method @NonNull public android.location.GalileoAssistance.Builder setIonosphericModel(@Nullable android.location.GalileoIonosphericModel);
method @NonNull public android.location.GalileoAssistance.Builder setLeapSecondsModel(@Nullable android.location.LeapSecondsModel);
method @NonNull public android.location.GalileoAssistance.Builder setRealTimeIntegrityModels(@NonNull java.util.List<android.location.RealTimeIntegrityModel>);
method @NonNull public android.location.GalileoAssistance.Builder setSatelliteCorrections(@NonNull java.util.List<android.location.GnssAssistance.GnssSatelliteCorrections>);
diff --git a/location/java/android/location/BeidouSatelliteEphemeris.java b/location/java/android/location/BeidouSatelliteEphemeris.java
index 3382c20964d9..dc5e8b89d7d7 100644
--- a/location/java/android/location/BeidouSatelliteEphemeris.java
+++ b/location/java/android/location/BeidouSatelliteEphemeris.java
@@ -527,7 +527,7 @@ public final class BeidouSatelliteEphemeris implements Parcelable {
*
* <p>This is defined in BDS-SIS-ICD-B1I-3.0 section 5.2.4.11 Table 5-8.
*/
- private final int mIode;
+ private final int mAode;
/** Beidou week number without rollover */
private final int mBeidouWeekNumber;
@@ -540,18 +540,18 @@ public final class BeidouSatelliteEphemeris implements Parcelable {
private final int mToeSeconds;
private BeidouSatelliteEphemerisTime(Builder builder) {
- Preconditions.checkArgumentInRange(builder.mIode, 0, 31, "Iode");
+ Preconditions.checkArgumentInRange(builder.mAode, 0, 31, "Aode");
Preconditions.checkArgument(builder.mBeidouWeekNumber >= 0);
Preconditions.checkArgumentInRange(builder.mToeSeconds, 0, 604792, "ToeSeconds");
- mIode = builder.mIode;
+ mAode = builder.mAode;
mBeidouWeekNumber = builder.mBeidouWeekNumber;
mToeSeconds = builder.mToeSeconds;
}
/** Returns the AODE Age of Data, Ephemeris. */
@IntRange(from = 0, to = 31)
- public int getIode() {
- return mIode;
+ public int getAode() {
+ return mAode;
}
/** Returns the Beidou week number without rollover . */
@@ -573,7 +573,7 @@ public final class BeidouSatelliteEphemeris implements Parcelable {
public BeidouSatelliteEphemerisTime createFromParcel(Parcel in) {
final BeidouSatelliteEphemerisTime.Builder beidouSatelliteEphemerisTime =
new Builder()
- .setIode(in.readInt())
+ .setAode(in.readInt())
.setBeidouWeekNumber(in.readInt())
.setToeSeconds(in.readInt());
return beidouSatelliteEphemerisTime.build();
@@ -592,7 +592,7 @@ public final class BeidouSatelliteEphemeris implements Parcelable {
@Override
public void writeToParcel(@NonNull Parcel parcel, int flags) {
- parcel.writeInt(mIode);
+ parcel.writeInt(mAode);
parcel.writeInt(mBeidouWeekNumber);
parcel.writeInt(mToeSeconds);
}
@@ -600,7 +600,7 @@ public final class BeidouSatelliteEphemeris implements Parcelable {
@Override
public String toString() {
StringBuilder builder = new StringBuilder("BeidouSatelliteEphemerisTime[");
- builder.append("iode = ").append(mIode);
+ builder.append("aode = ").append(mAode);
builder.append(", beidouWeekNumber = ").append(mBeidouWeekNumber);
builder.append(", toeSeconds = ").append(mToeSeconds);
builder.append("]");
@@ -609,14 +609,14 @@ public final class BeidouSatelliteEphemeris implements Parcelable {
/** Builder for {@link BeidouSatelliteEphemerisTime} */
public static final class Builder {
- private int mIode;
+ private int mAode;
private int mBeidouWeekNumber;
private int mToeSeconds;
/** Sets the AODE Age of Data, Ephemeris. */
@NonNull
- public Builder setIode(int iode) {
- mIode = iode;
+ public Builder setAode(int iode) {
+ mAode = iode;
return this;
}
diff --git a/location/java/android/location/GalileoAssistance.java b/location/java/android/location/GalileoAssistance.java
index 8a09e6634d09..7f81ccdf346f 100644
--- a/location/java/android/location/GalileoAssistance.java
+++ b/location/java/android/location/GalileoAssistance.java
@@ -41,8 +41,8 @@ public final class GalileoAssistance implements Parcelable {
/** The Galileo almanac. */
@Nullable private final GnssAlmanac mAlmanac;
- /** The Klobuchar ionospheric model. */
- @Nullable private final KlobucharIonosphericModel mIonosphericModel;
+ /** The Galileo ionospheric model. */
+ @Nullable private final GalileoIonosphericModel mIonosphericModel;
/** The UTC model. */
@Nullable private final UtcModel mUtcModel;
@@ -102,9 +102,9 @@ public final class GalileoAssistance implements Parcelable {
return mAlmanac;
}
- /** Returns the Klobuchar ionospheric model. */
+ /** Returns the Galileo ionospheric model. */
@Nullable
- public KlobucharIonosphericModel getIonosphericModel() {
+ public GalileoIonosphericModel getIonosphericModel() {
return mIonosphericModel;
}
@@ -192,7 +192,7 @@ public final class GalileoAssistance implements Parcelable {
return new GalileoAssistance.Builder()
.setAlmanac(in.readTypedObject(GnssAlmanac.CREATOR))
.setIonosphericModel(
- in.readTypedObject(KlobucharIonosphericModel.CREATOR))
+ in.readTypedObject(GalileoIonosphericModel.CREATOR))
.setUtcModel(in.readTypedObject(UtcModel.CREATOR))
.setLeapSecondsModel(in.readTypedObject(LeapSecondsModel.CREATOR))
.setAuxiliaryInformation(
@@ -216,7 +216,7 @@ public final class GalileoAssistance implements Parcelable {
/** Builder for {@link GalileoAssistance}. */
public static final class Builder {
private GnssAlmanac mAlmanac;
- private KlobucharIonosphericModel mIonosphericModel;
+ private GalileoIonosphericModel mIonosphericModel;
private UtcModel mUtcModel;
private LeapSecondsModel mLeapSecondsModel;
private AuxiliaryInformation mAuxiliaryInformation;
@@ -232,9 +232,9 @@ public final class GalileoAssistance implements Parcelable {
return this;
}
- /** Sets the Klobuchar ionospheric model. */
+ /** Sets the Galileo ionospheric model. */
@NonNull
- public Builder setIonosphericModel(@Nullable KlobucharIonosphericModel ionosphericModel) {
+ public Builder setIonosphericModel(@Nullable GalileoIonosphericModel ionosphericModel) {
mIonosphericModel = ionosphericModel;
return this;
}
diff --git a/media/java/android/media/AudioDeviceVolumeManager.java b/media/java/android/media/AudioDeviceVolumeManager.java
index 56d3df3b2555..311d64f6c7e8 100644
--- a/media/java/android/media/AudioDeviceVolumeManager.java
+++ b/media/java/android/media/AudioDeviceVolumeManager.java
@@ -29,6 +29,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
+import android.annotation.TestApi;
import android.content.Context;
import android.os.IBinder;
import android.os.RemoteException;
@@ -52,6 +53,127 @@ public class AudioDeviceVolumeManager {
private static final String TAG = "AudioDeviceVolumeManager";
+ /**
+ * @hide
+ * Volume behavior for an audio device that has no particular volume behavior set. Invalid as
+ * an argument to {@link #setDeviceVolumeBehavior(AudioDeviceAttributes, int)} and should not
+ * be returned by {@link #getDeviceVolumeBehavior(AudioDeviceAttributes)}.
+ */
+ public static final int DEVICE_VOLUME_BEHAVIOR_UNSET = -1;
+ /**
+ * @hide
+ * Volume behavior for an audio device where a software attenuation is applied
+ * @see #setDeviceVolumeBehavior(AudioDeviceAttributes, int)
+ */
+ @SystemApi
+ @FlaggedApi(FLAG_UNIFY_ABSOLUTE_VOLUME_MANAGEMENT)
+ public static final int DEVICE_VOLUME_BEHAVIOR_VARIABLE = 0;
+ /**
+ * @hide
+ * Volume behavior for an audio device where the volume is always set to provide no attenuation
+ * nor gain (e.g. unit gain).
+ * @see #setDeviceVolumeBehavior(AudioDeviceAttributes, int)
+ */
+ @SystemApi
+ @FlaggedApi(FLAG_UNIFY_ABSOLUTE_VOLUME_MANAGEMENT)
+ public static final int DEVICE_VOLUME_BEHAVIOR_FULL = 1;
+ /**
+ * @hide
+ * Volume behavior for an audio device where the volume is either set to muted, or to provide
+ * no attenuation nor gain (e.g. unit gain).
+ * @see #setDeviceVolumeBehavior(AudioDeviceAttributes, int)
+ */
+ @SystemApi
+ @FlaggedApi(FLAG_UNIFY_ABSOLUTE_VOLUME_MANAGEMENT)
+ public static final int DEVICE_VOLUME_BEHAVIOR_FIXED = 2;
+ /**
+ * @hide
+ * Volume behavior for an audio device where no software attenuation is applied, and
+ * the volume is kept synchronized between the host and the device itself through a
+ * device-specific protocol such as BT AVRCP.
+ * @see #setDeviceVolumeBehavior(AudioDeviceAttributes, int)
+ */
+ @SystemApi
+ @FlaggedApi(FLAG_UNIFY_ABSOLUTE_VOLUME_MANAGEMENT)
+ public static final int DEVICE_VOLUME_BEHAVIOR_ABSOLUTE = 3;
+ /**
+ * @hide
+ * Volume behavior for an audio device where no software attenuation is applied, and
+ * the volume is kept synchronized between the host and the device itself through a
+ * device-specific protocol (such as for hearing aids), based on the audio mode (e.g.
+ * normal vs in phone call).
+ * @see AudioManager#setMode(int)
+ * @see #setDeviceVolumeBehavior(AudioDeviceAttributes, int)
+ */
+ @SystemApi
+ @FlaggedApi(FLAG_UNIFY_ABSOLUTE_VOLUME_MANAGEMENT)
+ public static final int DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_MULTI_MODE = 4;
+
+ /**
+ * @hide
+ * A variant of {@link #DEVICE_VOLUME_BEHAVIOR_ABSOLUTE} where the host cannot reliably set
+ * the volume percentage of the audio device. Specifically, {@link AudioManager#setStreamVolume}
+ * will have no effect, or an unreliable effect.
+ */
+ @SystemApi
+ @FlaggedApi(FLAG_UNIFY_ABSOLUTE_VOLUME_MANAGEMENT)
+ public static final int DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_ADJUST_ONLY = 5;
+
+ /** @hide */
+ @IntDef({
+ DEVICE_VOLUME_BEHAVIOR_VARIABLE,
+ DEVICE_VOLUME_BEHAVIOR_FULL,
+ DEVICE_VOLUME_BEHAVIOR_FIXED,
+ DEVICE_VOLUME_BEHAVIOR_ABSOLUTE,
+ DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_MULTI_MODE,
+ DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_ADJUST_ONLY,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface DeviceVolumeBehavior {}
+
+ /** @hide */
+ @IntDef({
+ DEVICE_VOLUME_BEHAVIOR_UNSET,
+ DEVICE_VOLUME_BEHAVIOR_VARIABLE,
+ DEVICE_VOLUME_BEHAVIOR_FULL,
+ DEVICE_VOLUME_BEHAVIOR_FIXED,
+ DEVICE_VOLUME_BEHAVIOR_ABSOLUTE,
+ DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_MULTI_MODE,
+ DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_ADJUST_ONLY,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface DeviceVolumeBehaviorState {}
+
+ /**
+ * Variants of absolute volume behavior that are set in for absolute volume management.
+ * @hide
+ */
+ @IntDef({
+ DEVICE_VOLUME_BEHAVIOR_ABSOLUTE,
+ DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_ADJUST_ONLY,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface AbsoluteDeviceVolumeBehavior {}
+
+ /**
+ * @hide
+ * Throws IAE on an invalid volume behavior value
+ * @param volumeBehavior behavior value to check
+ */
+ public static void enforceValidVolumeBehavior(int volumeBehavior) {
+ switch (volumeBehavior) {
+ case DEVICE_VOLUME_BEHAVIOR_VARIABLE:
+ case DEVICE_VOLUME_BEHAVIOR_FULL:
+ case DEVICE_VOLUME_BEHAVIOR_FIXED:
+ case DEVICE_VOLUME_BEHAVIOR_ABSOLUTE:
+ case DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_MULTI_MODE:
+ case DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_ADJUST_ONLY:
+ return;
+ default:
+ throw new IllegalArgumentException("Illegal volume behavior " + volumeBehavior);
+ }
+ }
+
/** @hide
* Indicates no special treatment in the handling of the volume adjustment */
public static final int ADJUST_MODE_NORMAL = 0;
@@ -158,7 +280,7 @@ public class AudioDeviceVolumeManager {
android.Manifest.permission.BLUETOOTH_PRIVILEGED })
public void register(boolean register, @NonNull AudioDeviceAttributes device,
@NonNull List<VolumeInfo> volumes, boolean handlesVolumeAdjustment,
- @AudioManager.AbsoluteDeviceVolumeBehavior int behavior) {
+ @AbsoluteDeviceVolumeBehavior int behavior) {
try {
getService().registerDeviceVolumeDispatcherForAbsoluteVolume(register,
this, mPackageName,
@@ -204,6 +326,94 @@ public class AudioDeviceVolumeManager {
/**
* @hide
+ * Sets the volume behavior for an audio output device.
+ * @see #DEVICE_VOLUME_BEHAVIOR_VARIABLE
+ * @see #DEVICE_VOLUME_BEHAVIOR_FULL
+ * @see #DEVICE_VOLUME_BEHAVIOR_FIXED
+ * @see #DEVICE_VOLUME_BEHAVIOR_ABSOLUTE
+ * @see #DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_MULTI_MODE
+ * @param device the device to be affected
+ * @param deviceVolumeBehavior one of the device behaviors
+ */
+ @SystemApi
+ @RequiresPermission(anyOf = {
+ Manifest.permission.MODIFY_AUDIO_ROUTING,
+ Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED
+ })
+ @FlaggedApi(FLAG_UNIFY_ABSOLUTE_VOLUME_MANAGEMENT)
+ public void setDeviceVolumeBehavior(@NonNull AudioDeviceAttributes device,
+ @DeviceVolumeBehavior int deviceVolumeBehavior) {
+ // verify arguments (validity of device type is enforced in server)
+ Objects.requireNonNull(device);
+ enforceValidVolumeBehavior(deviceVolumeBehavior);
+ // communicate with service
+ final IAudioService service = getService();
+ try {
+ service.setDeviceVolumeBehavior(device, deviceVolumeBehavior, mPackageName);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * @hide
+ * Returns the volume device behavior for the given audio device
+ * @param device the audio device
+ * @return the volume behavior for the device
+ */
+ @SystemApi
+ @RequiresPermission(anyOf = {
+ Manifest.permission.MODIFY_AUDIO_ROUTING,
+ Manifest.permission.QUERY_AUDIO_STATE,
+ Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED
+ })
+ @FlaggedApi(FLAG_UNIFY_ABSOLUTE_VOLUME_MANAGEMENT)
+ public @DeviceVolumeBehavior int getDeviceVolumeBehavior(
+ @NonNull AudioDeviceAttributes device) {
+ // verify arguments (validity of device type is enforced in server)
+ Objects.requireNonNull(device);
+ // communicate with service
+ final IAudioService service = getService();
+ try {
+ return service.getDeviceVolumeBehavior(device);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * @hide
+ * Returns {@code true} if the volume device behavior is {@link #DEVICE_VOLUME_BEHAVIOR_FULL}.
+ */
+ @TestApi
+ @RequiresPermission(anyOf = {
+ Manifest.permission.MODIFY_AUDIO_ROUTING,
+ Manifest.permission.QUERY_AUDIO_STATE,
+ Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED
+ })
+ @SuppressWarnings("UnflaggedApi") // @TestApi without associated feature.
+ public boolean isFullVolumeDevice() {
+ final AudioAttributes attributes = new AudioAttributes.Builder()
+ .setUsage(AudioAttributes.USAGE_MEDIA)
+ .build();
+ List<AudioDeviceAttributes> devices;
+ final IAudioService service = getService();
+ try {
+ devices = service.getDevicesForAttributes(attributes);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+
+ for (AudioDeviceAttributes device : devices) {
+ if (getDeviceVolumeBehavior(device) == DEVICE_VOLUME_BEHAVIOR_FULL) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * @hide
* Configures a device to use absolute volume model, and registers a listener for receiving
* volume updates to apply on that device
* @param device the audio device set to absolute volume mode
@@ -297,7 +507,7 @@ public class AudioDeviceVolumeManager {
@NonNull @CallbackExecutor Executor executor,
@NonNull OnAudioDeviceVolumeChangedListener vclistener) {
baseSetDeviceAbsoluteMultiVolumeBehavior(device, volumes, executor, vclistener,
- handlesVolumeAdjustment, AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE);
+ handlesVolumeAdjustment, DEVICE_VOLUME_BEHAVIOR_ABSOLUTE);
}
/**
@@ -355,12 +565,12 @@ public class AudioDeviceVolumeManager {
@NonNull @CallbackExecutor Executor executor,
@NonNull OnAudioDeviceVolumeChangedListener vclistener) {
baseSetDeviceAbsoluteMultiVolumeBehavior(device, volumes, executor, vclistener,
- handlesVolumeAdjustment, AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_ADJUST_ONLY);
+ handlesVolumeAdjustment, DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_ADJUST_ONLY);
}
/**
* Base method for configuring a device to use absolute volume behavior, or one of its variants.
- * See {@link AudioManager.AbsoluteDeviceVolumeBehavior} for a list of allowed behaviors.
+ * See {@link AbsoluteDeviceVolumeBehavior} for a list of allowed behaviors.
*
* @param behavior the variant of absolute device volume behavior to adopt
*/
@@ -372,7 +582,7 @@ public class AudioDeviceVolumeManager {
@NonNull @CallbackExecutor Executor executor,
@NonNull OnAudioDeviceVolumeChangedListener vclistener,
boolean handlesVolumeAdjustment,
- @AudioManager.AbsoluteDeviceVolumeBehavior int behavior) {
+ @AbsoluteDeviceVolumeBehavior int behavior) {
Objects.requireNonNull(device);
Objects.requireNonNull(volumes);
Objects.requireNonNull(executor);
@@ -417,7 +627,7 @@ public class AudioDeviceVolumeManager {
*/
void onDeviceVolumeBehaviorChanged(
@NonNull AudioDeviceAttributes device,
- @AudioManager.DeviceVolumeBehavior int volumeBehavior);
+ @DeviceVolumeBehavior int volumeBehavior);
}
/**
@@ -580,19 +790,19 @@ public class AudioDeviceVolumeManager {
* @param behavior one of the volume behaviors defined in AudioManager
* @return a string for the given behavior
*/
- public static String volumeBehaviorName(@AudioManager.DeviceVolumeBehavior int behavior) {
+ public static String volumeBehaviorName(@DeviceVolumeBehavior int behavior) {
switch (behavior) {
- case AudioManager.DEVICE_VOLUME_BEHAVIOR_VARIABLE:
+ case DEVICE_VOLUME_BEHAVIOR_VARIABLE:
return "DEVICE_VOLUME_BEHAVIOR_VARIABLE";
- case AudioManager.DEVICE_VOLUME_BEHAVIOR_FULL:
+ case DEVICE_VOLUME_BEHAVIOR_FULL:
return "DEVICE_VOLUME_BEHAVIOR_FULL";
- case AudioManager.DEVICE_VOLUME_BEHAVIOR_FIXED:
+ case DEVICE_VOLUME_BEHAVIOR_FIXED:
return "DEVICE_VOLUME_BEHAVIOR_FIXED";
- case AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE:
+ case DEVICE_VOLUME_BEHAVIOR_ABSOLUTE:
return "DEVICE_VOLUME_BEHAVIOR_ABSOLUTE";
- case AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_MULTI_MODE:
+ case DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_MULTI_MODE:
return "DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_MULTI_MODE";
- case AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_ADJUST_ONLY:
+ case DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_ADJUST_ONLY:
return "DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_ADJUST_ONLY";
default:
return "invalid volume behavior " + behavior;
@@ -611,7 +821,7 @@ public class AudioDeviceVolumeManager {
@Override
public void dispatchDeviceVolumeBehaviorChanged(@NonNull AudioDeviceAttributes device,
- @AudioManager.DeviceVolumeBehavior int volumeBehavior) {
+ @DeviceVolumeBehavior int volumeBehavior) {
mDeviceVolumeBehaviorChangedListenerMgr.callListeners((listener) ->
listener.onDeviceVolumeBehaviorChanged(device, volumeBehavior));
}
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 32af7c6fca68..0a1bfd55e77f 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -19,13 +19,15 @@ package android.media;
import static android.companion.virtual.VirtualDeviceParams.DEVICE_POLICY_DEFAULT;
import static android.companion.virtual.VirtualDeviceParams.POLICY_TYPE_AUDIO;
import static android.content.Context.DEVICE_ID_DEFAULT;
-import static android.media.audio.Flags.autoPublicVolumeApiHardening;
-import static android.media.audio.Flags.cacheGetStreamMinMaxVolume;
-import static android.media.audio.Flags.cacheGetStreamVolume;
import static android.media.audio.Flags.FLAG_DEPRECATE_STREAM_BT_SCO;
import static android.media.audio.Flags.FLAG_FOCUS_EXCLUSIVE_WITH_RECORDING;
import static android.media.audio.Flags.FLAG_FOCUS_FREEZE_TEST_API;
+import static android.media.audio.Flags.FLAG_REGISTER_VOLUME_CALLBACK_API_HARDENING;
import static android.media.audio.Flags.FLAG_SUPPORTED_DEVICE_TYPES_API;
+import static android.media.audio.Flags.FLAG_UNIFY_ABSOLUTE_VOLUME_MANAGEMENT;
+import static android.media.audio.Flags.autoPublicVolumeApiHardening;
+import static android.media.audio.Flags.cacheGetStreamMinMaxVolume;
+import static android.media.audio.Flags.cacheGetStreamVolume;
import static android.media.audiopolicy.Flags.FLAG_ENABLE_FADE_MANAGER_CONFIGURATION;
import android.Manifest;
@@ -64,7 +66,7 @@ import android.media.audiopolicy.AudioPolicy;
import android.media.audiopolicy.AudioPolicy.AudioPolicyFocusListener;
import android.media.audiopolicy.AudioProductStrategy;
import android.media.audiopolicy.AudioVolumeGroup;
-import android.media.audiopolicy.AudioVolumeGroupChangeHandler;
+import android.media.audiopolicy.IAudioVolumeChangeDispatcher;
import android.media.projection.MediaProjection;
import android.media.session.MediaController;
import android.media.session.MediaSession;
@@ -127,8 +129,6 @@ public class AudioManager {
private static final String TAG = "AudioManager";
private static final boolean DEBUG = false;
private static final AudioPortEventHandler sAudioPortEventHandler = new AudioPortEventHandler();
- private static final AudioVolumeGroupChangeHandler sAudioAudioVolumeGroupChangedHandler =
- new AudioVolumeGroupChangeHandler();
private static WeakReference<Context> sContext;
@@ -6659,24 +6659,30 @@ public class AudioManager {
* @hide
* Volume behavior for an audio device where a software attenuation is applied
* @see #setDeviceVolumeBehavior(AudioDeviceAttributes, int)
+ * @deprecated use {@link AudioDeviceVolumeManager#DEVICE_VOLUME_BEHAVIOR_VARIABLE} instead
*/
@SystemApi
+ @FlaggedApi(FLAG_UNIFY_ABSOLUTE_VOLUME_MANAGEMENT)
public static final int DEVICE_VOLUME_BEHAVIOR_VARIABLE = 0;
/**
* @hide
* Volume behavior for an audio device where the volume is always set to provide no attenuation
* nor gain (e.g. unit gain).
* @see #setDeviceVolumeBehavior(AudioDeviceAttributes, int)
+ * @deprecated use {@link AudioDeviceVolumeManager#DEVICE_VOLUME_BEHAVIOR_FULL} instead
*/
@SystemApi
+ @FlaggedApi(FLAG_UNIFY_ABSOLUTE_VOLUME_MANAGEMENT)
public static final int DEVICE_VOLUME_BEHAVIOR_FULL = 1;
/**
* @hide
* Volume behavior for an audio device where the volume is either set to muted, or to provide
* no attenuation nor gain (e.g. unit gain).
* @see #setDeviceVolumeBehavior(AudioDeviceAttributes, int)
+ * @deprecated use {@link AudioDeviceVolumeManager#DEVICE_VOLUME_BEHAVIOR_FIXED} instead
*/
@SystemApi
+ @FlaggedApi(FLAG_UNIFY_ABSOLUTE_VOLUME_MANAGEMENT)
public static final int DEVICE_VOLUME_BEHAVIOR_FIXED = 2;
/**
* @hide
@@ -6684,8 +6690,10 @@ public class AudioManager {
* the volume is kept synchronized between the host and the device itself through a
* device-specific protocol such as BT AVRCP.
* @see #setDeviceVolumeBehavior(AudioDeviceAttributes, int)
+ * @deprecated use {@link AudioDeviceVolumeManager#DEVICE_VOLUME_BEHAVIOR_ABSOLUTE} instead
*/
@SystemApi
+ @FlaggedApi(FLAG_UNIFY_ABSOLUTE_VOLUME_MANAGEMENT)
public static final int DEVICE_VOLUME_BEHAVIOR_ABSOLUTE = 3;
/**
* @hide
@@ -6695,8 +6703,11 @@ public class AudioManager {
* normal vs in phone call).
* @see #setMode(int)
* @see #setDeviceVolumeBehavior(AudioDeviceAttributes, int)
+ * @deprecated use {@link AudioDeviceVolumeManager#DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_MULTI_MODE}
+ * instead
*/
@SystemApi
+ @FlaggedApi(FLAG_UNIFY_ABSOLUTE_VOLUME_MANAGEMENT)
public static final int DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_MULTI_MODE = 4;
/**
@@ -6704,8 +6715,11 @@ public class AudioManager {
* A variant of {@link #DEVICE_VOLUME_BEHAVIOR_ABSOLUTE} where the host cannot reliably set
* the volume percentage of the audio device. Specifically, {@link #setStreamVolume} will have
* no effect, or an unreliable effect.
+ * @deprecated use {@link AudioDeviceVolumeManager#DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_ADJUST_ONLY}
+ * instead
*/
@SystemApi
+ @FlaggedApi(FLAG_UNIFY_ABSOLUTE_VOLUME_MANAGEMENT)
public static final int DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_ADJUST_ONLY = 5;
/** @hide */
@@ -6720,49 +6734,6 @@ public class AudioManager {
@Retention(RetentionPolicy.SOURCE)
public @interface DeviceVolumeBehavior {}
- /** @hide */
- @IntDef({
- DEVICE_VOLUME_BEHAVIOR_UNSET,
- DEVICE_VOLUME_BEHAVIOR_VARIABLE,
- DEVICE_VOLUME_BEHAVIOR_FULL,
- DEVICE_VOLUME_BEHAVIOR_FIXED,
- DEVICE_VOLUME_BEHAVIOR_ABSOLUTE,
- DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_MULTI_MODE,
- DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_ADJUST_ONLY,
- })
- @Retention(RetentionPolicy.SOURCE)
- public @interface DeviceVolumeBehaviorState {}
-
- /**
- * Variants of absolute volume behavior that are set in {@link AudioDeviceVolumeManager}.
- * @hide
- */
- @IntDef({
- DEVICE_VOLUME_BEHAVIOR_ABSOLUTE,
- DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_ADJUST_ONLY,
- })
- @Retention(RetentionPolicy.SOURCE)
- public @interface AbsoluteDeviceVolumeBehavior {}
-
- /**
- * @hide
- * Throws IAE on an invalid volume behavior value
- * @param volumeBehavior behavior value to check
- */
- public static void enforceValidVolumeBehavior(int volumeBehavior) {
- switch (volumeBehavior) {
- case DEVICE_VOLUME_BEHAVIOR_VARIABLE:
- case DEVICE_VOLUME_BEHAVIOR_FULL:
- case DEVICE_VOLUME_BEHAVIOR_FIXED:
- case DEVICE_VOLUME_BEHAVIOR_ABSOLUTE:
- case DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_MULTI_MODE:
- case DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_ADJUST_ONLY:
- return;
- default:
- throw new IllegalArgumentException("Illegal volume behavior " + volumeBehavior);
- }
- }
-
/**
* @hide
* Sets the volume behavior for an audio output device.
@@ -6773,17 +6744,21 @@ public class AudioManager {
* @see #DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_MULTI_MODE
* @param device the device to be affected
* @param deviceVolumeBehavior one of the device behaviors
+ *
+ * @deprecated use
+ * {@link AudioDeviceVolumeManager#setDeviceVolumeBehavior(AudioDeviceAttributes, int)} instead
*/
@SystemApi
@RequiresPermission(anyOf = {
Manifest.permission.MODIFY_AUDIO_ROUTING,
Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED
})
+ @FlaggedApi(FLAG_UNIFY_ABSOLUTE_VOLUME_MANAGEMENT)
public void setDeviceVolumeBehavior(@NonNull AudioDeviceAttributes device,
@DeviceVolumeBehavior int deviceVolumeBehavior) {
// verify arguments (validity of device type is enforced in server)
Objects.requireNonNull(device);
- enforceValidVolumeBehavior(deviceVolumeBehavior);
+ AudioDeviceVolumeManager.enforceValidVolumeBehavior(deviceVolumeBehavior);
// communicate with service
final IAudioService service = getService();
try {
@@ -6810,6 +6785,8 @@ public class AudioManager {
* Returns the volume device behavior for the given audio device
* @param device the audio device
* @return the volume behavior for the device
+ * @deprecated use
+ * {@link AudioDeviceVolumeManager#getDeviceVolumeBehavior(AudioDeviceAttributes)} instead
*/
@SystemApi
@RequiresPermission(anyOf = {
@@ -6817,6 +6794,7 @@ public class AudioManager {
Manifest.permission.QUERY_AUDIO_STATE,
Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED
})
+ @FlaggedApi(FLAG_UNIFY_ABSOLUTE_VOLUME_MANAGEMENT)
public @DeviceVolumeBehavior
int getDeviceVolumeBehavior(@NonNull AudioDeviceAttributes device) {
// verify arguments (validity of device type is enforced in server)
@@ -6836,29 +6814,6 @@ public class AudioManager {
}
/**
- * @hide
- * Returns {@code true} if the volume device behavior is {@link #DEVICE_VOLUME_BEHAVIOR_FULL}.
- */
- @TestApi
- @RequiresPermission(anyOf = {
- Manifest.permission.MODIFY_AUDIO_ROUTING,
- Manifest.permission.QUERY_AUDIO_STATE,
- Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED
- })
- public boolean isFullVolumeDevice() {
- final AudioAttributes attributes = new AudioAttributes.Builder()
- .setUsage(AudioAttributes.USAGE_MEDIA)
- .build();
- final List<AudioDeviceAttributes> devices = getDevicesForAttributes(attributes);
- for (AudioDeviceAttributes device : devices) {
- if (getDeviceVolumeBehavior(device) == DEVICE_VOLUME_BEHAVIOR_FULL) {
- return true;
- }
- }
- return false;
- }
-
- /**
* Indicate wired accessory connection state change.
* @param device type of device connected/disconnected (AudioManager.DEVICE_OUT_xxx)
* @param state new connection state: 1 connected, 0 disconnected
@@ -8805,9 +8760,13 @@ public class AudioManager {
}
}
+ //====================================================================
+ // Notification of volume group changes
/**
+ * Callback to receive updates on volume group changes, register using
+ * {@link AudioManager#registerVolumeGroupCallback(Executor, AudioVolumeCallback)}.
+ *
* @hide
- * Callback registered by client to be notified upon volume group change.
*/
@SystemApi
public abstract static class VolumeGroupCallback {
@@ -8818,35 +8777,70 @@ public class AudioManager {
public void onAudioVolumeGroupChanged(int group, int flags) {}
}
- /**
- * @hide
- * Register an audio volume group change listener.
- * @param callback the {@link VolumeGroupCallback} to register
- */
+ /**
+ * Register an audio volume group change listener.
+ *
+ * @param executor {@link Executor} to handle the callbacks
+ * @param callback the callback to receive the audio volume group changes
+ * @throws SecurityException if the caller doesn't have the required permission.
+ *
+ * @hide
+ */
@SystemApi
- public void registerVolumeGroupCallback(
- @NonNull Executor executor,
+ @FlaggedApi(FLAG_REGISTER_VOLUME_CALLBACK_API_HARDENING)
+ @RequiresPermission("Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED")
+ public void registerVolumeGroupCallback(@NonNull Executor executor,
@NonNull VolumeGroupCallback callback) {
- Preconditions.checkNotNull(executor, "executor must not be null");
- Preconditions.checkNotNull(callback, "volume group change cb must not be null");
- sAudioAudioVolumeGroupChangedHandler.init();
- // TODO: make use of executor
- sAudioAudioVolumeGroupChangedHandler.registerListener(callback);
+ mVolumeChangedListenerMgr.addListener(executor, callback, "registerVolumeGroupCallback",
+ () -> new AudioVolumeChangeDispatcherStub());
}
- /**
- * @hide
- * Unregister an audio volume group change listener.
- * @param callback the {@link VolumeGroupCallback} to unregister
- */
+ /**
+ * Unregister an audio volume group change listener.
+ *
+ * @param callback the {@link VolumeGroupCallback} to unregister
+ *
+ * @hide
+ */
@SystemApi
- public void unregisterVolumeGroupCallback(
- @NonNull VolumeGroupCallback callback) {
- Preconditions.checkNotNull(callback, "volume group change cb must not be null");
- sAudioAudioVolumeGroupChangedHandler.unregisterListener(callback);
+ @FlaggedApi(FLAG_REGISTER_VOLUME_CALLBACK_API_HARDENING)
+ @RequiresPermission("Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED")
+ public void unregisterVolumeGroupCallback(@NonNull VolumeGroupCallback callback) {
+ mVolumeChangedListenerMgr.removeListener(callback, "unregisterVolumeGroupCallback");
}
/**
+ * Manages the VolumeGroupCallback listeners and the AudioVolumeChangeDispatcherStub
+ */
+ private final CallbackUtil.LazyListenerManager<VolumeGroupCallback> mVolumeChangedListenerMgr =
+ new CallbackUtil.LazyListenerManager();
+
+ final class AudioVolumeChangeDispatcherStub extends IAudioVolumeChangeDispatcher.Stub
+ implements CallbackUtil.DispatcherStub {
+
+ @Override
+ public void register(boolean register) {
+ try {
+ if (register) {
+ getService().registerAudioVolumeCallback(this);
+ } else {
+ getService().unregisterAudioVolumeCallback(this);
+ }
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+
+ @Override
+ public void onAudioVolumeGroupChanged(int group, int flags) {
+ mVolumeChangedListenerMgr.callListeners((listener) ->
+ listener.onAudioVolumeGroupChanged(group, flags));
+ }
+ }
+
+ //====================================================================
+
+ /**
* Return if an asset contains haptic channels or not.
*
* @param context the {@link Context} to resolve the uri.
diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java
index ad6f2e52fd97..4906cd3fb1e5 100644
--- a/media/java/android/media/AudioSystem.java
+++ b/media/java/android/media/AudioSystem.java
@@ -2732,4 +2732,25 @@ public class AudioSystem
* @hide
*/
public static native void triggerSystemPropertyUpdate(long handle);
+
+ /**
+ * Registers the given {@link INativeAudioVolumeGroupCallback} to native audioserver.
+ * @param callback to register
+ * @return {@link #SUCCESS} if successfully registered.
+ *
+ * @hide
+ */
+ public static native int registerAudioVolumeGroupCallback(
+ INativeAudioVolumeGroupCallback callback);
+
+ /**
+ * Unegisters the given {@link INativeAudioVolumeGroupCallback} from native audioserver
+ * previously registered via {@link #registerAudioVolumeGroupCallback}.
+ * @param callback to register
+ * @return {@link #SUCCESS} if successfully registered.
+ *
+ * @hide
+ */
+ public static native int unregisterAudioVolumeGroupCallback(
+ INativeAudioVolumeGroupCallback callback);
}
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index 8aadb418cf5a..b97b943113b6 100644
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -65,6 +65,7 @@ import android.media.audiopolicy.AudioPolicyConfig;
import android.media.audiopolicy.AudioProductStrategy;
import android.media.audiopolicy.AudioVolumeGroup;
import android.media.audiopolicy.IAudioPolicyCallback;
+import android.media.audiopolicy.IAudioVolumeChangeDispatcher;
import android.media.projection.IMediaProjection;
import android.net.Uri;
import android.os.PersistableBundle;
@@ -446,6 +447,10 @@ interface IAudioService {
boolean isAudioServerRunning();
+ void registerAudioVolumeCallback(IAudioVolumeChangeDispatcher avc);
+
+ oneway void unregisterAudioVolumeCallback(IAudioVolumeChangeDispatcher avc);
+
int setUidDeviceAffinity(in IAudioPolicyCallback pcb, in int uid, in int[] deviceTypes,
in String[] deviceAddresses);
diff --git a/media/java/android/media/MediaCodecInfo.java b/media/java/android/media/MediaCodecInfo.java
index f3b21bfdaa3c..3b560b7a880e 100644
--- a/media/java/android/media/MediaCodecInfo.java
+++ b/media/java/android/media/MediaCodecInfo.java
@@ -219,13 +219,14 @@ public final class MediaCodecInfo {
private static final int DEFAULT_MAX_SUPPORTED_INSTANCES = 32;
private static final int MAX_SUPPORTED_INSTANCES_LIMIT = 256;
- private static final class LazyHolder {
- private static final Range<Integer> SIZE_RANGE = Process.is64Bit()
- ? Range.create(1, 32768)
- : Range.create(1, MediaProperties.resolution_limit_32bit().orElse(4096));
- }
- private static Range<Integer> getSizeRange() {
- return LazyHolder.SIZE_RANGE;
+ private static Range<Integer> SIZE_RANGE;
+ private static synchronized Range<Integer> getSizeRange() {
+ if (SIZE_RANGE == null) {
+ SIZE_RANGE = Process.is64Bit()
+ ? Range.create(1, 32768)
+ : Range.create(1, MediaProperties.resolution_limit_32bit().orElse(4096));
+ }
+ return SIZE_RANGE;
}
// found stuff that is not supported by framework (=> this should not happen)
diff --git a/media/java/android/media/MediaRecorder.java b/media/java/android/media/MediaRecorder.java
index 7af78b81cda5..03bcc6afc1b7 100644
--- a/media/java/android/media/MediaRecorder.java
+++ b/media/java/android/media/MediaRecorder.java
@@ -1299,6 +1299,7 @@ public class MediaRecorder implements AudioRouting,
* start() or before setOutputFormat().
* @throws IOException if prepare fails otherwise.
*/
+ @RequiresPermission(value = android.Manifest.permission.RECORD_AUDIO, conditional = true)
public void prepare() throws IllegalStateException, IOException
{
if (mPath != null) {
@@ -1337,6 +1338,7 @@ public class MediaRecorder implements AudioRouting,
* @throws IllegalStateException if it is called before
* prepare() or when the camera is already in use by another app.
*/
+ @RequiresPermission(value = android.Manifest.permission.RECORD_AUDIO, conditional = true)
public native void start() throws IllegalStateException;
/**
diff --git a/media/java/android/media/audiopolicy/AudioVolumeGroupChangeHandler.java b/media/java/android/media/audiopolicy/AudioVolumeGroupChangeHandler.java
deleted file mode 100644
index 022cfeeb4e43..000000000000
--- a/media/java/android/media/audiopolicy/AudioVolumeGroupChangeHandler.java
+++ /dev/null
@@ -1,166 +0,0 @@
-/*
- * Copyright (C) 2018 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 android.media.audiopolicy;
-
-import android.annotation.NonNull;
-import android.media.AudioManager;
-import android.os.Handler;
-import android.os.HandlerThread;
-import android.os.Message;
-
-import com.android.internal.util.Preconditions;
-
-import java.lang.ref.WeakReference;
-import java.util.ArrayList;
-
-/**
- * The AudioVolumeGroupChangeHandler handles AudioManager.OnAudioVolumeGroupChangedListener
- * callbacks posted from JNI
- *
- * TODO: Make use of Executor of callbacks.
- * @hide
- */
-public class AudioVolumeGroupChangeHandler {
- private Handler mHandler;
- private HandlerThread mHandlerThread;
- private final ArrayList<AudioManager.VolumeGroupCallback> mListeners =
- new ArrayList<AudioManager.VolumeGroupCallback>();
-
- private static final String TAG = "AudioVolumeGroupChangeHandler";
-
- private static final int AUDIOVOLUMEGROUP_EVENT_VOLUME_CHANGED = 1000;
- private static final int AUDIOVOLUMEGROUP_EVENT_NEW_LISTENER = 4;
-
- /**
- * Accessed by native methods: JNI Callback context.
- */
- @SuppressWarnings("unused")
- private long mJniCallback;
-
- /**
- * Initialization
- */
- public void init() {
- synchronized (this) {
- if (mHandler != null) {
- return;
- }
- // create a new thread for our new event handler
- mHandlerThread = new HandlerThread(TAG);
- mHandlerThread.start();
-
- if (mHandlerThread.getLooper() == null) {
- mHandler = null;
- return;
- }
- mHandler = new Handler(mHandlerThread.getLooper()) {
- @Override
- public void handleMessage(Message msg) {
- ArrayList<AudioManager.VolumeGroupCallback> listeners;
- synchronized (this) {
- if (msg.what == AUDIOVOLUMEGROUP_EVENT_NEW_LISTENER) {
- listeners =
- new ArrayList<AudioManager.VolumeGroupCallback>();
- if (mListeners.contains(msg.obj)) {
- listeners.add(
- (AudioManager.VolumeGroupCallback) msg.obj);
- }
- } else {
- listeners = (ArrayList<AudioManager.VolumeGroupCallback>)
- mListeners.clone();
- }
- }
- if (listeners.isEmpty()) {
- return;
- }
-
- switch (msg.what) {
- case AUDIOVOLUMEGROUP_EVENT_VOLUME_CHANGED:
- for (int i = 0; i < listeners.size(); i++) {
- listeners.get(i).onAudioVolumeGroupChanged((int) msg.arg1,
- (int) msg.arg2);
- }
- break;
-
- default:
- break;
- }
- }
- };
- native_setup(new WeakReference<AudioVolumeGroupChangeHandler>(this));
- }
- }
-
- private native void native_setup(Object moduleThis);
-
- @Override
- protected void finalize() {
- native_finalize();
- if (mHandlerThread.isAlive()) {
- mHandlerThread.quit();
- }
- }
- private native void native_finalize();
-
- /**
- * @param cb the {@link AudioManager.VolumeGroupCallback} to register
- */
- public void registerListener(@NonNull AudioManager.VolumeGroupCallback cb) {
- Preconditions.checkNotNull(cb, "volume group callback shall not be null");
- synchronized (this) {
- mListeners.add(cb);
- }
- if (mHandler != null) {
- Message m = mHandler.obtainMessage(
- AUDIOVOLUMEGROUP_EVENT_NEW_LISTENER, 0, 0, cb);
- mHandler.sendMessage(m);
- }
- }
-
- /**
- * @param cb the {@link AudioManager.VolumeGroupCallback} to unregister
- */
- public void unregisterListener(@NonNull AudioManager.VolumeGroupCallback cb) {
- Preconditions.checkNotNull(cb, "volume group callback shall not be null");
- synchronized (this) {
- mListeners.remove(cb);
- }
- }
-
- Handler handler() {
- return mHandler;
- }
-
- @SuppressWarnings("unused")
- private static void postEventFromNative(Object moduleRef,
- int what, int arg1, int arg2, Object obj) {
- AudioVolumeGroupChangeHandler eventHandler =
- (AudioVolumeGroupChangeHandler) ((WeakReference) moduleRef).get();
- if (eventHandler == null) {
- return;
- }
-
- if (eventHandler != null) {
- Handler handler = eventHandler.handler();
- if (handler != null) {
- Message m = handler.obtainMessage(what, arg1, arg2, obj);
- // Do not remove previous messages, as we would lose notification of group changes
- handler.sendMessage(m);
- }
- }
- }
-}
diff --git a/media/java/android/media/audiopolicy/IAudioVolumeChangeDispatcher.aidl b/media/java/android/media/audiopolicy/IAudioVolumeChangeDispatcher.aidl
new file mode 100644
index 000000000000..e6f9024cfd1e
--- /dev/null
+++ b/media/java/android/media/audiopolicy/IAudioVolumeChangeDispatcher.aidl
@@ -0,0 +1,31 @@
+/* Copyright (C) 2025 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 android.media.audiopolicy;
+
+/**
+ * AIDL for the AudioService to signal audio volume groups changes
+ *
+ * {@hide}
+ */
+oneway interface IAudioVolumeChangeDispatcher {
+
+ /**
+ * Called when a volume group has been changed
+ * @param group id of the volume group that has changed.
+ * @param flags one or more flags to describe the volume change.
+ */
+ void onAudioVolumeGroupChanged(int group, int flags);
+}
diff --git a/media/java/android/media/flags/media_better_together.aconfig b/media/java/android/media/flags/media_better_together.aconfig
index c9ec31bab048..7221f1ddeb7f 100644
--- a/media/java/android/media/flags/media_better_together.aconfig
+++ b/media/java/android/media/flags/media_better_together.aconfig
@@ -259,3 +259,10 @@ flag {
purpose: PURPOSE_BUGFIX
}
}
+
+flag {
+ name: "enable_output_switcher_personal_audio_sharing"
+ namespace: "cross_device_experiences"
+ description: "Enables personal audio sharing in the output switcher."
+ bug: "385672684"
+} \ No newline at end of file
diff --git a/media/java/android/media/projection/TEST_MAPPING b/media/java/android/media/projection/TEST_MAPPING
index b33097c50002..ea62287b7411 100644
--- a/media/java/android/media/projection/TEST_MAPPING
+++ b/media/java/android/media/projection/TEST_MAPPING
@@ -1,7 +1,7 @@
{
- "presubmit": [
+ "imports": [
{
- "name": "MediaProjectionTests"
+ "path": "frameworks/base/services/core/java/com/android/server/media/projection"
}
]
-}
+} \ No newline at end of file
diff --git a/media/java/android/media/quality/MediaQualityContract.java b/media/java/android/media/quality/MediaQualityContract.java
index fccdba8e727f..ece87a66556f 100644
--- a/media/java/android/media/quality/MediaQualityContract.java
+++ b/media/java/android/media/quality/MediaQualityContract.java
@@ -72,6 +72,43 @@ public class MediaQualityContract {
*/
public static final String LEVEL_OFF = "level_off";
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @StringDef(prefix = "COLOR_TEMP", value = {
+ COLOR_TEMP_USER,
+ COLOR_TEMP_COOL,
+ COLOR_TEMP_STANDARD,
+ COLOR_TEMP_WARM,
+ COLOR_TEMP_USER_HDR10PLUS,
+ COLOR_TEMP_COOL_HDR10PLUS,
+ COLOR_TEMP_STANDARD_HDR10PLUS,
+ COLOR_TEMP_WARM_HDR10PLUS,
+ COLOR_TEMP_FMMSDR,
+ COLOR_TEMP_FMMHDR,
+ })
+ public @interface ColorTempValue {}
+
+ /** @hide */
+ public static final String COLOR_TEMP_USER = "color_temp_user";
+ /** @hide */
+ public static final String COLOR_TEMP_COOL = "color_temp_cool";
+ /** @hide */
+ public static final String COLOR_TEMP_STANDARD = "color_temp_standard";
+ /** @hide */
+ public static final String COLOR_TEMP_WARM = "color_temp_warm";
+ /** @hide */
+ public static final String COLOR_TEMP_USER_HDR10PLUS = "color_temp_user_hdr10plus";
+ /** @hide */
+ public static final String COLOR_TEMP_COOL_HDR10PLUS = "color_temp_cool_hdr10plus";
+ /** @hide */
+ public static final String COLOR_TEMP_STANDARD_HDR10PLUS = "color_temp_standard_hdr10plus";
+ /** @hide */
+ public static final String COLOR_TEMP_WARM_HDR10PLUS = "color_temp_warm_hdr10plus";
+ /** @hide */
+ public static final String COLOR_TEMP_FMMSDR = "color_temp_fmmsdr";
+ /** @hide */
+ public static final String COLOR_TEMP_FMMHDR = "color_temp_fmmhdr";
+
/**
* @hide
@@ -82,7 +119,6 @@ public class MediaQualityContract {
String PARAMETER_NAME = "_name";
String PARAMETER_PACKAGE = "_package";
String PARAMETER_INPUT_ID = "_input_id";
- String VENDOR_PARAMETERS = "_vendor_parameters";
}
/**
diff --git a/media/tests/AudioPolicyTest/src/com/android/audiopolicytest/AudioVolumeGroupChangeHandlerTest.java b/media/tests/AudioPolicyTest/src/com/android/audiopolicytest/AudioVolumeGroupChangeHandlerTest.java
deleted file mode 100644
index 82394a2eb420..000000000000
--- a/media/tests/AudioPolicyTest/src/com/android/audiopolicytest/AudioVolumeGroupChangeHandlerTest.java
+++ /dev/null
@@ -1,211 +0,0 @@
-/*
- * Copyright (C) 2020 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.audiopolicytest;
-
-import static androidx.test.core.app.ApplicationProvider.getApplicationContext;
-
-import static com.android.audiopolicytest.AudioVolumeTestUtil.DEFAULT_ATTRIBUTES;
-import static com.android.audiopolicytest.AudioVolumeTestUtil.incrementVolumeIndex;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertThrows;
-import static org.junit.Assert.assertTrue;
-
-import android.media.AudioAttributes;
-import android.media.AudioManager;
-import android.media.audiopolicy.AudioVolumeGroup;
-import android.media.audiopolicy.AudioVolumeGroupChangeHandler;
-import android.platform.test.annotations.Presubmit;
-
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.util.ArrayList;
-import java.util.List;
-
-@Presubmit
-@RunWith(AndroidJUnit4.class)
-public class AudioVolumeGroupChangeHandlerTest {
- private static final String TAG = "AudioVolumeGroupChangeHandlerTest";
-
- @Rule
- public final AudioVolumesTestRule rule = new AudioVolumesTestRule();
-
- private AudioManager mAudioManager;
-
- @Before
- public void setUp() {
- mAudioManager = getApplicationContext().getSystemService(AudioManager.class);
- }
-
- @Test
- public void testRegisterInvalidCallback() {
- final AudioVolumeGroupChangeHandler audioAudioVolumeGroupChangedHandler =
- new AudioVolumeGroupChangeHandler();
-
- audioAudioVolumeGroupChangedHandler.init();
-
- assertThrows(NullPointerException.class, () -> {
- AudioManager.VolumeGroupCallback nullCb = null;
- audioAudioVolumeGroupChangedHandler.registerListener(nullCb);
- });
- }
-
- @Test
- public void testUnregisterInvalidCallback() {
- final AudioVolumeGroupChangeHandler audioAudioVolumeGroupChangedHandler =
- new AudioVolumeGroupChangeHandler();
-
- audioAudioVolumeGroupChangedHandler.init();
-
- final AudioVolumeGroupCallbackHelper cb = new AudioVolumeGroupCallbackHelper();
- audioAudioVolumeGroupChangedHandler.registerListener(cb);
-
- assertThrows(NullPointerException.class, () -> {
- AudioManager.VolumeGroupCallback nullCb = null;
- audioAudioVolumeGroupChangedHandler.unregisterListener(nullCb);
- });
- audioAudioVolumeGroupChangedHandler.unregisterListener(cb);
- }
-
- @Test
- public void testRegisterUnregisterCallback() {
- final AudioVolumeGroupChangeHandler audioAudioVolumeGroupChangedHandler =
- new AudioVolumeGroupChangeHandler();
-
- audioAudioVolumeGroupChangedHandler.init();
- final AudioVolumeGroupCallbackHelper validCb = new AudioVolumeGroupCallbackHelper();
-
- // Should not assert, otherwise test will fail
- audioAudioVolumeGroupChangedHandler.registerListener(validCb);
-
- // Should not assert, otherwise test will fail
- audioAudioVolumeGroupChangedHandler.unregisterListener(validCb);
- }
-
- @Test
- public void testCallbackReceived() {
- final AudioVolumeGroupChangeHandler audioAudioVolumeGroupChangedHandler =
- new AudioVolumeGroupChangeHandler();
-
- audioAudioVolumeGroupChangedHandler.init();
-
- final AudioVolumeGroupCallbackHelper validCb = new AudioVolumeGroupCallbackHelper();
- audioAudioVolumeGroupChangedHandler.registerListener(validCb);
-
- List<AudioVolumeGroup> audioVolumeGroups = mAudioManager.getAudioVolumeGroups();
- assertTrue(audioVolumeGroups.size() > 0);
-
- try {
- for (final AudioVolumeGroup audioVolumeGroup : audioVolumeGroups) {
- int volumeGroupId = audioVolumeGroup.getId();
-
- List<AudioAttributes> avgAttributes = audioVolumeGroup.getAudioAttributes();
- // Set the volume per attributes (if valid) and wait the callback
- if (avgAttributes.size() == 0 || avgAttributes.get(0).equals(DEFAULT_ATTRIBUTES)) {
- // Some volume groups may not have valid attributes, used for internal
- // volume management like patch/rerouting
- // so bailing out strategy retrieval from attributes
- continue;
- }
- final AudioAttributes aa = avgAttributes.get(0);
-
- int index = mAudioManager.getVolumeIndexForAttributes(aa);
- int indexMax = mAudioManager.getMaxVolumeIndexForAttributes(aa);
- int indexMin = mAudioManager.getMinVolumeIndexForAttributes(aa);
-
- final int indexForAa = incrementVolumeIndex(index, indexMin, indexMax);
-
- // Set the receiver to filter only the current group callback
- validCb.setExpectedVolumeGroup(volumeGroupId);
- mAudioManager.setVolumeIndexForAttributes(aa, indexForAa, 0/*flags*/);
- assertTrue(validCb.waitForExpectedVolumeGroupChanged(
- AudioVolumeGroupCallbackHelper.ASYNC_TIMEOUT_MS));
-
- final int readIndex = mAudioManager.getVolumeIndexForAttributes(aa);
- assertEquals(readIndex, indexForAa);
- }
- } finally {
- audioAudioVolumeGroupChangedHandler.unregisterListener(validCb);
- }
- }
-
- @Test
- public void testMultipleCallbackReceived() {
-
- final AudioVolumeGroupChangeHandler audioAudioVolumeGroupChangedHandler =
- new AudioVolumeGroupChangeHandler();
-
- audioAudioVolumeGroupChangedHandler.init();
-
- final int callbackCount = 10;
- final List<AudioVolumeGroupCallbackHelper> validCbs =
- new ArrayList<AudioVolumeGroupCallbackHelper>();
- for (int i = 0; i < callbackCount; i++) {
- validCbs.add(new AudioVolumeGroupCallbackHelper());
- }
- for (final AudioVolumeGroupCallbackHelper cb : validCbs) {
- audioAudioVolumeGroupChangedHandler.registerListener(cb);
- }
-
- List<AudioVolumeGroup> audioVolumeGroups = mAudioManager.getAudioVolumeGroups();
- assertTrue(audioVolumeGroups.size() > 0);
-
- try {
- for (final AudioVolumeGroup audioVolumeGroup : audioVolumeGroups) {
- int volumeGroupId = audioVolumeGroup.getId();
-
- List<AudioAttributes> avgAttributes = audioVolumeGroup.getAudioAttributes();
- // Set the volume per attributes (if valid) and wait the callback
- if (avgAttributes.size() == 0 || avgAttributes.get(0).equals(DEFAULT_ATTRIBUTES)) {
- // Some volume groups may not have valid attributes, used for internal
- // volume management like patch/rerouting
- // so bailing out strategy retrieval from attributes
- continue;
- }
- AudioAttributes aa = avgAttributes.get(0);
-
- int index = mAudioManager.getVolumeIndexForAttributes(aa);
- int indexMax = mAudioManager.getMaxVolumeIndexForAttributes(aa);
- int indexMin = mAudioManager.getMinVolumeIndexForAttributes(aa);
-
- final int indexForAa = incrementVolumeIndex(index, indexMin, indexMax);
-
- // Set the receiver to filter only the current group callback
- for (final AudioVolumeGroupCallbackHelper cb : validCbs) {
- cb.setExpectedVolumeGroup(volumeGroupId);
- }
- mAudioManager.setVolumeIndexForAttributes(aa, indexForAa, 0/*flags*/);
-
- for (final AudioVolumeGroupCallbackHelper cb : validCbs) {
- assertTrue(cb.waitForExpectedVolumeGroupChanged(
- AudioVolumeGroupCallbackHelper.ASYNC_TIMEOUT_MS));
- }
- int readIndex = mAudioManager.getVolumeIndexForAttributes(aa);
- assertEquals(readIndex, indexForAa);
- }
- } finally {
- for (final AudioVolumeGroupCallbackHelper cb : validCbs) {
- audioAudioVolumeGroupChangedHandler.unregisterListener(cb);
- }
- }
- }
-}
diff --git a/media/tests/projection/Android.bp b/media/tests/projection/Android.bp
index 94db2c02eb28..48621e4e2094 100644
--- a/media/tests/projection/Android.bp
+++ b/media/tests/projection/Android.bp
@@ -16,7 +16,6 @@ android_test {
name: "MediaProjectionTests",
srcs: ["**/*.java"],
-
libs: [
"android.test.base.stubs.system",
"android.test.mock.stubs.system",
@@ -30,6 +29,7 @@ android_test {
"frameworks-base-testutils",
"mockito-target-extended-minus-junit4",
"platform-test-annotations",
+ "cts-mediaprojection-common",
"testng",
"testables",
"truth",
@@ -42,7 +42,11 @@ android_test {
"libstaticjvmtiagent",
],
- test_suites: ["device-tests"],
+ data: [
+ ":CtsMediaProjectionTestCasesHelperApp",
+ ],
+
+ test_suites: ["general-tests"],
platform_apis: true,
diff --git a/media/tests/projection/AndroidManifest.xml b/media/tests/projection/AndroidManifest.xml
index 0c9760400ce0..514fb5f689c9 100644
--- a/media/tests/projection/AndroidManifest.xml
+++ b/media/tests/projection/AndroidManifest.xml
@@ -20,6 +20,8 @@
android:sharedUserId="com.android.uid.test">
<uses-permission android:name="android.permission.READ_COMPAT_CHANGE_CONFIG" />
<uses-permission android:name="android.permission.MANAGE_MEDIA_PROJECTION" />
+ <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
+ <uses-permission android:name="android.permission.READ_PHONE_STATE" />
<application android:debuggable="true"
android:testOnly="true">
diff --git a/media/tests/projection/AndroidTest.xml b/media/tests/projection/AndroidTest.xml
index f64930a0eb3f..99b42d1cd263 100644
--- a/media/tests/projection/AndroidTest.xml
+++ b/media/tests/projection/AndroidTest.xml
@@ -22,6 +22,15 @@
<option name="install-arg" value="-t" />
<option name="test-file-name" value="MediaProjectionTests.apk" />
</target_preparer>
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="true" />
+ <option name="force-install-mode" value="FULL"/>ss
+ <option name="test-file-name" value="CtsMediaProjectionTestCasesHelperApp.apk" />
+ </target_preparer>
+ <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
+ <option name="run-command" value="input keyevent KEYCODE_WAKEUP" />
+ <option name="run-command" value="wm dismiss-keyguard" />
+ </target_preparer>
<option name="test-tag" value="MediaProjectionTests" />
<test class="com.android.tradefed.testtype.AndroidJUnitTest">
diff --git a/media/tests/projection/TEST_MAPPING b/media/tests/projection/TEST_MAPPING
new file mode 100644
index 000000000000..b33097c50002
--- /dev/null
+++ b/media/tests/projection/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "presubmit": [
+ {
+ "name": "MediaProjectionTests"
+ }
+ ]
+}
diff --git a/media/tests/projection/src/android/media/projection/MediaProjectionStoppingTest.java b/media/tests/projection/src/android/media/projection/MediaProjectionStoppingTest.java
new file mode 100644
index 000000000000..0b84e01c4d02
--- /dev/null
+++ b/media/tests/projection/src/android/media/projection/MediaProjectionStoppingTest.java
@@ -0,0 +1,293 @@
+/*
+ * Copyright (C) 2025 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 android.media.projection;
+
+import static com.android.compatibility.common.util.FeatureUtil.isAutomotive;
+import static com.android.compatibility.common.util.FeatureUtil.isTV;
+import static com.android.compatibility.common.util.FeatureUtil.isWatch;
+import static com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import static org.junit.Assert.fail;
+import static org.junit.Assume.assumeFalse;
+import static org.junit.Assume.assumeTrue;
+
+import android.Manifest;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.media.cts.MediaProjectionRule;
+import android.os.UserHandle;
+import android.platform.test.annotations.RequiresFlagsDisabled;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.telecom.TelecomManager;
+import android.telephony.TelephonyCallback;
+import android.telephony.TelephonyManager;
+import android.util.Log;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.uiautomator.By;
+import androidx.test.uiautomator.UiDevice;
+import androidx.test.uiautomator.Until;
+
+import com.android.compatibility.common.util.ApiTest;
+import com.android.compatibility.common.util.FrameworkSpecificTest;
+import com.android.media.projection.flags.Flags;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Test {@link MediaProjection} stopping behavior.
+ *
+ * Run with:
+ * atest MediaProjectionTests:MediaProjectionStoppingTest
+ */
+@FrameworkSpecificTest
+public class MediaProjectionStoppingTest {
+ private static final String TAG = "MediaProjectionStoppingTest";
+ private static final int STOP_DIALOG_WAIT_TIMEOUT_MS = 5000;
+ private static final String CALL_HELPER_START_CALL = "start_call";
+ private static final String CALL_HELPER_STOP_CALL = "stop_call";
+ private static final String STOP_DIALOG_TITLE_RES_ID = "android:id/alertTitle";
+ private static final String STOP_DIALOG_CLOSE_BUTTON_RES_ID = "android:id/button2";
+
+ @Rule public MediaProjectionRule mMediaProjectionRule = new MediaProjectionRule();
+
+ private Context mContext;
+ private int mTimeoutMs;
+ private TelecomManager mTelecomManager;
+ private TelephonyManager mTelephonyManager;
+ private TestCallStateListener mTestCallStateListener;
+
+ @Before
+ public void setUp() throws InterruptedException {
+ mContext = InstrumentationRegistry.getInstrumentation().getContext();
+ runWithShellPermissionIdentity(
+ () -> {
+ mContext.getPackageManager()
+ .revokeRuntimePermission(
+ mContext.getPackageName(),
+ Manifest.permission.SYSTEM_ALERT_WINDOW,
+ new UserHandle(mContext.getUserId()));
+ });
+ mTimeoutMs = 1000;
+
+ mTestCallStateListener = new TestCallStateListener(mContext);
+ }
+
+ @After
+ public void cleanup() {
+ mTestCallStateListener.release();
+ }
+
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_STOP_MEDIA_PROJECTION_ON_CALL_END)
+ @ApiTest(apis = "android.media.projection.MediaProjection.Callback#onStop")
+ public void testMediaProjectionStop_callStartedAfterMediaProjection_doesNotStop()
+ throws Exception {
+ assumeTrue(mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_TELECOM));
+
+ mMediaProjectionRule.startMediaProjection();
+
+ CountDownLatch latch = new CountDownLatch(1);
+ mMediaProjectionRule.registerCallback(
+ new MediaProjection.Callback() {
+ @Override
+ public void onStop() {
+ latch.countDown();
+ }
+ });
+ mMediaProjectionRule.createVirtualDisplay();
+
+ try {
+ startPhoneCall();
+ } finally {
+ endPhoneCall();
+ }
+
+ assertWithMessage("MediaProjection should not be stopped on call end")
+ .that(latch.await(mTimeoutMs, TimeUnit.MILLISECONDS)).isFalse();
+ }
+
+ @Test
+ @RequiresFlagsEnabled(Flags.FLAG_STOP_MEDIA_PROJECTION_ON_CALL_END)
+ @RequiresFlagsDisabled(Flags.FLAG_SHOW_STOP_DIALOG_POST_CALL_END)
+ @ApiTest(apis = "android.media.projection.MediaProjection.Callback#onStop")
+ public void
+ testMediaProjectionStop_callStartedBeforeMediaProjection_stopDialogFlagDisabled__shouldStop()
+ throws Exception {
+ assumeTrue(mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_TELECOM));
+ CountDownLatch latch = new CountDownLatch(1);
+ try {
+ startPhoneCall();
+
+ mMediaProjectionRule.startMediaProjection();
+
+ mMediaProjectionRule.registerCallback(
+ new MediaProjection.Callback() {
+ @Override
+ public void onStop() {
+ latch.countDown();
+ }
+ });
+ mMediaProjectionRule.createVirtualDisplay();
+
+ } finally {
+ endPhoneCall();
+ }
+
+ assertWithMessage("MediaProjection was not stopped after call end")
+ .that(latch.await(mTimeoutMs, TimeUnit.MILLISECONDS)).isTrue();
+ }
+
+ @Test
+ @RequiresFlagsEnabled({
+ Flags.FLAG_STOP_MEDIA_PROJECTION_ON_CALL_END,
+ Flags.FLAG_SHOW_STOP_DIALOG_POST_CALL_END
+ })
+ public void
+ callEnds_mediaProjectionStartedDuringCallAndIsActive_stopDialogFlagEnabled_showsStopDialog()
+ throws Exception {
+ // MediaProjection stop Dialog is only available on phones.
+ assumeFalse(isWatch());
+ assumeFalse(isAutomotive());
+ assumeFalse(isTV());
+
+ assumeTrue(mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_TELECOM));
+
+ try {
+ startPhoneCall();
+ mMediaProjectionRule.startMediaProjection();
+
+ mMediaProjectionRule.registerCallback(
+ new MediaProjection.Callback() {
+ @Override
+ public void onStop() {
+ fail(
+ "MediaProjection should not be stopped when"
+ + " FLAG_SHOW_STOP_DIALOG_POST_CALL_END is enabled");
+ }
+ });
+ mMediaProjectionRule.createVirtualDisplay();
+
+ } finally {
+ endPhoneCall();
+ }
+
+ UiDevice device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
+ boolean isDialogShown =
+ device.wait(
+ Until.hasObject(By.res(STOP_DIALOG_TITLE_RES_ID)),
+ STOP_DIALOG_WAIT_TIMEOUT_MS);
+ assertWithMessage("Stop dialog should be visible").that(isDialogShown).isTrue();
+
+ // Find and click the "Close" button
+ boolean hasCloseButton =
+ device.wait(
+ Until.hasObject(By.res(STOP_DIALOG_CLOSE_BUTTON_RES_ID)),
+ STOP_DIALOG_WAIT_TIMEOUT_MS);
+ if (hasCloseButton) {
+ device.findObject(By.res(STOP_DIALOG_CLOSE_BUTTON_RES_ID)).click();
+ Log.d(TAG, "Clicked on 'Close' button to dismiss the stop dialog.");
+ } else {
+ fail("Close button not found, unable to dismiss stop dialog.");
+ }
+ }
+
+ private void startPhoneCall() throws InterruptedException {
+ mTestCallStateListener.assertCallState(false);
+ mContext.startActivity(getCallHelperIntent(CALL_HELPER_START_CALL));
+ mTestCallStateListener.waitForNextCallState(true, mTimeoutMs, TimeUnit.MILLISECONDS);
+ }
+
+ private void endPhoneCall() throws InterruptedException {
+ mTestCallStateListener.assertCallState(true);
+ mContext.startActivity(getCallHelperIntent(CALL_HELPER_STOP_CALL));
+ mTestCallStateListener.waitForNextCallState(false, mTimeoutMs, TimeUnit.MILLISECONDS);
+ }
+
+ private Intent getCallHelperIntent(String action) {
+ return new Intent(action)
+ .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK)
+ .setComponent(
+ new ComponentName(
+ "android.media.projection.cts.helper",
+ "android.media.projection.cts.helper.CallHelperActivity"));
+ }
+
+ private static final class TestCallStateListener extends TelephonyCallback
+ implements TelephonyCallback.CallStateListener {
+ private final BlockingQueue<Boolean> mCallStates = new LinkedBlockingQueue<>();
+ private final TelecomManager mTelecomManager;
+ private final TelephonyManager mTelephonyManager;
+
+ private TestCallStateListener(Context context) throws InterruptedException {
+ mTelecomManager = context.getSystemService(TelecomManager.class);
+ mTelephonyManager = context.getSystemService(TelephonyManager.class);
+ mCallStates.offer(isInCall());
+
+ assertThat(mCallStates.take()).isFalse();
+
+ runWithShellPermissionIdentity(
+ () ->
+ mTelephonyManager.registerTelephonyCallback(
+ context.getMainExecutor(), this));
+ }
+
+ public void release() {
+ runWithShellPermissionIdentity(
+ () -> mTelephonyManager.unregisterTelephonyCallback(this));
+ }
+
+ @Override
+ public void onCallStateChanged(int state) {
+ mCallStates.offer(isInCall());
+ }
+
+ public void waitForNextCallState(boolean expectedCallState, long timeout, TimeUnit unit)
+ throws InterruptedException {
+ String message =
+ String.format(
+ "Call was not %s after timeout",
+ expectedCallState ? "started" : "ended");
+
+ boolean value;
+ do {
+ value = mCallStates.poll(timeout, unit);
+ } while (value != expectedCallState);
+ assertWithMessage(message).that(value).isEqualTo(expectedCallState);
+ }
+
+ private boolean isInCall() {
+ return runWithShellPermissionIdentity(mTelecomManager::isInCall);
+ }
+
+ public void assertCallState(boolean expected) {
+ assertWithMessage("Unexpected call state").that(isInCall()).isEqualTo(expected);
+ }
+ }
+}
diff --git a/packages/CompanionDeviceManager/res/values-gl/strings.xml b/packages/CompanionDeviceManager/res/values-gl/strings.xml
index ce23be8e8fbb..bc9ad8dea004 100644
--- a/packages/CompanionDeviceManager/res/values-gl/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-gl/strings.xml
@@ -25,7 +25,7 @@
<string name="discovery_mixed" msgid="7071466134150760127">"Bluetooth e wifi"</string>
<string name="profile_name_watch" msgid="576290739483672360">"reloxo"</string>
<string name="chooser_title_non_profile" msgid="6035023914517087400">"Escolle un dispositivo para que o xestione a aplicación &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;"</string>
- <string name="chooser_title" msgid="2235819929238267637">"Escolle o perfil (<xliff:g id="PROFILE_NAME">%1$s</xliff:g>) que queiras configurar"</string>
+ <string name="chooser_title" msgid="2235819929238267637">"Escolle o <xliff:g id="PROFILE_NAME">%1$s</xliff:g> que queiras configurar"</string>
<string name="single_device_title" msgid="4199861437545438606">"Buscando <xliff:g id="PROFILE_NAME">%1$s</xliff:g>"</string>
<string name="summary_watch" msgid="8134580124808507407">"Esta aplicación poderá sincronizar información (por exemplo, o nome de quen chama) e acceder a estes permisos do dispositivo (<xliff:g id="DEVICE_TYPE">%1$s</xliff:g>)"</string>
<string name="confirmation_title_glasses" msgid="8288346850537727333">"Queres permitir que &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; xestione o dispositivo (&lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;)?"</string>
diff --git a/packages/CredentialManager/wear/AndroidManifest.xml b/packages/CredentialManager/wear/AndroidManifest.xml
index b480ac30d2cb..c91bf13bf98e 100644
--- a/packages/CredentialManager/wear/AndroidManifest.xml
+++ b/packages/CredentialManager/wear/AndroidManifest.xml
@@ -32,7 +32,8 @@
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
android:label="@string/app_name"
- android:supportsRtl="true">
+ android:supportsRtl="true"
+ android:theme="@style/Theme.CredentialSelector">
<!-- Activity called by GMS has to be exactly:
com.android.credentialmanager.CredentialSelectorActivity -->
@@ -42,7 +43,8 @@
android:exported="true"
android:label="@string/app_name"
android:launchMode="singleTop"
- android:permission="android.permission.LAUNCH_CREDENTIAL_SELECTOR" />
+ android:permission="android.permission.LAUNCH_CREDENTIAL_SELECTOR"
+ android:theme="@style/Theme.CredentialSelector"/>
</application>
</manifest>
diff --git a/packages/CredentialManager/wear/res/values/themes.xml b/packages/CredentialManager/wear/res/values/themes.xml
new file mode 100644
index 000000000000..22329e9ff2ce
--- /dev/null
+++ b/packages/CredentialManager/wear/res/values/themes.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2023 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<resources>
+ <style name="Theme.CredentialSelector" parent="@*android:style/ThemeOverlay.DeviceDefault.Accent.DayNight">
+ <item name="android:windowContentOverlay">@null</item>
+ <item name="android:windowNoTitle">true</item>
+ <item name="android:windowBackground">@android:color/transparent</item>
+ <item name="android:windowIsTranslucent">true</item>
+ </style>
+</resources> \ No newline at end of file
diff --git a/packages/InputDevices/res/raw/keyboard_layout_hungarian.kcm b/packages/InputDevices/res/raw/keyboard_layout_hungarian.kcm
index 6c947c77ad3d..455fb83474b6 100644
--- a/packages/InputDevices/res/raw/keyboard_layout_hungarian.kcm
+++ b/packages/InputDevices/res/raw/keyboard_layout_hungarian.kcm
@@ -37,8 +37,8 @@ key 0 {
key 1 {
label: '1'
base: '1'
- shift: '!'
- ralt: '\u0303'
+ shift: '\''
+ ralt: '\u007e'
}
key 2 {
@@ -80,7 +80,7 @@ key 7 {
label: '7'
base: '7'
shift: '='
- ralt: '\u0300'
+ ralt: '`'
}
key 8 {
@@ -374,6 +374,7 @@ key M {
base: 'm'
shift, capslock: 'M'
shift+capslock: 'm'
+ ralt: '<'
}
key COMMA {
@@ -387,6 +388,7 @@ key PERIOD {
label: '.'
base: '.'
shift: ':'
+ ralt: '>'
}
key MINUS {
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/PackageUtil.java b/packages/PackageInstaller/src/com/android/packageinstaller/PackageUtil.java
index bcc737a351a9..d05aaaa6e389 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/PackageUtil.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/PackageUtil.java
@@ -52,8 +52,11 @@ import java.io.ByteArrayOutputStream;
import java.io.Closeable;
import java.io.File;
import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
import java.util.Arrays;
import java.util.Objects;
+import java.util.stream.Stream;
/**
* This is a utility class for defining some utility methods and constants
@@ -69,7 +72,8 @@ public class PackageUtil {
//intent attribute strings related to uninstall
public static final String INTENT_ATTR_PACKAGE_NAME=PREFIX+"PackageName";
private static final String DOWNLOADS_AUTHORITY = "downloads";
- private static final String SPLIT_BASE_APK_END_WITH = "base.apk";
+ private static final String SPLIT_BASE_APK_SUFFIX = "base.apk";
+ private static final String SPLIT_APK_SUFFIX = ".apk";
/**
* Utility method to get package information for a given {@link File}
@@ -77,11 +81,20 @@ public class PackageUtil {
@Nullable
public static PackageInfo getPackageInfo(Context context, File sourceFile, int flags) {
String filePath = sourceFile.getAbsolutePath();
- if (filePath.endsWith(SPLIT_BASE_APK_END_WITH)) {
+ if (filePath.endsWith(SPLIT_BASE_APK_SUFFIX)) {
File dir = sourceFile.getParentFile();
- if (dir.listFiles().length > 1) {
- // split apks, use file directory to get archive info
- filePath = dir.getPath();
+ try (Stream<Path> list = Files.list(dir.toPath())) {
+ long count = list
+ .filter((name) -> name.endsWith(SPLIT_APK_SUFFIX))
+ .limit(2)
+ .count();
+ if (count > 1) {
+ // split apks, use file directory to get archive info
+ filePath = dir.getPath();
+ }
+ } catch (Exception ignored) {
+ // No access to the parent directory, proceed to read app snippet
+ // from the base apk only
}
}
try {
@@ -240,9 +253,10 @@ public class PackageUtil {
appInfo.publicSourceDir = archiveFilePath;
if (appInfo.splitNames != null && appInfo.splitSourceDirs == null) {
- final File[] files = sourceFile.getParentFile().listFiles();
+ final File[] files = sourceFile.getParentFile().listFiles(
+ (dir, name) -> name.endsWith(SPLIT_APK_SUFFIX));
final String[] splits = Arrays.stream(appInfo.splitNames)
- .map(i -> findFilePath(files, i + ".apk"))
+ .map(i -> findFilePath(files, i + SPLIT_APK_SUFFIX))
.filter(Objects::nonNull)
.toArray(String[]::new);
@@ -283,7 +297,9 @@ public class PackageUtil {
}
private static String findFilePath(File[] files, String postfix) {
- for (File file : files) {
+ final int length = files != null ? files.length : 0;
+ for (int i = 0; i < length; i++) {
+ File file = files[i];
final String path = file.getAbsolutePath();
if (path.endsWith(postfix)) {
return path;
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/PackageUtil.kt b/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/PackageUtil.kt
index e8477ef261a8..b84b903ac1cb 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/PackageUtil.kt
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/v2/model/PackageUtil.kt
@@ -41,6 +41,8 @@ import android.util.Log
import com.android.packageinstaller.v2.model.PackageUtil.getAppSnippet
import java.io.ByteArrayOutputStream
import java.io.File
+import java.nio.file.Files
+import java.nio.file.Path
import kotlinx.parcelize.Parceler
import kotlinx.parcelize.Parcelize
@@ -48,6 +50,7 @@ object PackageUtil {
private val LOG_TAG = InstallRepository::class.java.simpleName
private const val DOWNLOADS_AUTHORITY = "downloads"
private const val SPLIT_BASE_APK_SUFFIX = "base.apk"
+ private const val SPLIT_APK_SUFFIX = ".apk"
const val localLogv = false
const val ARGS_ABORT_REASON: String = "abort_reason"
@@ -440,9 +443,20 @@ object PackageUtil {
var filePath = sourceFile.absolutePath
if (filePath.endsWith(SPLIT_BASE_APK_SUFFIX)) {
val dir = sourceFile.parentFile
- if ((dir?.listFiles()?.size ?: 0) > 1) {
- // split apks, use file directory to get archive info
- filePath = dir.path
+ try {
+ Files.list(dir.toPath()).use { list ->
+ val count: Long = list
+ .filter { name: Path -> name.endsWith(SPLIT_APK_SUFFIX) }
+ .limit(2)
+ .count()
+ if (count > 1) {
+ // split apks, use file directory to get archive info
+ filePath = dir.path
+ }
+ }
+ } catch (ignored: Exception) {
+ // No access to the parent directory, proceed to read app snippet
+ // from the base apk only
}
}
return try {
diff --git a/packages/SettingsLib/AdaptiveIcon/Android.bp b/packages/SettingsLib/AdaptiveIcon/Android.bp
index 67b6fb5f2ed9..c9409e44c8ee 100644
--- a/packages/SettingsLib/AdaptiveIcon/Android.bp
+++ b/packages/SettingsLib/AdaptiveIcon/Android.bp
@@ -18,8 +18,9 @@ android_library {
resource_dirs: ["res"],
static_libs: [
- "androidx.annotation_annotation",
"SettingsLibTile",
+ "androidx.annotation_annotation",
+ "androidx.core_core",
],
min_sdk_version: "21",
diff --git a/packages/SettingsLib/AdaptiveIcon/src/com/android/settingslib/widget/AdaptiveIconShapeDrawable.java b/packages/SettingsLib/AdaptiveIcon/src/com/android/settingslib/widget/AdaptiveIconShapeDrawable.java
index 4d7610cf97b6..aa1d35afab4a 100644
--- a/packages/SettingsLib/AdaptiveIcon/src/com/android/settingslib/widget/AdaptiveIconShapeDrawable.java
+++ b/packages/SettingsLib/AdaptiveIcon/src/com/android/settingslib/widget/AdaptiveIconShapeDrawable.java
@@ -23,7 +23,8 @@ import android.graphics.drawable.AdaptiveIconDrawable;
import android.graphics.drawable.ShapeDrawable;
import android.graphics.drawable.shapes.PathShape;
import android.util.AttributeSet;
-import android.util.PathParser;
+
+import androidx.core.graphics.PathParser;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
diff --git a/packages/SettingsLib/AdaptiveIcon/src/com/android/settingslib/widget/AdaptiveOutlineDrawable.java b/packages/SettingsLib/AdaptiveIcon/src/com/android/settingslib/widget/AdaptiveOutlineDrawable.java
index 12c234ecd739..c7ca18a9c339 100644
--- a/packages/SettingsLib/AdaptiveIcon/src/com/android/settingslib/widget/AdaptiveOutlineDrawable.java
+++ b/packages/SettingsLib/AdaptiveIcon/src/com/android/settingslib/widget/AdaptiveOutlineDrawable.java
@@ -27,12 +27,12 @@ import android.graphics.Rect;
import android.graphics.drawable.AdaptiveIconDrawable;
import android.graphics.drawable.DrawableWrapper;
import android.os.RemoteException;
-import android.util.PathParser;
import android.view.IWindowManager;
import android.view.WindowManagerGlobal;
import androidx.annotation.IntDef;
import androidx.annotation.VisibleForTesting;
+import androidx.core.graphics.PathParser;
import com.android.settingslib.widget.adaptiveicon.R;
diff --git a/packages/SettingsLib/ButtonPreference/src/com/android/settingslib/widget/ButtonPreference.java b/packages/SettingsLib/ButtonPreference/src/com/android/settingslib/widget/ButtonPreference.java
index be711accd542..89e5372b530e 100644
--- a/packages/SettingsLib/ButtonPreference/src/com/android/settingslib/widget/ButtonPreference.java
+++ b/packages/SettingsLib/ButtonPreference/src/com/android/settingslib/widget/ButtonPreference.java
@@ -27,6 +27,7 @@ import android.widget.Button;
import android.widget.LinearLayout;
import androidx.annotation.GravityInt;
+import androidx.annotation.IntDef;
import androidx.preference.Preference;
import androidx.preference.PreferenceViewHolder;
@@ -34,21 +35,46 @@ import com.android.settingslib.widget.preference.button.R;
import com.google.android.material.button.MaterialButton;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
/**
* A preference handled a button
*/
public class ButtonPreference extends Preference implements GroupSectionDividerMixin {
+ public static final int TYPE_FILLED = 0;
+ public static final int TYPE_TONAL = 1;
+ public static final int TYPE_OUTLINE = 2;
+
+ @IntDef({TYPE_FILLED, TYPE_TONAL, TYPE_OUTLINE})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Type {
+ }
+
+ public static final int SIZE_NORMAL = 0;
+ public static final int SIZE_LARGE = 1;
+ public static final int SIZE_EXTRA_LARGE = 2;
+
+ @IntDef({SIZE_NORMAL, SIZE_LARGE, SIZE_EXTRA_LARGE})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Size {
+ }
+
enum ButtonStyle {
- FILLED_NORMAL(0, 0, R.layout.settingslib_expressive_button_filled),
- FILLED_LARGE(0, 1, R.layout.settingslib_expressive_button_filled_large),
- FILLED_EXTRA(0, 2, R.layout.settingslib_expressive_button_filled_extra),
- TONAL_NORMAL(1, 0, R.layout.settingslib_expressive_button_tonal),
- TONAL_LARGE(1, 1, R.layout.settingslib_expressive_button_tonal_large),
- TONAL_EXTRA(1, 2, R.layout.settingslib_expressive_button_tonal_extra),
- OUTLINE_NORMAL(2, 0, R.layout.settingslib_expressive_button_outline),
- OUTLINE_LARGE(2, 1, R.layout.settingslib_expressive_button_outline_large),
- OUTLINE_EXTRA(2, 2, R.layout.settingslib_expressive_button_outline_extra);
+ FILLED_NORMAL(TYPE_FILLED, SIZE_NORMAL, R.layout.settingslib_expressive_button_filled),
+ FILLED_LARGE(TYPE_FILLED, SIZE_LARGE, R.layout.settingslib_expressive_button_filled_large),
+ FILLED_EXTRA(TYPE_FILLED, SIZE_EXTRA_LARGE,
+ R.layout.settingslib_expressive_button_filled_extra),
+ TONAL_NORMAL(TYPE_TONAL, SIZE_NORMAL, R.layout.settingslib_expressive_button_tonal),
+ TONAL_LARGE(TYPE_TONAL, SIZE_LARGE, R.layout.settingslib_expressive_button_tonal_large),
+ TONAL_EXTRA(TYPE_TONAL, SIZE_EXTRA_LARGE,
+ R.layout.settingslib_expressive_button_tonal_extra),
+ OUTLINE_NORMAL(TYPE_OUTLINE, SIZE_NORMAL, R.layout.settingslib_expressive_button_outline),
+ OUTLINE_LARGE(TYPE_OUTLINE, SIZE_LARGE,
+ R.layout.settingslib_expressive_button_outline_large),
+ OUTLINE_EXTRA(TYPE_OUTLINE, SIZE_EXTRA_LARGE,
+ R.layout.settingslib_expressive_button_outline_extra);
private final int mType;
private final int mSize;
@@ -60,7 +86,7 @@ public class ButtonPreference extends Preference implements GroupSectionDividerM
this.mLayoutId = layoutId;
}
- static int getLayoutId(int type, int size) {
+ static int getLayoutId(@Type int type, @Size int size) {
for (ButtonStyle style : values()) {
if (style.mType == type && style.mSize == size) {
return style.mLayoutId;
@@ -266,7 +292,7 @@ public class ButtonPreference extends Preference implements GroupSectionDividerM
* <li>2: extra large</li>
* </ul>
*/
- public void setButtonStyle(int type, int size) {
+ public void setButtonStyle(@Type int type, @Size int size) {
int layoutId = ButtonStyle.getLayoutId(type, size);
setLayoutResource(layoutId);
notifyChanged();
diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/res/layout-v36/settingslib_expressive_collapsing_toolbar_content_layout.xml b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/layout-v36/settingslib_expressive_collapsing_toolbar_content_layout.xml
index 3db0ac653848..b1e42c0aebff 100644
--- a/packages/SettingsLib/CollapsingToolbarBaseActivity/res/layout-v36/settingslib_expressive_collapsing_toolbar_content_layout.xml
+++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/layout-v36/settingslib_expressive_collapsing_toolbar_content_layout.xml
@@ -35,6 +35,7 @@
android:layout_height="@dimen/settingslib_toolbar_layout_height"
app:layout_scrollFlags="scroll|exitUntilCollapsed|snap"
app:toolbarId="@id/action_bar"
+ app:maxLines="2"
style="@style/SettingsLibCollapsingToolbarLayoutStyle.Expressive">
<Toolbar
@@ -44,7 +45,24 @@
android:layout_marginStart="@dimen/settingslib_expressive_space_extrasmall4"
android:theme="?android:attr/actionBarTheme"
android:transitionName="shared_element_view"
- app:layout_collapseMode="pin"/>
+ app:layout_collapseMode="pin">
+
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:paddingStart="@dimen/settingslib_expressive_space_extrasmall6"
+ android:layout_marginEnd="@dimen/settingslib_expressive_space_small4"
+ android:layout_gravity="end">
+
+ <com.google.android.material.button.MaterialButton
+ android:id="@+id/action_button"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ style="@style/SettingsLibButtonStyle.Expressive.Filled"
+ android:visibility="gone"/>
+ </LinearLayout>
+
+ </Toolbar>
</com.google.android.material.appbar.CollapsingToolbarLayout>
</com.google.android.material.appbar.AppBarLayout>
diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarAppCompatActivity.java b/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarAppCompatActivity.java
index feacecbd5d0c..ec1df447995c 100644
--- a/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarAppCompatActivity.java
+++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarAppCompatActivity.java
@@ -24,6 +24,7 @@ import android.view.View;
import android.view.ViewGroup;
import android.widget.Toolbar;
+import androidx.annotation.DrawableRes;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
@@ -132,6 +133,29 @@ public class CollapsingToolbarAppCompatActivity extends AppCompatActivity {
setTitle(getText(titleId));
}
+ /**
+ * Show/Hide the action button on the Toolbar.
+ * @param enabled true to show the button, otherwise it's hidden.
+ */
+ public void setActionButtonEnabled(boolean enabled) {
+ getToolbarDelegate().setActionButtonEnabled(enabled);
+ }
+
+ /** Set the icon to the action button */
+ public void setActionButtonIcon(@DrawableRes int drawableRes) {
+ getToolbarDelegate().setActionButtonIcon(this, drawableRes);
+ }
+
+ /** Set the text to the action button */
+ public void setActionButtonText(@Nullable CharSequence text) {
+ getToolbarDelegate().setActionButtonText(text);
+ }
+
+ /** Set the OnClick listener to the action button */
+ public void setActionButtonListener(@Nullable View.OnClickListener listener) {
+ getToolbarDelegate().setActionButtonOnClickListener(listener);
+ }
+
@Override
public boolean onSupportNavigateUp() {
if (getSupportFragmentManager().getBackStackEntryCount() > 0) {
diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarBaseActivity.java b/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarBaseActivity.java
index 0f9d94e9cc3d..b1b8c983b033 100644
--- a/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarBaseActivity.java
+++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarBaseActivity.java
@@ -25,6 +25,7 @@ import android.view.View;
import android.view.ViewGroup;
import android.widget.Toolbar;
+import androidx.annotation.DrawableRes;
import androidx.annotation.Nullable;
import androidx.fragment.app.FragmentActivity;
@@ -124,6 +125,29 @@ public class CollapsingToolbarBaseActivity extends FragmentActivity {
setTitle(getText(titleId));
}
+ /**
+ * Show/Hide the action button on the Toolbar.
+ * @param enabled true to show the button, otherwise it's hidden.
+ */
+ public void setActionButtonEnabled(boolean enabled) {
+ getToolbarDelegate().setActionButtonEnabled(enabled);
+ }
+
+ /** Set the icon to the action button */
+ public void setActionButtonIcon(@DrawableRes int drawableRes) {
+ getToolbarDelegate().setActionButtonIcon(this, drawableRes);
+ }
+
+ /** Set the text to the action button */
+ public void setActionButtonText(@Nullable CharSequence text) {
+ getToolbarDelegate().setActionButtonText(text);
+ }
+
+ /** Set the OnClick listener to the action button */
+ public void setActionButtonListener(@Nullable View.OnClickListener listener) {
+ getToolbarDelegate().setActionButtonOnClickListener(listener);
+ }
+
@Override
public boolean onNavigateUp() {
if (getSupportFragmentManager().getBackStackEntryCount() > 0) {
diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarDelegate.java b/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarDelegate.java
index de0d60916b3d..072b36549093 100644
--- a/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarDelegate.java
+++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarDelegate.java
@@ -33,6 +33,7 @@ import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.Toolbar;
+import androidx.annotation.DrawableRes;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
@@ -42,6 +43,7 @@ import com.android.settingslib.widget.SettingsThemeHelper;
import com.google.android.material.appbar.AppBarLayout;
import com.google.android.material.appbar.CollapsingToolbarLayout;
+import com.google.android.material.button.MaterialButton;
/**
* A delegate that allows to use the collapsing toolbar layout in hosts that doesn't want/need to
@@ -80,6 +82,8 @@ public class CollapsingToolbarDelegate {
private AppBarLayout mAppBarLayout;
@NonNull
private Toolbar mToolbar;
+ @Nullable
+ private MaterialButton mActionButton;
@NonNull
private FrameLayout mContentFrameLayout;
@NonNull
@@ -154,6 +158,7 @@ public class CollapsingToolbarDelegate {
}
autoSetCollapsingToolbarLayoutScrolling();
mContentFrameLayout = view.findViewById(R.id.content_frame);
+ mActionButton = view.findViewById(R.id.action_button);
if (activity instanceof AppCompatActivity) {
Log.d(TAG, "onCreateView: from AppCompatActivity and sub-class.");
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
@@ -215,6 +220,42 @@ public class CollapsingToolbarDelegate {
}
}
+ /**
+ * Show/Hide the action button on the Toolbar.
+ * @param enabled true to show the button, otherwise it's hidden.
+ */
+ public void setActionButtonEnabled(boolean enabled) {
+ if (mActionButton == null) {
+ return;
+ }
+ int visibility = enabled ? View.VISIBLE : View.GONE;
+ mActionButton.setVisibility(visibility);
+ }
+
+ /** Set the icon to the action button */
+ public void setActionButtonIcon(@NonNull Context context, @DrawableRes int drawableRes) {
+ if (mActionButton == null) {
+ return;
+ }
+ mActionButton.setIcon(context.getResources().getDrawable(drawableRes, context.getTheme()));
+ }
+
+ /** Set the text to the action button */
+ public void setActionButtonText(@Nullable CharSequence text) {
+ if (mActionButton == null) {
+ return;
+ }
+ mActionButton.setText(text);
+ }
+
+ /** Set the OnClick listener to the action button */
+ public void setActionButtonOnClickListener(@Nullable View.OnClickListener listener) {
+ if (mActionButton == null) {
+ return;
+ }
+ mActionButton.setOnClickListener(listener);
+ }
+
/** Return an instance of CoordinatorLayout. */
@Nullable
public CoordinatorLayout getCoordinatorLayout() {
diff --git a/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/AndroidSecureSettings.java b/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/AndroidSecureSettings.java
index 8aee576c3d04..a06d8829e331 100644
--- a/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/AndroidSecureSettings.java
+++ b/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/AndroidSecureSettings.java
@@ -24,11 +24,11 @@ import android.provider.Settings;
* Implementation of {@link SecureSettings} that uses Android's {@link Settings.Secure}
* implementation.
*/
-class AndroidSecureSettings implements SecureSettings {
+public class AndroidSecureSettings implements SecureSettings {
private final ContentResolver mContentResolver;
- AndroidSecureSettings(ContentResolver contentResolver) {
+ public AndroidSecureSettings(ContentResolver contentResolver) {
mContentResolver = contentResolver;
}
diff --git a/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/DeviceStateAutoRotateSettingManager.kt b/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/DeviceStateAutoRotateSettingManager.kt
index fdde3d3f5669..1da17756fae6 100644
--- a/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/DeviceStateAutoRotateSettingManager.kt
+++ b/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/DeviceStateAutoRotateSettingManager.kt
@@ -17,6 +17,7 @@
package com.android.settingslib.devicestate
import android.provider.Settings.Secure.DEVICE_STATE_ROTATION_LOCK
+import android.util.Dumpable
/**
* Interface for managing [DEVICE_STATE_ROTATION_LOCK] setting.
@@ -25,7 +26,7 @@ import android.provider.Settings.Secure.DEVICE_STATE_ROTATION_LOCK
* specific device states, retrieve the setting value, and check if rotation is locked for specific
* or all device states.
*/
-interface DeviceStateAutoRotateSettingManager {
+interface DeviceStateAutoRotateSettingManager : Dumpable {
// TODO: b/397928958 - Rename all terms from rotationLock to autoRotate in all apis.
/** Listener for changes in device-state based auto rotate setting. */
@@ -65,5 +66,3 @@ data class SettableDeviceState(
/** Returns whether there is an auto-rotation setting for this device state. */
val isSettable: Boolean
)
-
-
diff --git a/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/DeviceStateAutoRotateSettingManagerImpl.kt b/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/DeviceStateAutoRotateSettingManagerImpl.kt
index 0b6c6e238956..a9f9eda07118 100644
--- a/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/DeviceStateAutoRotateSettingManagerImpl.kt
+++ b/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/DeviceStateAutoRotateSettingManagerImpl.kt
@@ -22,11 +22,13 @@ import android.os.UserHandle
import android.provider.Settings.Secure.DEVICE_STATE_ROTATION_LOCK
import android.provider.Settings.Secure.DEVICE_STATE_ROTATION_LOCK_IGNORED
import android.provider.Settings.Secure.DEVICE_STATE_ROTATION_LOCK_LOCKED
+import android.util.IndentingPrintWriter
import android.util.Log
import android.util.SparseIntArray
import com.android.internal.R
import com.android.settingslib.devicestate.DeviceStateAutoRotateSettingManager.DeviceStateAutoRotateSettingListener
import com.android.window.flags.Flags
+import java.io.PrintWriter
import java.util.concurrent.Executor
/**
@@ -104,6 +106,15 @@ class DeviceStateAutoRotateSettingManagerImpl(
throw UnsupportedOperationException("API updateSetting is not implemented yet")
}
+ override fun dump(writer: PrintWriter, args: Array<out String>?) {
+ val indentingWriter = IndentingPrintWriter(writer)
+ indentingWriter.println("DeviceStateAutoRotateSettingManagerImpl")
+ indentingWriter.increaseIndent()
+ indentingWriter.println("fallbackPostureMap: $fallbackPostureMap")
+ indentingWriter.println("settableDeviceState: $settableDeviceState")
+ indentingWriter.decreaseIndent()
+ }
+
private fun notifyListeners() =
settingListeners.forEach { listener -> listener.onSettingsChanged() }
diff --git a/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/DeviceStateAutoRotateSettingManagerProvider.kt b/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/DeviceStateAutoRotateSettingManagerProvider.kt
new file mode 100644
index 000000000000..2db8e6f97498
--- /dev/null
+++ b/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/DeviceStateAutoRotateSettingManagerProvider.kt
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2025 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.settingslib.devicestate
+
+import android.content.Context
+import android.os.Handler
+import com.android.window.flags.Flags
+import java.util.concurrent.Executor
+
+/**
+ * Provides appropriate instance of [DeviceStateAutoRotateSettingManager], based on the value of
+ * [Flags.FLAG_ENABLE_DEVICE_STATE_AUTO_ROTATE_SETTING_REFACTOR].
+ */
+object DeviceStateAutoRotateSettingManagerProvider {
+ /**
+ * Provides an instance of [DeviceStateAutoRotateSettingManager], based on the value of
+ * [Flags.FLAG_ENABLE_DEVICE_STATE_AUTO_ROTATE_SETTING_REFACTOR]. It is supposed to be used
+ * by apps that supports dagger.
+ */
+ @JvmStatic
+ fun createInstance(
+ context: Context,
+ backgroundExecutor: Executor,
+ secureSettings: SecureSettings,
+ mainHandler: Handler,
+ posturesHelper: PosturesHelper,
+ ): DeviceStateAutoRotateSettingManager =
+ if (Flags.enableDeviceStateAutoRotateSettingRefactor()) {
+ DeviceStateAutoRotateSettingManagerImpl(
+ context,
+ backgroundExecutor,
+ secureSettings,
+ mainHandler,
+ posturesHelper,
+ )
+ } else {
+ DeviceStateRotationLockSettingsManager(context, secureSettings)
+ }
+}
diff --git a/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/DeviceStateRotationLockSettingsManager.java b/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/DeviceStateRotationLockSettingsManager.java
index deeba574f2ad..6d180b63cd08 100644
--- a/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/DeviceStateRotationLockSettingsManager.java
+++ b/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/DeviceStateRotationLockSettingsManager.java
@@ -20,10 +20,8 @@ import static android.provider.Settings.Secure.DEVICE_STATE_ROTATION_LOCK_IGNORE
import static android.provider.Settings.Secure.DEVICE_STATE_ROTATION_LOCK_LOCKED;
import static android.provider.Settings.Secure.DEVICE_STATE_ROTATION_LOCK_UNLOCKED;
-import static com.android.settingslib.devicestate.DeviceStateAutoRotateSettingManager.DeviceStateAutoRotateSettingListener;
-
+import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.content.ContentResolver;
import android.content.Context;
import android.content.res.Resources;
import android.database.ContentObserver;
@@ -41,6 +39,7 @@ import android.util.SparseIntArray;
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
+import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
@@ -51,7 +50,8 @@ import java.util.Set;
* Manages device-state based rotation lock settings. Handles reading, writing, and listening for
* changes.
*/
-public final class DeviceStateRotationLockSettingsManager {
+public final class DeviceStateRotationLockSettingsManager implements
+ DeviceStateAutoRotateSettingManager {
private static final String TAG = "DSRotLockSettingsMngr";
private static final String SEPARATOR_REGEX = ":";
@@ -68,8 +68,7 @@ public final class DeviceStateRotationLockSettingsManager {
private SparseIntArray mPostureRotationLockFallbackSettings;
private List<SettableDeviceState> mSettableDeviceStates;
- @VisibleForTesting
- DeviceStateRotationLockSettingsManager(Context context, SecureSettings secureSettings) {
+ public DeviceStateRotationLockSettingsManager(Context context, SecureSettings secureSettings) {
mSecureSettings = secureSettings;
mPosturesHelper = new PosturesHelper(context, getDeviceStateManager(context));
@@ -89,30 +88,6 @@ public final class DeviceStateRotationLockSettingsManager {
return null;
}
- /** Returns a singleton instance of this class */
- public static synchronized DeviceStateRotationLockSettingsManager getInstance(Context context) {
- if (sSingleton == null) {
- Context applicationContext = context.getApplicationContext();
- ContentResolver contentResolver = applicationContext.getContentResolver();
- SecureSettings secureSettings = new AndroidSecureSettings(contentResolver);
- sSingleton =
- new DeviceStateRotationLockSettingsManager(applicationContext, secureSettings);
- }
- return sSingleton;
- }
-
- /** Resets the singleton instance of this class. Only used for testing. */
- @VisibleForTesting
- public static synchronized void resetInstance() {
- sSingleton = null;
- }
-
- /** Returns true if device-state based rotation lock settings are enabled. */
- public static boolean isDeviceStateRotationLockEnabled(Context context) {
- return context.getResources()
- .getStringArray(R.array.config_perDeviceStateRotationLockDefaults).length > 0;
- }
-
private void listenForSettingsChange() {
mSecureSettings
.registerContentObserver(
@@ -131,7 +106,8 @@ public final class DeviceStateRotationLockSettingsManager {
* Registers a {@link DeviceStateAutoRotateSettingListener} to be notified when the settings
* change. Can be called multiple times with different listeners.
*/
- public void registerListener(DeviceStateAutoRotateSettingListener runnable) {
+ @Override
+ public void registerListener(@NonNull DeviceStateAutoRotateSettingListener runnable) {
mListeners.add(runnable);
}
@@ -139,14 +115,16 @@ public final class DeviceStateRotationLockSettingsManager {
* Unregisters a {@link DeviceStateAutoRotateSettingListener}. No-op if the given instance
* was never registered.
*/
+ @Override
public void unregisterListener(
- DeviceStateAutoRotateSettingListener deviceStateAutoRotateSettingListener) {
+ @NonNull DeviceStateAutoRotateSettingListener deviceStateAutoRotateSettingListener) {
if (!mListeners.remove(deviceStateAutoRotateSettingListener)) {
Log.w(TAG, "Attempting to unregister a listener hadn't been registered");
}
}
/** Updates the rotation lock setting for a specified device state. */
+ @Override
public void updateSetting(int deviceState, boolean rotationLocked) {
int posture = mPosturesHelper.deviceStateToPosture(deviceState);
if (mPostureRotationLockFallbackSettings.indexOfKey(posture) >= 0) {
@@ -173,6 +151,7 @@ public final class DeviceStateRotationLockSettingsManager {
* DEVICE_STATE_ROTATION_LOCK_IGNORED}.
*/
@Settings.Secure.DeviceStateRotationLockSetting
+ @Override
public int getRotationLockSetting(int deviceState) {
int devicePosture = mPosturesHelper.deviceStateToPosture(deviceState);
int rotationLockSetting = mPostureRotationLockSettings.get(
@@ -196,6 +175,7 @@ public final class DeviceStateRotationLockSettingsManager {
/** Returns true if the rotation is locked for the current device state */
+ @Override
public boolean isRotationLocked(int deviceState) {
return getRotationLockSetting(deviceState) == DEVICE_STATE_ROTATION_LOCK_LOCKED;
}
@@ -204,6 +184,7 @@ public final class DeviceStateRotationLockSettingsManager {
* Returns true if there is no device state for which the current setting is {@link
* DEVICE_STATE_ROTATION_LOCK_UNLOCKED}.
*/
+ @Override
public boolean isRotationLockedForAllStates() {
for (int i = 0; i < mPostureRotationLockSettings.size(); i++) {
if (mPostureRotationLockSettings.valueAt(i)
@@ -215,6 +196,8 @@ public final class DeviceStateRotationLockSettingsManager {
}
/** Returns a list of device states and their respective auto-rotation setting availability. */
+ @Override
+ @NonNull
public List<SettableDeviceState> getSettableDeviceStates() {
// Returning a copy to make sure that nothing outside can mutate our internal list.
return new ArrayList<>(mSettableDeviceStates);
@@ -356,17 +339,21 @@ public final class DeviceStateRotationLockSettingsManager {
}
}
- /** Dumps internal state. */
- public void dump(IndentingPrintWriter pw) {
- pw.println("DeviceStateRotationLockSettingsManager");
- pw.increaseIndent();
- pw.println("mPostureRotationLockDefaults: "
+ @Override
+ public void dump(@NonNull PrintWriter writer, String[] args) {
+ IndentingPrintWriter indentingWriter = new IndentingPrintWriter(writer);
+ indentingWriter.println("DeviceStateRotationLockSettingsManager");
+ indentingWriter.increaseIndent();
+ indentingWriter.println("mPostureRotationLockDefaults: "
+ Arrays.toString(mPostureRotationLockDefaults));
- pw.println("mPostureDefaultRotationLockSettings: " + mPostureDefaultRotationLockSettings);
- pw.println("mDeviceStateRotationLockSettings: " + mPostureRotationLockSettings);
- pw.println("mPostureRotationLockFallbackSettings: " + mPostureRotationLockFallbackSettings);
- pw.println("mSettableDeviceStates: " + mSettableDeviceStates);
- pw.decreaseIndent();
+ indentingWriter.println(
+ "mPostureDefaultRotationLockSettings: " + mPostureDefaultRotationLockSettings);
+ indentingWriter.println(
+ "mDeviceStateRotationLockSettings: " + mPostureRotationLockSettings);
+ indentingWriter.println(
+ "mPostureRotationLockFallbackSettings: " + mPostureRotationLockFallbackSettings);
+ indentingWriter.println("mSettableDeviceStates: " + mSettableDeviceStates);
+ indentingWriter.decreaseIndent();
}
/**
diff --git a/packages/SettingsLib/SettingsTheme/res/layout/settingslib_expressive_collapsable_textview.xml b/packages/SettingsLib/SettingsTheme/res/layout/settingslib_expressive_collapsable_textview.xml
index 7d7bec14ed78..cc55cacb9e5c 100644
--- a/packages/SettingsLib/SettingsTheme/res/layout/settingslib_expressive_collapsable_textview.xml
+++ b/packages/SettingsLib/SettingsTheme/res/layout/settingslib_expressive_collapsable_textview.xml
@@ -44,10 +44,11 @@
<com.android.settingslib.widget.LinkableTextView
android:id="@+id/settingslib_expressive_learn_more"
- android:layout_width="wrap_content"
+ android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintTop_toBottomOf="@android:id/title"
app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"
android:paddingTop="@dimen/settingslib_expressive_space_extrasmall6"
android:textAlignment="viewStart"
android:clickable="true"
diff --git a/packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/CollapsableTextView.kt b/packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/CollapsableTextView.kt
index 976711bdc5f3..007dc5143262 100644
--- a/packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/CollapsableTextView.kt
+++ b/packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/CollapsableTextView.kt
@@ -85,6 +85,7 @@ class CollapsableTextView @JvmOverloads constructor(
Gravity.CENTER_VERTICAL, Gravity.CENTER, Gravity.CENTER_HORIZONTAL -> {
centerHorizontally(titleTextView)
centerHorizontally(collapseButton)
+ centerHorizontally(learnMoreTextView)
}
}
isCollapsable = getBoolean(isCollapsableAttr, DEFAULT_COLLAPSABLE)
diff --git a/packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/SettingsPreferenceGroupAdapter.kt b/packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/SettingsPreferenceGroupAdapter.kt
index 2672787a0519..d1c88de3f399 100644
--- a/packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/SettingsPreferenceGroupAdapter.kt
+++ b/packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/SettingsPreferenceGroupAdapter.kt
@@ -71,18 +71,27 @@ open class SettingsPreferenceGroupAdapter(preferenceGroup: PreferenceGroup) :
override fun onPreferenceHierarchyChange(preference: Preference) {
super.onPreferenceHierarchyChange(preference)
- // Post after super class has posted their sync runnable to update preferences.
- mHandler.removeCallbacks(syncRunnable)
- mHandler.post(syncRunnable)
+ if (SettingsThemeHelper.isExpressiveTheme(preference.context)) {
+ // Post after super class has posted their sync runnable to update preferences.
+ mHandler.removeCallbacks(syncRunnable)
+ mHandler.post(syncRunnable)
+ }
}
@SuppressLint("RestrictedApi")
override fun onBindViewHolder(holder: PreferenceViewHolder, position: Int) {
super.onBindViewHolder(holder, position)
- updateBackground(holder, position)
+
+ if (SettingsThemeHelper.isExpressiveTheme(holder.itemView.context)) {
+ updateBackground(holder, position)
+ }
}
private fun updatePreferencesList() {
+ if (!SettingsThemeHelper.isExpressiveTheme(mPreferenceGroup.context)) {
+ return
+ }
+
val oldList = ArrayList(mRoundCornerMappingList)
mRoundCornerMappingList = ArrayList()
mappingPreferenceGroup(mRoundCornerMappingList, mPreferenceGroup)
diff --git a/packages/SettingsLib/Spa/build.gradle.kts b/packages/SettingsLib/Spa/build.gradle.kts
index 50408190b3ef..25406d794af9 100644
--- a/packages/SettingsLib/Spa/build.gradle.kts
+++ b/packages/SettingsLib/Spa/build.gradle.kts
@@ -28,7 +28,7 @@ val androidTop: String = File(rootDir, "../../../../..").canonicalPath
allprojects {
extra["androidTop"] = androidTop
- extra["jetpackComposeVersion"] = "1.8.0-beta02"
+ extra["jetpackComposeVersion"] = "1.8.0-rc01"
}
subprojects {
@@ -36,11 +36,11 @@ subprojects {
plugins.withType<AndroidBasePlugin> {
configure<BaseExtension> {
- compileSdkVersion(35)
+ compileSdkVersion(36)
defaultConfig {
minSdk = 21
- targetSdk = 35
+ targetSdk = 36
}
}
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GallerySpaEnvironment.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GallerySpaEnvironment.kt
index 8636524ed23c..b9a3a7f4b48f 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GallerySpaEnvironment.kt
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GallerySpaEnvironment.kt
@@ -124,4 +124,6 @@ class GallerySpaEnvironment(context: Context) : SpaEnvironment(context) {
// For debugging
override val searchProviderAuthorities = "com.android.spa.gallery.search.provider"
+
+ override val isSpaExpressiveEnabled = true
}
diff --git a/packages/SettingsLib/Spa/gradle/libs.versions.toml b/packages/SettingsLib/Spa/gradle/libs.versions.toml
index b074f4b91ed0..d041eb011986 100644
--- a/packages/SettingsLib/Spa/gradle/libs.versions.toml
+++ b/packages/SettingsLib/Spa/gradle/libs.versions.toml
@@ -15,10 +15,10 @@
#
[versions]
-agp = "8.8.1"
+agp = "8.9.0"
dexmaker-mockito = "2.28.3"
jvm = "21"
-kotlin = "2.0.21"
+kotlin = "2.1.10"
truth = "1.4.4"
[libraries]
diff --git a/packages/SettingsLib/Spa/spa/build.gradle.kts b/packages/SettingsLib/Spa/spa/build.gradle.kts
index 7ce5b71f678e..57f5520fd659 100644
--- a/packages/SettingsLib/Spa/spa/build.gradle.kts
+++ b/packages/SettingsLib/Spa/spa/build.gradle.kts
@@ -52,18 +52,18 @@ android {
dependencies {
api(project(":SettingsLibColor"))
api("androidx.appcompat:appcompat:1.7.0")
- api("androidx.compose.material3:material3:1.4.0-alpha08")
- api("androidx.compose.material:material-icons-extended")
+ api("androidx.compose.material3:material3:1.4.0-alpha10")
+ api("androidx.compose.material:material-icons-extended:1.7.8")
api("androidx.compose.runtime:runtime-livedata:$jetpackComposeVersion")
api("androidx.compose.ui:ui-tooling-preview:$jetpackComposeVersion")
api("androidx.graphics:graphics-shapes-android:1.0.1")
api("androidx.lifecycle:lifecycle-livedata-ktx")
api("androidx.lifecycle:lifecycle-runtime-compose")
- api("androidx.navigation:navigation-compose:2.9.0-alpha06")
+ api("androidx.navigation:navigation-compose:2.9.0-alpha08")
api("com.github.PhilJay:MPAndroidChart:v3.1.0-alpha")
api("com.google.android.material:material:1.13.0-alpha08")
debugApi("androidx.compose.ui:ui-tooling:$jetpackComposeVersion")
- implementation("com.airbnb.android:lottie-compose:6.4.0")
+ implementation("com.airbnb.android:lottie-compose:6.5.2")
androidTestImplementation(project(":testutils"))
androidTestImplementation(libs.dexmaker.mockito)
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Category.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Category.kt
index f10f96afd389..395328f86047 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Category.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Category.kt
@@ -78,6 +78,8 @@ fun CategoryTitle(title: String) {
/**
* A container that is used to group similar items. A [Category] displays a [CategoryTitle] and
* visually separates groups of items.
+ *
+ * @param content The content of the category.
*/
@Composable
fun Category(
@@ -126,7 +128,8 @@ fun Category(
* be decided by the index.
* @param bottomPadding Optional. Bottom outside padding of the category.
* @param state Optional. State of LazyList.
- * @param content Optional. Content to be shown at the top of the category.
+ * @param footer Optional. Content to be shown at the bottom of the category.
+ * @param header Optional. Content to be shown at the top of the category.
*/
@Composable
fun LazyCategory(
@@ -136,7 +139,8 @@ fun LazyCategory(
title: ((Int) -> String?)? = null,
bottomPadding: Dp = SettingsDimension.paddingSmall,
state: LazyListState = rememberLazyListState(),
- content: @Composable () -> Unit,
+ footer: @Composable () -> Unit = {},
+ header: @Composable () -> Unit,
) {
Column(
Modifier.padding(
@@ -154,12 +158,14 @@ fun LazyCategory(
verticalArrangement = Arrangement.spacedBy(SettingsDimension.paddingTiny),
state = state,
) {
- item { CompositionLocalProvider(LocalIsInCategory provides true) { content() } }
+ item { CompositionLocalProvider(LocalIsInCategory provides true) { header() } }
items(count = list.size, key = key) {
title?.invoke(it)?.let { title -> CategoryTitle(title) }
CompositionLocalProvider(LocalIsInCategory provides true) { entry(it)() }
}
+
+ item { CompositionLocalProvider(LocalIsInCategory provides true) { footer() } }
}
}
}
@@ -189,3 +195,28 @@ private fun CategoryPreview() {
}
}
}
+
+@Preview
+@Composable
+private fun LazyCategoryPreview() {
+ SettingsTheme {
+ LazyCategory(
+ list = listOf(1, 2, 3),
+ entry = { key ->
+ @Composable {
+ Preference(
+ object : PreferenceModel {
+ override val title = key.toString()
+ }
+ )
+ }
+ },
+ footer = @Composable {
+ Footer("Footer")
+ },
+ header = @Composable {
+ Text("Header")
+ },
+ )
+ }
+}
diff --git a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/ui/CategoryTest.kt b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/ui/CategoryTest.kt
index 4b4a8c20b39e..7d199511044a 100644
--- a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/ui/CategoryTest.kt
+++ b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/ui/CategoryTest.kt
@@ -71,10 +71,17 @@ class CategoryTest {
}
@Test
- fun lazyCategory_content_displayed() {
+ fun lazyCategory_headerDisplayed() {
composeTestRule.setContent { TestLazyCategory() }
- composeTestRule.onNodeWithText("text").assertExists()
+ composeTestRule.onNodeWithText("Header").assertExists()
+ }
+
+ @Test
+ fun lazyCategory_footerDisplayed() {
+ composeTestRule.setContent { TestLazyCategory() }
+
+ composeTestRule.onNodeWithText("Footer").assertExists()
}
@Test
@@ -102,8 +109,8 @@ private fun TestLazyCategory() {
list = list,
entry = { index: Int -> @Composable { Preference(list[index]) } },
title = { index: Int -> if (index == 0) "LazyCategory $index" else null },
- ) {
- Text("text")
- }
+ footer = @Composable { Footer("Footer") },
+ header = @Composable { Text("Header") },
+ )
}
}
diff --git a/packages/SettingsLib/StatusBannerPreference/res/drawable/settingslib_expressive_icon_status_level_off.xml b/packages/SettingsLib/StatusBannerPreference/res/drawable/settingslib_expressive_icon_status_level_off.xml
new file mode 100644
index 000000000000..6b534aa9647d
--- /dev/null
+++ b/packages/SettingsLib/StatusBannerPreference/res/drawable/settingslib_expressive_icon_status_level_off.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2025 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.
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="34dp"
+ android:height="42dp"
+ android:viewportWidth="34"
+ android:viewportHeight="42">
+ <path
+ android:pathData="M0.856,17.569C0.887,19.083 1.004,20.593 1.206,22.094C2.166,28.584 5.804,35.937 15.774,41.089C16.171,41.293 16.61,41.4 17.056,41.4C17.503,41.4 17.942,41.293 18.339,41.089C28.309,35.936 31.947,28.583 32.907,22.093C33.109,20.593 33.226,19.083 33.256,17.569V8.605C33.257,7.919 33.046,7.25 32.652,6.688C32.259,6.127 31.703,5.7 31.059,5.467L18.191,0.8C17.458,0.534 16.655,0.534 15.922,0.8L3.054,5.467C2.41,5.7 1.854,6.127 1.461,6.688C1.067,7.25 0.856,7.919 0.856,8.605V17.569Z"
+ android:fillColor="#D1C2CB"/>
+ <path
+ android:pathData="M15.067,24.333V10.733H18.933V24.333H15.067ZM15.067,31.267V27.433H18.933V31.267H15.067Z"
+ android:fillColor="@color/settingslib_materialColorSurfaceContainerLowest"/>
+</vector> \ No newline at end of file
diff --git a/packages/SettingsLib/StatusBannerPreference/res/layout/settingslib_expressive_preference_statusbanner.xml b/packages/SettingsLib/StatusBannerPreference/res/layout/settingslib_expressive_preference_statusbanner.xml
index 083b862e8a5c..c778fb03e04f 100644
--- a/packages/SettingsLib/StatusBannerPreference/res/layout/settingslib_expressive_preference_statusbanner.xml
+++ b/packages/SettingsLib/StatusBannerPreference/res/layout/settingslib_expressive_preference_statusbanner.xml
@@ -55,6 +55,27 @@
android:layout_gravity="center"
android:scaleType="centerInside"/>
+ <com.google.android.material.progressindicator.CircularProgressIndicator
+ android:id="@+id/progress_indicator"
+ style="@style/Widget.Material3.CircularProgressIndicator"
+ android:layout_width="@dimen/settingslib_expressive_space_medium4"
+ android:layout_height="@dimen/settingslib_expressive_space_medium4"
+ android:layout_gravity="center"
+ android:scaleType="centerInside"
+ android:indeterminate="false"
+ android:max="100"
+ android:progress="0"
+ android:visibility="gone" />
+
+ <com.google.android.material.loadingindicator.LoadingIndicator
+ android:id="@+id/loading_indicator"
+ style="@style/Widget.Material3.LoadingIndicator"
+ android:layout_width="@dimen/settingslib_expressive_space_medium4"
+ android:layout_height="@dimen/settingslib_expressive_space_medium4"
+ android:layout_gravity="center"
+ android:scaleType="centerInside"
+ android:visibility="gone" />
+
</FrameLayout>
<LinearLayout
diff --git a/packages/SettingsLib/StatusBannerPreference/res/values/attrs.xml b/packages/SettingsLib/StatusBannerPreference/res/values/attrs.xml
index 54860d4af20f..bb9a5ad689cd 100644
--- a/packages/SettingsLib/StatusBannerPreference/res/values/attrs.xml
+++ b/packages/SettingsLib/StatusBannerPreference/res/values/attrs.xml
@@ -21,6 +21,9 @@
<enum name="low" value="1"/>
<enum name="medium" value="2"/>
<enum name="high" value="3"/>
+ <enum name="off" value="4"/>
+ <enum name="loading_determinate" value="5"/>
+ <enum name="loading_indeterminate" value="6"/>
</attr>
<attr name="buttonLevel" format="enum">
<enum name="generic" value="0"/>
diff --git a/packages/SettingsLib/StatusBannerPreference/res/values/colors.xml b/packages/SettingsLib/StatusBannerPreference/res/values/colors.xml
index 19181dd55852..abc458bc1e27 100644
--- a/packages/SettingsLib/StatusBannerPreference/res/values/colors.xml
+++ b/packages/SettingsLib/StatusBannerPreference/res/values/colors.xml
@@ -22,4 +22,5 @@
<color name="settingslib_expressive_color_status_level_medium">#FCBD00</color>
<!-- static palette red50 -->
<color name="settingslib_expressive_color_status_level_high">#DB372D</color>
+ <color name="settingslib_expressive_color_status_level_off">#D1C2CB</color>
</resources> \ No newline at end of file
diff --git a/packages/SettingsLib/StatusBannerPreference/src/com/android/settingslib/widget/StatusBannerPreference.kt b/packages/SettingsLib/StatusBannerPreference/src/com/android/settingslib/widget/StatusBannerPreference.kt
index 1f8cfb5e432e..e6c6638f7de4 100644
--- a/packages/SettingsLib/StatusBannerPreference/src/com/android/settingslib/widget/StatusBannerPreference.kt
+++ b/packages/SettingsLib/StatusBannerPreference/src/com/android/settingslib/widget/StatusBannerPreference.kt
@@ -28,6 +28,7 @@ import androidx.preference.Preference
import androidx.preference.PreferenceViewHolder
import com.android.settingslib.widget.preference.statusbanner.R
import com.google.android.material.button.MaterialButton
+import com.google.android.material.progressindicator.CircularProgressIndicator
class StatusBannerPreference @JvmOverloads constructor(
context: Context,
@@ -40,7 +41,10 @@ class StatusBannerPreference @JvmOverloads constructor(
GENERIC,
LOW,
MEDIUM,
- HIGH
+ HIGH,
+ OFF,
+ LOADING_DETERMINATE, // The loading progress is set by the caller.
+ LOADING_INDETERMINATE // No loading progress. Just loading animation
}
var iconLevel: BannerStatus = BannerStatus.GENERIC
set(value) {
@@ -59,6 +63,8 @@ class StatusBannerPreference @JvmOverloads constructor(
}
private var listener: View.OnClickListener? = null
+ private var circularProgressIndicator: CircularProgressIndicator? = null
+
init {
layoutResource = R.layout.settingslib_expressive_preference_statusbanner
isSelectable = false
@@ -87,6 +93,9 @@ class StatusBannerPreference @JvmOverloads constructor(
1 -> BannerStatus.LOW
2 -> BannerStatus.MEDIUM
3 -> BannerStatus.HIGH
+ 4 -> BannerStatus.OFF
+ 5 -> BannerStatus.LOADING_DETERMINATE
+ 6 -> BannerStatus.LOADING_INDETERMINATE
else -> BannerStatus.GENERIC
}
@@ -100,17 +109,55 @@ class StatusBannerPreference @JvmOverloads constructor(
}
holder.findViewById(android.R.id.icon_frame)?.apply {
- visibility = if (icon != null) View.VISIBLE else View.GONE
+ visibility =
+ if (
+ icon != null || iconLevel == BannerStatus.LOADING_DETERMINATE ||
+ iconLevel == BannerStatus.LOADING_INDETERMINATE
+ )
+ View.VISIBLE
+ else View.GONE
+ }
+
+ holder.findViewById(android.R.id.icon)?.apply {
+ visibility =
+ if (iconLevel == BannerStatus.LOADING_DETERMINATE ||
+ iconLevel == BannerStatus.LOADING_INDETERMINATE)
+ View.GONE
+ else View.VISIBLE
+ }
+
+ circularProgressIndicator = holder.findViewById(R.id.progress_indicator)
+ as? CircularProgressIndicator
+
+ (circularProgressIndicator)?.apply {
+ visibility =
+ if (iconLevel == BannerStatus.LOADING_DETERMINATE)
+ View.VISIBLE
+ else View.GONE
+ }
+
+ holder.findViewById(R.id.loading_indicator)?.apply {
+ visibility =
+ if (iconLevel == BannerStatus.LOADING_INDETERMINATE)
+ View.VISIBLE
+ else View.GONE
}
(holder.findViewById(R.id.status_banner_button) as? MaterialButton)?.apply {
- setBackgroundColor(getBackgroundColor(buttonLevel))
+ setBackgroundColor(
+ if (buttonLevel == BannerStatus.OFF) getBackgroundColor(BannerStatus.GENERIC)
+ else getBackgroundColor(buttonLevel)
+ )
text = buttonText
setOnClickListener(listener)
visibility = if (listener != null) View.VISIBLE else View.GONE
}
}
+ fun getProgressIndicator(): CircularProgressIndicator? {
+ return circularProgressIndicator
+ }
+
/**
* Sets the text to be displayed in button.
*/
@@ -143,6 +190,11 @@ class StatusBannerPreference @JvmOverloads constructor(
R.color.settingslib_expressive_color_status_level_high
)
+ BannerStatus.OFF -> ContextCompat.getColor(
+ context,
+ R.color.settingslib_expressive_color_status_level_off
+ )
+
else -> ContextCompat.getColor(
context,
com.android.settingslib.widget.theme.R.color.settingslib_materialColorPrimary
@@ -167,6 +219,11 @@ class StatusBannerPreference @JvmOverloads constructor(
R.drawable.settingslib_expressive_icon_status_level_high
)
+ BannerStatus.OFF -> ContextCompat.getDrawable(
+ context,
+ R.drawable.settingslib_expressive_icon_status_level_off
+ )
+
else -> null
}
}
@@ -188,6 +245,7 @@ class StatusBannerPreference @JvmOverloads constructor(
R.drawable.settingslib_expressive_background_level_high
)
+ // Using the same background drawable for other levels.
else -> ContextCompat.getDrawable(
context,
R.drawable.settingslib_expressive_background_generic
diff --git a/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/Tile.java b/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/Tile.java
index 7f4bebcf4a62..335526632383 100644
--- a/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/Tile.java
+++ b/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/Tile.java
@@ -46,6 +46,8 @@ import android.os.UserHandle;
import android.text.TextUtils;
import android.util.Log;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import java.util.ArrayList;
@@ -326,6 +328,15 @@ public abstract class Tile implements Parcelable {
return false;
}
+ /** Returns the icon color scheme. */
+ @Nullable
+ public String getIconColorScheme(@NonNull Context context) {
+ ensureMetadataNotStale(context);
+ return mMetaData != null
+ ? mMetaData.getString(TileUtils.META_DATA_PREFERENCE_ICON_COLOR_SCHEME, null)
+ : null;
+ }
+
/** Whether the {@link Activity} should be launched in a separate task. */
public boolean isNewTask() {
if (mMetaData != null && mMetaData.containsKey(META_DATA_NEW_TASK)) {
diff --git a/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/TileUtils.java b/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/TileUtils.java
index ac0b9b45aba6..d62ed2f60ed0 100644
--- a/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/TileUtils.java
+++ b/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/TileUtils.java
@@ -135,6 +135,13 @@ public class TileUtils {
public static final String META_DATA_PREFERENCE_ICON = "com.android.settings.icon";
/**
+ * Name of the meta-data item that should be set in the AndroidManifest.xml to specify the icon
+ * color scheme. Only available for preferences on the homepage.
+ */
+ public static final String META_DATA_PREFERENCE_ICON_COLOR_SCHEME =
+ "com.android.settings.icon_color_scheme";
+
+ /**
* Name of the meta-data item that should be set in the AndroidManifest.xml
* to specify the icon background color. The value may or may not be used by Settings app.
*/
diff --git a/packages/SettingsLib/ValuePreference/Android.bp b/packages/SettingsLib/ValuePreference/Android.bp
new file mode 100644
index 000000000000..1cdcd2247eaf
--- /dev/null
+++ b/packages/SettingsLib/ValuePreference/Android.bp
@@ -0,0 +1,31 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+android_library {
+ name: "SettingsLibValuePreference",
+ use_resource_processor: true,
+ defaults: [
+ "SettingsLintDefaults",
+ ],
+
+ srcs: ["src/**/*.java"],
+ resource_dirs: ["res"],
+
+ static_libs: [
+ "androidx.annotation_annotation",
+ "androidx.preference_preference",
+ "SettingsLibSettingsTheme",
+ ],
+
+ sdk_version: "system_current",
+ min_sdk_version: "23",
+ apex_available: [
+ "//apex_available:platform",
+ ],
+}
diff --git a/packages/SettingsLib/ValuePreference/AndroidManifest.xml b/packages/SettingsLib/ValuePreference/AndroidManifest.xml
new file mode 100644
index 000000000000..217282be9c80
--- /dev/null
+++ b/packages/SettingsLib/ValuePreference/AndroidManifest.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2025 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.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.settingslib.widget.preference.value">
+
+ <uses-sdk android:minSdkVersion="21"/>
+
+</manifest>
diff --git a/packages/SettingsLib/ValuePreference/res/layout/settingslib_expressive_value_preference.xml b/packages/SettingsLib/ValuePreference/res/layout/settingslib_expressive_value_preference.xml
new file mode 100644
index 000000000000..4fd3ab10fb16
--- /dev/null
+++ b/packages/SettingsLib/ValuePreference/res/layout/settingslib_expressive_value_preference.xml
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2025 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.
+ -->
+
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:minHeight="72dp"
+ android:gravity="center_vertical"
+ android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+ android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+ android:background="?android:attr/selectableItemBackground"
+ android:clipToPadding="false"
+ android:baselineAligned="false"
+ android:filterTouchesWhenObscured="false">
+
+ <include layout="@layout/settingslib_expressive_preference_icon_frame"/>
+
+ <include layout="@layout/settingslib_expressive_value_preference_text_frame"/>
+
+ <!-- Preference should place its actual preference widget here. -->
+ <LinearLayout
+ android:id="@android:id/widget_frame"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:gravity="end|center_vertical"
+ android:paddingStart="@dimen/settingslib_expressive_space_small1"
+ android:paddingEnd="0dp"
+ android:orientation="vertical"/>
+
+</LinearLayout> \ No newline at end of file
diff --git a/packages/SettingsLib/ValuePreference/res/layout/settingslib_expressive_value_preference_text_frame.xml b/packages/SettingsLib/ValuePreference/res/layout/settingslib_expressive_value_preference_text_frame.xml
new file mode 100644
index 000000000000..a385ee521a74
--- /dev/null
+++ b/packages/SettingsLib/ValuePreference/res/layout/settingslib_expressive_value_preference_text_frame.xml
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2025 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.
+ -->
+
+<RelativeLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="@dimen/settingslib_expressive_space_none"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:paddingVertical="@dimen/settingslib_expressive_space_small1"
+ android:paddingStart="@dimen/settingslib_expressive_space_none"
+ android:paddingEnd="@dimen/settingslib_expressive_space_small1"
+ android:filterTouchesWhenObscured="false">
+
+ <TextView
+ android:id="@android:id/title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="start"
+ android:textAlignment="viewStart"
+ android:maxLines="2"
+ android:textAppearance="@style/TextAppearance.SettingsLib.ValuePreferenceTitle"
+ android:ellipsize="marquee"/>
+
+ <TextView
+ android:id="@android:id/summary"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_below="@android:id/title"
+ android:layout_alignStart="@android:id/title"
+ android:layout_gravity="start"
+ android:textAlignment="viewStart"
+ android:textAppearance="?android:attr/textAppearanceListItemSecondary"
+ android:textColor="?android:attr/textColorSecondary"
+ android:maxLines="10"/>
+</RelativeLayout> \ No newline at end of file
diff --git a/packages/SettingsLib/ValuePreference/res/values/styles.xml b/packages/SettingsLib/ValuePreference/res/values/styles.xml
new file mode 100644
index 000000000000..284819498f64
--- /dev/null
+++ b/packages/SettingsLib/ValuePreference/res/values/styles.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2025 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.
+ -->
+
+<resources xmlns:tools="http://schemas.android.com/tools">
+ <style name="TextAppearance.SettingsLib.ValuePreferenceTitle"
+ parent="@style/TextAppearance.SettingsLib.DisplaySmall">
+ <item name="android:textColor">@color/settingslib_text_color_primary</item>
+ </style>
+</resources>
diff --git a/packages/SettingsLib/ValuePreference/src/com/android/settingslib/widget/ValuePreference.kt b/packages/SettingsLib/ValuePreference/src/com/android/settingslib/widget/ValuePreference.kt
new file mode 100644
index 000000000000..475638c8b406
--- /dev/null
+++ b/packages/SettingsLib/ValuePreference/src/com/android/settingslib/widget/ValuePreference.kt
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2025 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.settingslib.widget
+
+import android.content.Context
+import android.util.AttributeSet
+import androidx.preference.Preference
+import androidx.preference.PreferenceViewHolder
+import com.android.settingslib.widget.preference.value.R
+
+/** The BulletPreference shows a text which describe a feature. */
+class ValuePreference
+@JvmOverloads
+constructor(
+ context: Context,
+ attrs: AttributeSet? = null,
+ defStyleAttr: Int = 0,
+ defStyleRes: Int = 0
+) : Preference(context, attrs, defStyleAttr, defStyleRes) {
+
+ init {
+ layoutResource = R.layout.settingslib_expressive_value_preference
+ }
+
+ override fun onBindViewHolder(holder: PreferenceViewHolder) {
+ super.onBindViewHolder(holder)
+ holder.isDividerAllowedAbove = false
+ holder.isDividerAllowedBelow = false
+ }
+}
diff --git a/packages/SettingsLib/aconfig/settingslib.aconfig b/packages/SettingsLib/aconfig/settingslib.aconfig
index 349d13a29b05..90e5a010416c 100644
--- a/packages/SettingsLib/aconfig/settingslib.aconfig
+++ b/packages/SettingsLib/aconfig/settingslib.aconfig
@@ -37,16 +37,6 @@ flag {
}
flag {
- name: "enable_set_preferred_transport_for_le_audio_device"
- namespace: "bluetooth"
- description: "Enable setting preferred transport for Le Audio device"
- bug: "330581926"
- metadata {
- purpose: PURPOSE_BUGFIX
- }
-}
-
-flag {
name: "enable_determining_advanced_details_header_with_metadata"
namespace: "pixel_cross_device_control"
description: "Use metadata instead of device type to determine whether a bluetooth device should use advanced details header."
diff --git a/packages/SettingsLib/res/values-af/strings.xml b/packages/SettingsLib/res/values-af/strings.xml
index 58ddc723b77a..54074ec63456 100644
--- a/packages/SettingsLib/res/values-af/strings.xml
+++ b/packages/SettingsLib/res/values-af/strings.xml
@@ -539,8 +539,7 @@
<string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Beheer deur Beperkte Instellings"</string>
<string name="disabled_in_phone_call_text" msgid="6568931334337318320">"Onbeskikbaar tydens oproepe"</string>
<string name="disabled" msgid="8017887509554714950">"Gedeaktiveer"</string>
- <!-- no translation found for enabled (3997122818554810678) -->
- <skip />
+ <string name="enabled" msgid="3997122818554810678">"Geaktiveer"</string>
<string name="external_source_trusted" msgid="1146522036773132905">"Toegelaat"</string>
<string name="external_source_untrusted" msgid="5037891688911672227">"Nie toegelaat nie"</string>
<string name="install_other_apps" msgid="3232595082023199454">"Installeer onbekende apps"</string>
@@ -612,7 +611,7 @@
<string name="media_transfer_this_phone" msgid="7194341457812151531">"Hierdie foon"</string>
<string name="media_transfer_digital_line_name" msgid="312091711951124301">"S/PDIF"</string>
<string name="media_transfer_analog_line_name" msgid="1841163866716302104">"Analoog"</string>
- <string name="media_transfer_aux_line_name" msgid="894135835967856689">"AANVULLENDE"</string>
+ <string name="media_transfer_aux_line_name" msgid="894135835967856689">"Aanvullend"</string>
<string name="media_output_status_unknown_error" msgid="5098565887497902222">"Kan nie op hierdie toestel speel nie"</string>
<string name="media_output_status_require_premium" msgid="8411255800047014822">"Gradeer rekening op om oor te skakel"</string>
<string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"Kan nie aflaaie hier speel nie"</string>
@@ -628,6 +627,7 @@
<string name="tv_media_transfer_internal_speakers" msgid="4662765121700213785">"Ingeboude luidspreker"</string>
<string name="tv_media_transfer_hdmi_title" msgid="6715658310934507444">"TV-oudio"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Kan nie koppel nie. Skakel toestel af en weer aan"</string>
+ <string name="bluetooth_key_missing_subtext" msgid="1003639333895028298">"Kan nie koppel nie"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Bedrade oudiotoestel"</string>
<string name="help_label" msgid="3528360748637781274">"Hulp en terugvoer"</string>
<string name="storage_category" msgid="2287342585424631813">"Berging"</string>
diff --git a/packages/SettingsLib/res/values-am/strings.xml b/packages/SettingsLib/res/values-am/strings.xml
index 3844a4b69d86..1fab31b41dec 100644
--- a/packages/SettingsLib/res/values-am/strings.xml
+++ b/packages/SettingsLib/res/values-am/strings.xml
@@ -539,8 +539,7 @@
<string name="disabled_by_app_ops_text" msgid="8373595926549098012">"በተገደበ ቅንብር ቁጥጥር የሚደረግበት"</string>
<string name="disabled_in_phone_call_text" msgid="6568931334337318320">"በጥሪዎች ጊዜ አይገኝም"</string>
<string name="disabled" msgid="8017887509554714950">"ቦዝኗል"</string>
- <!-- no translation found for enabled (3997122818554810678) -->
- <skip />
+ <string name="enabled" msgid="3997122818554810678">"ነቅቷል"</string>
<string name="external_source_trusted" msgid="1146522036773132905">"ይፈቀዳል"</string>
<string name="external_source_untrusted" msgid="5037891688911672227">"አይፈቀድም"</string>
<string name="install_other_apps" msgid="3232595082023199454">"ያልታወቁ መተግበሪያዎችን ይጫኑ"</string>
@@ -628,6 +627,7 @@
<string name="tv_media_transfer_internal_speakers" msgid="4662765121700213785">"አብሮ የተሰራ ድምፅ ማውጫ"</string>
<string name="tv_media_transfer_hdmi_title" msgid="6715658310934507444">"የTV ኦዲዮ"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"መገናኘት ላይ ችግር። መሳሪያውን ያጥፉት እና እንደገና ያብሩት"</string>
+ <string name="bluetooth_key_missing_subtext" msgid="1003639333895028298">"ማገናኘት አልተቻለም"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"ባለገመድ የኦዲዮ መሣሪያ"</string>
<string name="help_label" msgid="3528360748637781274">"እገዛ እና ግብረመልስ"</string>
<string name="storage_category" msgid="2287342585424631813">"ማከማቻ"</string>
diff --git a/packages/SettingsLib/res/values-ar/strings.xml b/packages/SettingsLib/res/values-ar/strings.xml
index 1344a25672d2..6cb81540a52b 100644
--- a/packages/SettingsLib/res/values-ar/strings.xml
+++ b/packages/SettingsLib/res/values-ar/strings.xml
@@ -627,6 +627,7 @@
<string name="tv_media_transfer_internal_speakers" msgid="4662765121700213785">"مكبِّر الصوت المُدمَج"</string>
<string name="tv_media_transfer_hdmi_title" msgid="6715658310934507444">"صوت التلفزيون"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"حدثت مشكلة أثناء الاتصال. يُرجى إيقاف الجهاز ثم إعادة تشغيله."</string>
+ <string name="bluetooth_key_missing_subtext" msgid="1003639333895028298">"يتعذّر الاتصال"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"جهاز سماعي سلكي"</string>
<string name="help_label" msgid="3528360748637781274">"المساعدة والملاحظات"</string>
<string name="storage_category" msgid="2287342585424631813">"مساحة التخزين"</string>
diff --git a/packages/SettingsLib/res/values-as/strings.xml b/packages/SettingsLib/res/values-as/strings.xml
index 56f73df512a1..b39593eae46c 100644
--- a/packages/SettingsLib/res/values-as/strings.xml
+++ b/packages/SettingsLib/res/values-as/strings.xml
@@ -77,7 +77,7 @@
<string name="osu_sign_up_failed" msgid="5605453599586001793">"ছাইন আপ সম্পূৰ্ণ কৰিব পৰা নগ’ল। আকৌ চেষ্টা কৰিবলৈ টিপক।"</string>
<string name="osu_sign_up_complete" msgid="7640183358878916847">"ছাইন আপ সম্পূৰ্ণ হৈছে সংযোগ কৰি থকা হৈছে…"</string>
<string name="speed_label_slow" msgid="6069917670665664161">"লেহেমীয়া"</string>
- <string name="speed_label_okay" msgid="1253594383880810424">"ঠিক"</string>
+ <string name="speed_label_okay" msgid="1253594383880810424">"ঠিক আছে"</string>
<string name="speed_label_fast" msgid="2677719134596044051">"দ্ৰুত"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"অতি দ্ৰুত"</string>
<string name="wifi_passpoint_expired" msgid="6540867261754427561">"ম্যাদ উকলিছে"</string>
@@ -539,8 +539,7 @@
<string name="disabled_by_app_ops_text" msgid="8373595926549098012">"প্ৰতিবন্ধিত ছেটিঙৰ দ্বাৰা নিয়ন্ত্ৰিত"</string>
<string name="disabled_in_phone_call_text" msgid="6568931334337318320">"কল চলি থকাৰ সময়ত উপলব্ধ নহয়"</string>
<string name="disabled" msgid="8017887509554714950">"নিষ্ক্ৰিয়"</string>
- <!-- no translation found for enabled (3997122818554810678) -->
- <skip />
+ <string name="enabled" msgid="3997122818554810678">"সক্ষম কৰা আছে"</string>
<string name="external_source_trusted" msgid="1146522036773132905">"অনুমতি দিয়া হৈছে"</string>
<string name="external_source_untrusted" msgid="5037891688911672227">"অনুমতি দিয়া হোৱা নাই"</string>
<string name="install_other_apps" msgid="3232595082023199454">"অজ্ঞাত এপ্ ইনষ্টল কৰক"</string>
@@ -579,7 +578,7 @@
<string name="next" msgid="2699398661093607009">"পৰৱৰ্তী"</string>
<string name="back" msgid="5554327870352703710">"উভতি যাওক"</string>
<string name="save" msgid="3745809743277153149">"ছেভ কৰক"</string>
- <string name="okay" msgid="949938843324579502">"ঠিক"</string>
+ <string name="okay" msgid="949938843324579502">"ঠিক আছে"</string>
<string name="done" msgid="381184316122520313">"হ’ল"</string>
<string name="alarms_and_reminders_label" msgid="6918395649731424294">"এলাৰ্ম আৰু ৰিমাইণ্ডাৰ"</string>
<string name="alarms_and_reminders_switch_title" msgid="4939393911531826222">"এলাৰ্ম আৰু ৰিমাইণ্ডাৰ ছেট কৰাৰ অনুমতি দিয়ক"</string>
@@ -612,7 +611,7 @@
<string name="media_transfer_this_phone" msgid="7194341457812151531">"এই ফ’নটো"</string>
<string name="media_transfer_digital_line_name" msgid="312091711951124301">"S/PDIF"</string>
<string name="media_transfer_analog_line_name" msgid="1841163866716302104">"এনালগ"</string>
- <string name="media_transfer_aux_line_name" msgid="894135835967856689">"অক্স"</string>
+ <string name="media_transfer_aux_line_name" msgid="894135835967856689">"AUX"</string>
<string name="media_output_status_unknown_error" msgid="5098565887497902222">"এই ডিভাইচটো প্লে\' কৰিব নোৱাৰি"</string>
<string name="media_output_status_require_premium" msgid="8411255800047014822">"সলনি কৰিবলৈ একাউণ্ট আপগ্ৰে’ড কৰক"</string>
<string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"ইয়াত ডাউনল’ডসমূহ প্লে’ কৰিব নোৱাৰি"</string>
@@ -628,6 +627,7 @@
<string name="tv_media_transfer_internal_speakers" msgid="4662765121700213785">"বিল্ট-ইন স্পীকাৰ"</string>
<string name="tv_media_transfer_hdmi_title" msgid="6715658310934507444">"টিভিৰ অডিঅ’"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"সংযোগ হোৱাত সমস্যা হৈছে। ডিভাইচটো অফ কৰি পুনৰ অন কৰক"</string>
+ <string name="bluetooth_key_missing_subtext" msgid="1003639333895028298">"সংযোগ কৰিব নোৱাৰি"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"তাঁৰযুক্ত অডিঅ’ ডিভাইচ"</string>
<string name="help_label" msgid="3528360748637781274">"সহায় আৰু মতামত"</string>
<string name="storage_category" msgid="2287342585424631813">"ষ্ট’ৰেজ"</string>
diff --git a/packages/SettingsLib/res/values-az/strings.xml b/packages/SettingsLib/res/values-az/strings.xml
index 878d8972e9a8..de74a46afe53 100644
--- a/packages/SettingsLib/res/values-az/strings.xml
+++ b/packages/SettingsLib/res/values-az/strings.xml
@@ -627,6 +627,7 @@
<string name="tv_media_transfer_internal_speakers" msgid="4662765121700213785">"Daxili dinamik"</string>
<string name="tv_media_transfer_hdmi_title" msgid="6715658310934507444">"TV audiosu"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Qoşulmaqla bağlı problem. Cihazı deaktiv edin, sonra yenidən aktiv edin"</string>
+ <string name="bluetooth_key_missing_subtext" msgid="1003639333895028298">"Qoşulmaq mümkün deyil"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Simli audio cihaz"</string>
<string name="help_label" msgid="3528360748637781274">"Yardım və rəy"</string>
<string name="storage_category" msgid="2287342585424631813">"Yaddaş"</string>
diff --git a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
index 97873e5a2d2a..3db1617619bb 100644
--- a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
+++ b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
@@ -539,8 +539,7 @@
<string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Kontrolišu ograničena podešavanja"</string>
<string name="disabled_in_phone_call_text" msgid="6568931334337318320">"Nedostupno tokom poziva"</string>
<string name="disabled" msgid="8017887509554714950">"Onemogućeno"</string>
- <!-- no translation found for enabled (3997122818554810678) -->
- <skip />
+ <string name="enabled" msgid="3997122818554810678">"Omogućeno"</string>
<string name="external_source_trusted" msgid="1146522036773132905">"Dozvoljeno"</string>
<string name="external_source_untrusted" msgid="5037891688911672227">"Nije dozvoljeno"</string>
<string name="install_other_apps" msgid="3232595082023199454">"Instaliranje nepoznatih aplikacija"</string>
@@ -628,6 +627,7 @@
<string name="tv_media_transfer_internal_speakers" msgid="4662765121700213785">"Ugrađeni zvučnik"</string>
<string name="tv_media_transfer_hdmi_title" msgid="6715658310934507444">"Zvuk TV-a"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Problem pri povezivanju. Isključite uređaj, pa ga ponovo uključite"</string>
+ <string name="bluetooth_key_missing_subtext" msgid="1003639333895028298">"Povezivanje nije uspelo"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Žičani audio uređaj"</string>
<string name="help_label" msgid="3528360748637781274">"Pomoć i povratne informacije"</string>
<string name="storage_category" msgid="2287342585424631813">"Memorijski prostor"</string>
diff --git a/packages/SettingsLib/res/values-be/strings.xml b/packages/SettingsLib/res/values-be/strings.xml
index 78a5eaf388f0..74dd6669b50d 100644
--- a/packages/SettingsLib/res/values-be/strings.xml
+++ b/packages/SettingsLib/res/values-be/strings.xml
@@ -539,8 +539,7 @@
<string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Пад кіраваннем Абмежаванага наладжвання"</string>
<string name="disabled_in_phone_call_text" msgid="6568931334337318320">"Недаступна падчас выклікаў"</string>
<string name="disabled" msgid="8017887509554714950">"Адключанае"</string>
- <!-- no translation found for enabled (3997122818554810678) -->
- <skip />
+ <string name="enabled" msgid="3997122818554810678">"Уключана"</string>
<string name="external_source_trusted" msgid="1146522036773132905">"Дазволена"</string>
<string name="external_source_untrusted" msgid="5037891688911672227">"Забаронена"</string>
<string name="install_other_apps" msgid="3232595082023199454">"Усталёўка невядомых праграм"</string>
@@ -628,6 +627,7 @@
<string name="tv_media_transfer_internal_speakers" msgid="4662765121700213785">"Убудаваны дынамік"</string>
<string name="tv_media_transfer_hdmi_title" msgid="6715658310934507444">"Аўдыя тэлевізара"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Праблема з падключэннем. Выключыце і зноў уключыце прыладу"</string>
+ <string name="bluetooth_key_missing_subtext" msgid="1003639333895028298">"Не ўдаецца падключыцца"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Правадная аўдыяпрылада"</string>
<string name="help_label" msgid="3528360748637781274">"Даведка і водгукі"</string>
<string name="storage_category" msgid="2287342585424631813">"Сховішча"</string>
diff --git a/packages/SettingsLib/res/values-bg/strings.xml b/packages/SettingsLib/res/values-bg/strings.xml
index f49b1d2b5adc..b0ff126d1c1f 100644
--- a/packages/SettingsLib/res/values-bg/strings.xml
+++ b/packages/SettingsLib/res/values-bg/strings.xml
@@ -610,7 +610,7 @@
<string name="media_transfer_default_device_name" msgid="4315604017399871828">"Свързано устройство"</string>
<string name="media_transfer_this_phone" msgid="7194341457812151531">"Този телефон"</string>
<string name="media_transfer_digital_line_name" msgid="312091711951124301">"S/PDIF"</string>
- <string name="media_transfer_analog_line_name" msgid="1841163866716302104">"Аналогов"</string>
+ <string name="media_transfer_analog_line_name" msgid="1841163866716302104">"Аналогов аудиоизход"</string>
<string name="media_transfer_aux_line_name" msgid="894135835967856689">"AUX"</string>
<string name="media_output_status_unknown_error" msgid="5098565887497902222">"Възпроизвеждането не е възможно на това устройство"</string>
<string name="media_output_status_require_premium" msgid="8411255800047014822">"Надстройте профила, за да превключите"</string>
@@ -627,6 +627,7 @@
<string name="tv_media_transfer_internal_speakers" msgid="4662765121700213785">"Вграден високоговорител"</string>
<string name="tv_media_transfer_hdmi_title" msgid="6715658310934507444">"Аудио на телевизора"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"При свързването възникна проблем. Изключете устройството и го включете отново"</string>
+ <string name="bluetooth_key_missing_subtext" msgid="1003639333895028298">"Няма връзка"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Аудиоустройство с кабел"</string>
<string name="help_label" msgid="3528360748637781274">"Помощ и отзиви"</string>
<string name="storage_category" msgid="2287342585424631813">"Хранилище"</string>
diff --git a/packages/SettingsLib/res/values-bn/strings.xml b/packages/SettingsLib/res/values-bn/strings.xml
index 79e4ed9600aa..9afef6243643 100644
--- a/packages/SettingsLib/res/values-bn/strings.xml
+++ b/packages/SettingsLib/res/values-bn/strings.xml
@@ -539,8 +539,7 @@
<string name="disabled_by_app_ops_text" msgid="8373595926549098012">"এটি বিধিনিষেধ সেটিং থেকে নিয়ন্ত্রণ করা হয়"</string>
<string name="disabled_in_phone_call_text" msgid="6568931334337318320">"কল চলাকালীন উপলভ্য হবে না"</string>
<string name="disabled" msgid="8017887509554714950">"অক্ষম হয়েছে"</string>
- <!-- no translation found for enabled (3997122818554810678) -->
- <skip />
+ <string name="enabled" msgid="3997122818554810678">"চালু করা আছে"</string>
<string name="external_source_trusted" msgid="1146522036773132905">"অনুমোদিত"</string>
<string name="external_source_untrusted" msgid="5037891688911672227">"অনুমোদিত নয়"</string>
<string name="install_other_apps" msgid="3232595082023199454">"অজানা অ্যাপ ইনস্টল করা"</string>
@@ -628,6 +627,7 @@
<string name="tv_media_transfer_internal_speakers" msgid="4662765121700213785">"বিল্ট-ইন স্পিকার"</string>
<string name="tv_media_transfer_hdmi_title" msgid="6715658310934507444">"টিভি অডিও"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"কানেক্ট করতে সমস্যা হচ্ছে। ডিভাইস বন্ধ করে আবার চালু করুন"</string>
+ <string name="bluetooth_key_missing_subtext" msgid="1003639333895028298">"কানেক্ট করা যাচ্ছে না"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"ওয়্যার অডিও ডিভাইস"</string>
<string name="help_label" msgid="3528360748637781274">"সহায়তা ও মতামত"</string>
<string name="storage_category" msgid="2287342585424631813">"স্টোরেজ"</string>
diff --git a/packages/SettingsLib/res/values-bs/strings.xml b/packages/SettingsLib/res/values-bs/strings.xml
index d06ce341fa99..22de5005947c 100644
--- a/packages/SettingsLib/res/values-bs/strings.xml
+++ b/packages/SettingsLib/res/values-bs/strings.xml
@@ -539,8 +539,7 @@
<string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Kontrolira ograničena postavka"</string>
<string name="disabled_in_phone_call_text" msgid="6568931334337318320">"Nije dostupno tokom poziva"</string>
<string name="disabled" msgid="8017887509554714950">"Onemogućeno"</string>
- <!-- no translation found for enabled (3997122818554810678) -->
- <skip />
+ <string name="enabled" msgid="3997122818554810678">"Omogućeno"</string>
<string name="external_source_trusted" msgid="1146522036773132905">"Dozvoljeno"</string>
<string name="external_source_untrusted" msgid="5037891688911672227">"Nije dozvoljeno"</string>
<string name="install_other_apps" msgid="3232595082023199454">"Instaliranje nepoznatih aplikacija"</string>
@@ -628,6 +627,7 @@
<string name="tv_media_transfer_internal_speakers" msgid="4662765121700213785">"Ugrađeni zvučnik"</string>
<string name="tv_media_transfer_hdmi_title" msgid="6715658310934507444">"Zvuk TV-a"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Došlo je do problema prilikom povezivanja. Isključite, pa ponovo uključite uređaj"</string>
+ <string name="bluetooth_key_missing_subtext" msgid="1003639333895028298">"Nije se moguće povezati"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Žičani audio uređaj"</string>
<string name="help_label" msgid="3528360748637781274">"Pomoć i povratne informacije"</string>
<string name="storage_category" msgid="2287342585424631813">"Pohrana"</string>
diff --git a/packages/SettingsLib/res/values-ca/strings.xml b/packages/SettingsLib/res/values-ca/strings.xml
index 776caae3b828..da50f09164a6 100644
--- a/packages/SettingsLib/res/values-ca/strings.xml
+++ b/packages/SettingsLib/res/values-ca/strings.xml
@@ -539,8 +539,7 @@
<string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Controlat per l\'opció de configuració restringida"</string>
<string name="disabled_in_phone_call_text" msgid="6568931334337318320">"No està disponible durant les trucades"</string>
<string name="disabled" msgid="8017887509554714950">"Desactivat"</string>
- <!-- no translation found for enabled (3997122818554810678) -->
- <skip />
+ <string name="enabled" msgid="3997122818554810678">"Activat"</string>
<string name="external_source_trusted" msgid="1146522036773132905">"Amb permís"</string>
<string name="external_source_untrusted" msgid="5037891688911672227">"Sense permís"</string>
<string name="install_other_apps" msgid="3232595082023199454">"Instal·la aplicacions desconegudes"</string>
@@ -628,6 +627,7 @@
<string name="tv_media_transfer_internal_speakers" msgid="4662765121700213785">"Altaveu integrat"</string>
<string name="tv_media_transfer_hdmi_title" msgid="6715658310934507444">"Àudio de la televisió"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Hi ha hagut un problema amb la connexió. Apaga el dispositiu i torna\'l a encendre."</string>
+ <string name="bluetooth_key_missing_subtext" msgid="1003639333895028298">"No es pot establir la connexió"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Dispositiu d\'àudio amb cable"</string>
<string name="help_label" msgid="3528360748637781274">"Ajuda i suggeriments"</string>
<string name="storage_category" msgid="2287342585424631813">"Emmagatzematge"</string>
diff --git a/packages/SettingsLib/res/values-cs/strings.xml b/packages/SettingsLib/res/values-cs/strings.xml
index 02722fd98959..9d61a5a10502 100644
--- a/packages/SettingsLib/res/values-cs/strings.xml
+++ b/packages/SettingsLib/res/values-cs/strings.xml
@@ -539,8 +539,7 @@
<string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Spravováno omezeným nastavením"</string>
<string name="disabled_in_phone_call_text" msgid="6568931334337318320">"Při volání nedostupné"</string>
<string name="disabled" msgid="8017887509554714950">"Deaktivováno"</string>
- <!-- no translation found for enabled (3997122818554810678) -->
- <skip />
+ <string name="enabled" msgid="3997122818554810678">"Zapnuto"</string>
<string name="external_source_trusted" msgid="1146522036773132905">"Povoleno"</string>
<string name="external_source_untrusted" msgid="5037891688911672227">"Není povoleno"</string>
<string name="install_other_apps" msgid="3232595082023199454">"Instalace neznámých aplikací"</string>
@@ -611,7 +610,7 @@
<string name="media_transfer_default_device_name" msgid="4315604017399871828">"Připojené zařízení"</string>
<string name="media_transfer_this_phone" msgid="7194341457812151531">"Tento telefon"</string>
<string name="media_transfer_digital_line_name" msgid="312091711951124301">"S/PDIF"</string>
- <string name="media_transfer_analog_line_name" msgid="1841163866716302104">"Analogové"</string>
+ <string name="media_transfer_analog_line_name" msgid="1841163866716302104">"Analogový výstup"</string>
<string name="media_transfer_aux_line_name" msgid="894135835967856689">"AUX"</string>
<string name="media_output_status_unknown_error" msgid="5098565887497902222">"V zařízení nelze přehrávat"</string>
<string name="media_output_status_require_premium" msgid="8411255800047014822">"Účet je třeba upgradovat"</string>
@@ -628,6 +627,7 @@
<string name="tv_media_transfer_internal_speakers" msgid="4662765121700213785">"Vestavěný reproduktor"</string>
<string name="tv_media_transfer_hdmi_title" msgid="6715658310934507444">"Zvuk televize"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Problém s připojením. Vypněte zařízení a znovu jej zapněte"</string>
+ <string name="bluetooth_key_missing_subtext" msgid="1003639333895028298">"Nelze se připojit"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Kabelové audiozařízení"</string>
<string name="help_label" msgid="3528360748637781274">"Nápověda a zpětná vazba"</string>
<string name="storage_category" msgid="2287342585424631813">"Úložiště"</string>
diff --git a/packages/SettingsLib/res/values-da/strings.xml b/packages/SettingsLib/res/values-da/strings.xml
index 4d37578c68b9..91b6420d4183 100644
--- a/packages/SettingsLib/res/values-da/strings.xml
+++ b/packages/SettingsLib/res/values-da/strings.xml
@@ -539,8 +539,7 @@
<string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Styres af en begrænset indstilling"</string>
<string name="disabled_in_phone_call_text" msgid="6568931334337318320">"Kan ikke bruges under opkald"</string>
<string name="disabled" msgid="8017887509554714950">"Deaktiveret"</string>
- <!-- no translation found for enabled (3997122818554810678) -->
- <skip />
+ <string name="enabled" msgid="3997122818554810678">"Aktiveret"</string>
<string name="external_source_trusted" msgid="1146522036773132905">"Tilladt"</string>
<string name="external_source_untrusted" msgid="5037891688911672227">"Ikke tilladt"</string>
<string name="install_other_apps" msgid="3232595082023199454">"Installer ukendte apps"</string>
@@ -628,6 +627,7 @@
<string name="tv_media_transfer_internal_speakers" msgid="4662765121700213785">"Indbygget højttaler"</string>
<string name="tv_media_transfer_hdmi_title" msgid="6715658310934507444">"TV-lyd"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Der kunne ikke oprettes forbindelse. Sluk og tænd enheden"</string>
+ <string name="bluetooth_key_missing_subtext" msgid="1003639333895028298">"Der kan ikke oprettes forbindelse"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Lydenhed med ledning"</string>
<string name="help_label" msgid="3528360748637781274">"Hjælp og feedback"</string>
<string name="storage_category" msgid="2287342585424631813">"Lager"</string>
diff --git a/packages/SettingsLib/res/values-de/strings.xml b/packages/SettingsLib/res/values-de/strings.xml
index 3e57002a7a59..0c7ea025a44e 100644
--- a/packages/SettingsLib/res/values-de/strings.xml
+++ b/packages/SettingsLib/res/values-de/strings.xml
@@ -539,8 +539,7 @@
<string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Gesteuert durch eingeschränkte Einstellung"</string>
<string name="disabled_in_phone_call_text" msgid="6568931334337318320">"Während Anrufen nicht verfügbar"</string>
<string name="disabled" msgid="8017887509554714950">"Deaktiviert"</string>
- <!-- no translation found for enabled (3997122818554810678) -->
- <skip />
+ <string name="enabled" msgid="3997122818554810678">"Aktiviert"</string>
<string name="external_source_trusted" msgid="1146522036773132905">"Zugelassen"</string>
<string name="external_source_untrusted" msgid="5037891688911672227">"Nicht zugelassen"</string>
<string name="install_other_apps" msgid="3232595082023199454">"Installieren unbekannter Apps"</string>
@@ -628,6 +627,7 @@
<string name="tv_media_transfer_internal_speakers" msgid="4662765121700213785">"Integrierter Lautsprecher"</string>
<string name="tv_media_transfer_hdmi_title" msgid="6715658310934507444">"TV‑Audio"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Verbindung kann nicht hergestellt werden. Schalte das Gerät aus und wieder ein."</string>
+ <string name="bluetooth_key_missing_subtext" msgid="1003639333895028298">"Verbindung nicht möglich"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Netzbetriebenes Audiogerät"</string>
<string name="help_label" msgid="3528360748637781274">"Hilfe und Feedback"</string>
<string name="storage_category" msgid="2287342585424631813">"Speicher"</string>
diff --git a/packages/SettingsLib/res/values-el/strings.xml b/packages/SettingsLib/res/values-el/strings.xml
index be1135690e62..06620f30d7f1 100644
--- a/packages/SettingsLib/res/values-el/strings.xml
+++ b/packages/SettingsLib/res/values-el/strings.xml
@@ -257,14 +257,14 @@
<string name="vpn_settings_not_available" msgid="2894137119965668920">"Οι ρυθμίσεις VPN δεν είναι διαθέσιμες γι\' αυτόν το χρήστη"</string>
<string name="tethering_settings_not_available" msgid="266821736434699780">"Οι ρυθμίσεις σύνδεσης μέσω κινητής συσκευής δεν είναι διαθέσιμες γι\' αυτόν το χρήστη"</string>
<string name="apn_settings_not_available" msgid="1147111671403342300">"Οι ρυθμίσεις ονόματος σημείου πρόσβασης δεν είναι διαθέσιμες γι\' αυτόν το χρήστη"</string>
- <string name="enable_adb" msgid="8072776357237289039">"Εντοπισμός σφαλμάτων USB"</string>
+ <string name="enable_adb" msgid="8072776357237289039">"Αποσφαλμάτωση USB"</string>
<string name="enable_adb_summary" msgid="3711526030096574316">"Λειτουργία εντοπισμού σφαλμάτων όταν το USB είναι συνδεδεμένο"</string>
- <string name="clear_adb_keys" msgid="3010148733140369917">"Ανάκληση εξ/σεων εντ/σμού σφ/των USB"</string>
- <string name="enable_adb_wireless" msgid="6973226350963971018">"Ασύρματος εντοπισμός σφαλμάτων"</string>
+ <string name="clear_adb_keys" msgid="3010148733140369917">"Ανάκληση εξ/σεων αποσφαλμάτ. USB"</string>
+ <string name="enable_adb_wireless" msgid="6973226350963971018">"Ασύρματη αποσφαλμάτωση"</string>
<string name="enable_adb_wireless_summary" msgid="7344391423657093011">"Λειτουργία εντοπισμού σφαλμάτων όταν το Wi‑Fi είναι συνδεδεμένο"</string>
<string name="adb_wireless_error" msgid="721958772149779856">"Σφάλμα"</string>
- <string name="adb_wireless_settings" msgid="2295017847215680229">"Ασύρματος εντοπισμός σφαλμάτων"</string>
- <string name="adb_wireless_list_empty_off" msgid="1713707973837255490">"Για να δείτε και να χρησιμοποιήσετε τις διαθέσιμες συσκευές, ενεργοποιήστε τον ασύρματο εντοπισμό σφαλμάτων"</string>
+ <string name="adb_wireless_settings" msgid="2295017847215680229">"Ασύρματη αποσφαλμάτωση"</string>
+ <string name="adb_wireless_list_empty_off" msgid="1713707973837255490">"Για να δείτε και να χρησιμοποιήσετε τις διαθέσιμες συσκευές, ενεργοποιήστε την ασύρματη αποσφαλμάτωση"</string>
<string name="adb_pair_method_qrcode_title" msgid="6982904096137468634">"Σύζευξη συσκευής με κωδικό QR"</string>
<string name="adb_pair_method_qrcode_summary" msgid="7130694277228970888">"Σύζευξη νέων συσκευών με τη χρήση σαρωτή κωδικών QR"</string>
<string name="adb_pair_method_code_title" msgid="1122590300445142904">"Σύζευξη συσκευής με κωδικό σύζευξης"</string>
@@ -287,7 +287,7 @@
<string name="adb_wireless_qrcode_pairing_title" msgid="1906409667944674707">"Σάρωση κωδικού QR"</string>
<string name="adb_wireless_qrcode_pairing_description" msgid="6014121407143607851">"Σύζευξη συσκευής μέσω Wi‑Fi με τη σάρωση ενός κωδικού QR"</string>
<string name="adb_wireless_no_network_msg" msgid="2365795244718494658">"Συνδεθείτε σε ένα δίκτυο Wi-Fi"</string>
- <string name="keywords_adb_wireless" msgid="6507505581882171240">"adb, εντοπισμός σφαλμάτων, προγραμματιστής"</string>
+ <string name="keywords_adb_wireless" msgid="6507505581882171240">"adb, αποσφαλμάτωση, προγραμματιστής"</string>
<string name="bugreport_in_power" msgid="8664089072534638709">"Συντόμευση αναφοράς σφαλμάτων"</string>
<string name="bugreport_in_power_summary" msgid="1885529649381831775">"Εμφάνιση κουμπιού στο μενού ενεργοποίησης για τη λήψη αναφοράς σφαλμάτων"</string>
<string name="keep_screen_on" msgid="1187161672348797558">"Παραμονή σε λειτουργία"</string>
@@ -353,11 +353,11 @@
<string name="debug_view_attributes" msgid="3539609843984208216">"Ενεργοποίηση του ελέγχου χαρακτηριστικών προβολής"</string>
<string name="mobile_data_always_on_summary" msgid="1112156365594371019">"Τα δεδομένα κινητής τηλεφωνίας να διατηρούνται πάντα ενεργά, ακόμα και όταν είναι ενεργό το Wi-Fi (για γρήγορη εναλλαγή δικτύου)."</string>
<string name="tethering_hardware_offload_summary" msgid="7801345335142803029">"Χρήση της σύνδεσης επιτάχυνσης υλικού εάν υπάρχει"</string>
- <string name="adb_warning_title" msgid="7708653449506485728">"Να επιτρέπεται ο εντοπισμός σφαλμάτων USB;"</string>
- <string name="adb_warning_message" msgid="8145270656419669221">"Ο εντοπισμός σφαλμάτων USB προορίζεται μόνο για σκοπούς προγραμματισμού. Χρησιμοποιήστε τον για αντιγραφή δεδομένων μεταξύ του υπολογιστή και της συσκευής σας, για την εγκατάσταση εφαρμογών στη συσκευή σας χωρίς προειδοποίηση και για την ανάγνωση δεδομένων καταγραφής."</string>
- <string name="adbwifi_warning_title" msgid="727104571653031865">"Να επιτρέπεται ο ασύρματος εντοπισμός σφαλμάτων;"</string>
- <string name="adbwifi_warning_message" msgid="8005936574322702388">"Ο ασύρματος εντοπισμός σφαλμάτων προορίζεται μόνο για σκοπούς προγραμματισμού. Χρησιμοποιήστε τον για αντιγραφή δεδομένων μεταξύ του υπολογιστή και της συσκευής σας, για την εγκατάσταση εφαρμογών στη συσκευή σας χωρίς ειδοποίηση και για την ανάγνωση δεδομένων καταγραφής."</string>
- <string name="adb_keys_warning_message" msgid="2968555274488101220">"Ανάκληση πρόσβασης στον εντοπισμό σφαλμάτων USB από όλους τους υπολογιστές για τους οποίους είχατε εξουσιοδότηση στο παρελθόν;"</string>
+ <string name="adb_warning_title" msgid="7708653449506485728">"Να επιτρέπεται η αποσφαλμάτωση USB;"</string>
+ <string name="adb_warning_message" msgid="8145270656419669221">"Η αποσφαλμάτωση USB προορίζεται μόνο για σκοπούς προγραμματισμού. Χρησιμοποιήστε τον για αντιγραφή δεδομένων μεταξύ του υπολογιστή και της συσκευής σας, για την εγκατάσταση εφαρμογών στη συσκευή σας χωρίς προειδοποίηση και για την ανάγνωση δεδομένων καταγραφής."</string>
+ <string name="adbwifi_warning_title" msgid="727104571653031865">"Να επιτρέπεται η ασύρματη αποσφαλμάτωση;"</string>
+ <string name="adbwifi_warning_message" msgid="8005936574322702388">"Η ασύρματη αποσφαλμάτωση προορίζεται μόνο για σκοπούς προγραμματισμού. Χρησιμοποιήστε τον για αντιγραφή δεδομένων μεταξύ του υπολογιστή και της συσκευής σας, για την εγκατάσταση εφαρμογών στη συσκευή σας χωρίς ειδοποίηση και για την ανάγνωση δεδομένων καταγραφής."</string>
+ <string name="adb_keys_warning_message" msgid="2968555274488101220">"Ανάκληση πρόσβασης στην αποσφαλμάτωση USB από όλους τους υπολογιστές για τους οποίους είχατε εξουσιοδότηση στο παρελθόν;"</string>
<string name="dev_settings_warning_title" msgid="8251234890169074553">"Να επιτρέπεται η χρήση των ρυθμίσεων ανάπτυξης;"</string>
<string name="dev_settings_warning_message" msgid="37741686486073668">"Αυτές οι ρυθμίσεις προορίζονται για χρήση κατά την ανάπτυξη. Μπορούν να προκαλέσουν προβλήματα στη λειτουργία της συσκευής και των εφαρμογών σας."</string>
<string name="verify_apps_over_usb_title" msgid="6031809675604442636">"Επαλήθευση εφαρμογών μέσω USB"</string>
@@ -373,14 +373,14 @@
<string name="disable_linux_terminal_disclaimer" msgid="3054320531778388231">"Εάν απενεργοποιηθεί, τα δεδομένα τερματικού Linux θα διαγραφούν"</string>
<string name="hdcp_checking_title" msgid="3155692785074095986">"Έλεγχος HDCP"</string>
<string name="hdcp_checking_dialog_title" msgid="7691060297616217781">"Ρύθμιση συμπεριφοράς ελέγχου HDCP"</string>
- <string name="debug_debugging_category" msgid="535341063709248842">"Εντοπισμός σφαλμάτων"</string>
+ <string name="debug_debugging_category" msgid="535341063709248842">"Εντοπ. σφαλμ."</string>
<string name="debug_app" msgid="8903350241392391766">"Επιλέξτε εφαρμογή εντοπισμού σφαλμάτων"</string>
<string name="debug_app_not_set" msgid="1934083001283807188">"Δεν έχει οριστεί εφαρμογή εντοπισμού σφαλμάτων"</string>
- <string name="debug_app_set" msgid="6599535090477753651">"Εφαρμογή εντοπισμού σφαλμάτων: <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+ <string name="debug_app_set" msgid="6599535090477753651">"Εφαρμογή αποσφαλμάτωσης: <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="select_application" msgid="2543228890535466325">"Επιλέξτε εφαρμογή"</string>
<string name="no_application" msgid="9038334538870247690">"Καμία"</string>
<string name="wait_for_debugger" msgid="7461199843335409809">"Περιμένετε το εργαλείο εντοπισμού σφαλμάτων"</string>
- <string name="wait_for_debugger_summary" msgid="6846330006113363286">"Αναμονή εφαρμογής για να συνδεθεί ο εντοπισμός σφαλμάτων"</string>
+ <string name="wait_for_debugger_summary" msgid="6846330006113363286">"Αναμονή εφαρμογής για να συνδεθεί η αποσφαλμάτωση"</string>
<string name="debug_input_category" msgid="7349460906970849771">"Εισαγωγή"</string>
<string name="debug_drawing_category" msgid="5066171112313666619">"Σχέδιο"</string>
<string name="debug_hw_drawing_category" msgid="5830815169336975162">"Απόδοση με επιτάχυνση από υλικό εξοπλισμό"</string>
@@ -419,7 +419,7 @@
<string name="window_blurs" msgid="6831008984828425106">"Θάμπωμα σε επίπεδο παραθ."</string>
<string name="force_msaa" msgid="4081288296137775550">"Αναγκαστικά 4x MSAA"</string>
<string name="force_msaa_summary" msgid="9070437493586769500">"Ενεργοποίηση 4x MSAA σε εφαρμογές OpenGL ES 2.0"</string>
- <string name="show_non_rect_clip" msgid="7499758654867881817">"Εντοπισμός σφαλμάτων σε λειτουργίες μη ορθογώνιας περιοχής"</string>
+ <string name="show_non_rect_clip" msgid="7499758654867881817">"Αποσφαλμάτωση σε λειτουργίες μη ορθογώνιας περιοχής"</string>
<string name="track_frame_time" msgid="522674651937771106">"Απόδοση HWUI προφίλ"</string>
<string name="enable_gpu_debug_layers" msgid="4986675516188740397">"Ενεργ. επιπ. εντ. σφ. GPU"</string>
<string name="enable_gpu_debug_layers_summary" msgid="4921521407377170481">"Φόρτωση επιπ. εντοπ. σφ. GPU για εφαρμ. αντιμ. σφ."</string>
@@ -539,8 +539,7 @@
<string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Ελέγχεται από τη Ρύθμιση με περιορισμό"</string>
<string name="disabled_in_phone_call_text" msgid="6568931334337318320">"Μη διαθέσιμη κατά τη διάρκεια κλήσεων"</string>
<string name="disabled" msgid="8017887509554714950">"Απενεργοποιημένη"</string>
- <!-- no translation found for enabled (3997122818554810678) -->
- <skip />
+ <string name="enabled" msgid="3997122818554810678">"Ενεργό"</string>
<string name="external_source_trusted" msgid="1146522036773132905">"Επιτρέπεται"</string>
<string name="external_source_untrusted" msgid="5037891688911672227">"Δεν επιτρέπεται"</string>
<string name="install_other_apps" msgid="3232595082023199454">"Εγκατ. άγνωστων εφ."</string>
@@ -611,7 +610,7 @@
<string name="media_transfer_default_device_name" msgid="4315604017399871828">"Συνδεδεμένη συσκευή"</string>
<string name="media_transfer_this_phone" msgid="7194341457812151531">"Αυτό το τηλέφ."</string>
<string name="media_transfer_digital_line_name" msgid="312091711951124301">"S/PDIF"</string>
- <string name="media_transfer_analog_line_name" msgid="1841163866716302104">"Αναλογικός"</string>
+ <string name="media_transfer_analog_line_name" msgid="1841163866716302104">"Αναλογική έξοδος"</string>
<string name="media_transfer_aux_line_name" msgid="894135835967856689">"AUX"</string>
<string name="media_output_status_unknown_error" msgid="5098565887497902222">"Δεν είναι δυνατή η αναπαραγωγή σε αυτήν τη συσκευή"</string>
<string name="media_output_status_require_premium" msgid="8411255800047014822">"Αναβαθμίστε τον λογαριασμό για εναλλαγή"</string>
@@ -628,6 +627,7 @@
<string name="tv_media_transfer_internal_speakers" msgid="4662765121700213785">"Ενσωματωμένο ηχείο"</string>
<string name="tv_media_transfer_hdmi_title" msgid="6715658310934507444">"Ήχος τηλεόρασης"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Πρόβλημα κατά τη σύνδεση. Απενεργοποιήστε τη συσκευή και ενεργοποιήστε την ξανά"</string>
+ <string name="bluetooth_key_missing_subtext" msgid="1003639333895028298">"Δεν είναι δυνατή η σύνδεση"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Ενσύρματη συσκευή ήχου"</string>
<string name="help_label" msgid="3528360748637781274">"Βοήθεια και σχόλια"</string>
<string name="storage_category" msgid="2287342585424631813">"Αποθηκευτικός χώρος"</string>
diff --git a/packages/SettingsLib/res/values-en-rAU/strings.xml b/packages/SettingsLib/res/values-en-rAU/strings.xml
index 8c819ac742a1..109558e01da7 100644
--- a/packages/SettingsLib/res/values-en-rAU/strings.xml
+++ b/packages/SettingsLib/res/values-en-rAU/strings.xml
@@ -539,8 +539,7 @@
<string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Controlled by restricted setting"</string>
<string name="disabled_in_phone_call_text" msgid="6568931334337318320">"Unavailable during calls"</string>
<string name="disabled" msgid="8017887509554714950">"Disabled"</string>
- <!-- no translation found for enabled (3997122818554810678) -->
- <skip />
+ <string name="enabled" msgid="3997122818554810678">"Enabled"</string>
<string name="external_source_trusted" msgid="1146522036773132905">"Allowed"</string>
<string name="external_source_untrusted" msgid="5037891688911672227">"Not allowed"</string>
<string name="install_other_apps" msgid="3232595082023199454">"Install unknown apps"</string>
@@ -628,6 +627,7 @@
<string name="tv_media_transfer_internal_speakers" msgid="4662765121700213785">"Built-in speaker"</string>
<string name="tv_media_transfer_hdmi_title" msgid="6715658310934507444">"TV audio"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Problem connecting. Turn device off and back on"</string>
+ <string name="bluetooth_key_missing_subtext" msgid="1003639333895028298">"Can\'t connect"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Wired audio device"</string>
<string name="help_label" msgid="3528360748637781274">"Help and feedback"</string>
<string name="storage_category" msgid="2287342585424631813">"Storage"</string>
diff --git a/packages/SettingsLib/res/values-en-rCA/strings.xml b/packages/SettingsLib/res/values-en-rCA/strings.xml
index 2faf2feed627..002b1049cb13 100644
--- a/packages/SettingsLib/res/values-en-rCA/strings.xml
+++ b/packages/SettingsLib/res/values-en-rCA/strings.xml
@@ -627,6 +627,7 @@
<string name="tv_media_transfer_internal_speakers" msgid="4662765121700213785">"Built-in speaker"</string>
<string name="tv_media_transfer_hdmi_title" msgid="6715658310934507444">"TV Audio"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Problem connecting. Turn device off &amp; back on"</string>
+ <string name="bluetooth_key_missing_subtext" msgid="1003639333895028298">"Can’t connect"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Wired audio device"</string>
<string name="help_label" msgid="3528360748637781274">"Help and feedback"</string>
<string name="storage_category" msgid="2287342585424631813">"Storage"</string>
diff --git a/packages/SettingsLib/res/values-en-rGB/strings.xml b/packages/SettingsLib/res/values-en-rGB/strings.xml
index 8c819ac742a1..109558e01da7 100644
--- a/packages/SettingsLib/res/values-en-rGB/strings.xml
+++ b/packages/SettingsLib/res/values-en-rGB/strings.xml
@@ -539,8 +539,7 @@
<string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Controlled by restricted setting"</string>
<string name="disabled_in_phone_call_text" msgid="6568931334337318320">"Unavailable during calls"</string>
<string name="disabled" msgid="8017887509554714950">"Disabled"</string>
- <!-- no translation found for enabled (3997122818554810678) -->
- <skip />
+ <string name="enabled" msgid="3997122818554810678">"Enabled"</string>
<string name="external_source_trusted" msgid="1146522036773132905">"Allowed"</string>
<string name="external_source_untrusted" msgid="5037891688911672227">"Not allowed"</string>
<string name="install_other_apps" msgid="3232595082023199454">"Install unknown apps"</string>
@@ -628,6 +627,7 @@
<string name="tv_media_transfer_internal_speakers" msgid="4662765121700213785">"Built-in speaker"</string>
<string name="tv_media_transfer_hdmi_title" msgid="6715658310934507444">"TV audio"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Problem connecting. Turn device off and back on"</string>
+ <string name="bluetooth_key_missing_subtext" msgid="1003639333895028298">"Can\'t connect"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Wired audio device"</string>
<string name="help_label" msgid="3528360748637781274">"Help and feedback"</string>
<string name="storage_category" msgid="2287342585424631813">"Storage"</string>
diff --git a/packages/SettingsLib/res/values-en-rIN/strings.xml b/packages/SettingsLib/res/values-en-rIN/strings.xml
index 8c819ac742a1..109558e01da7 100644
--- a/packages/SettingsLib/res/values-en-rIN/strings.xml
+++ b/packages/SettingsLib/res/values-en-rIN/strings.xml
@@ -539,8 +539,7 @@
<string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Controlled by restricted setting"</string>
<string name="disabled_in_phone_call_text" msgid="6568931334337318320">"Unavailable during calls"</string>
<string name="disabled" msgid="8017887509554714950">"Disabled"</string>
- <!-- no translation found for enabled (3997122818554810678) -->
- <skip />
+ <string name="enabled" msgid="3997122818554810678">"Enabled"</string>
<string name="external_source_trusted" msgid="1146522036773132905">"Allowed"</string>
<string name="external_source_untrusted" msgid="5037891688911672227">"Not allowed"</string>
<string name="install_other_apps" msgid="3232595082023199454">"Install unknown apps"</string>
@@ -628,6 +627,7 @@
<string name="tv_media_transfer_internal_speakers" msgid="4662765121700213785">"Built-in speaker"</string>
<string name="tv_media_transfer_hdmi_title" msgid="6715658310934507444">"TV audio"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Problem connecting. Turn device off and back on"</string>
+ <string name="bluetooth_key_missing_subtext" msgid="1003639333895028298">"Can\'t connect"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Wired audio device"</string>
<string name="help_label" msgid="3528360748637781274">"Help and feedback"</string>
<string name="storage_category" msgid="2287342585424631813">"Storage"</string>
diff --git a/packages/SettingsLib/res/values-es-rUS/strings.xml b/packages/SettingsLib/res/values-es-rUS/strings.xml
index 0e83356e185f..216ae5757f3b 100644
--- a/packages/SettingsLib/res/values-es-rUS/strings.xml
+++ b/packages/SettingsLib/res/values-es-rUS/strings.xml
@@ -627,6 +627,7 @@
<string name="tv_media_transfer_internal_speakers" msgid="4662765121700213785">"Bocina integrada"</string>
<string name="tv_media_transfer_hdmi_title" msgid="6715658310934507444">"Audio de la TV"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Error al establecer la conexión. Apaga el dispositivo y vuelve a encenderlo."</string>
+ <string name="bluetooth_key_missing_subtext" msgid="1003639333895028298">"No se puede establecer la conexión"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Dispositivo de audio con cable"</string>
<string name="help_label" msgid="3528360748637781274">"Ayuda y comentarios"</string>
<string name="storage_category" msgid="2287342585424631813">"Almacenamiento"</string>
diff --git a/packages/SettingsLib/res/values-es/strings.xml b/packages/SettingsLib/res/values-es/strings.xml
index 66d81d27a807..6bbff4b7061a 100644
--- a/packages/SettingsLib/res/values-es/strings.xml
+++ b/packages/SettingsLib/res/values-es/strings.xml
@@ -539,8 +539,7 @@
<string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Controlado por ajustes restringidos"</string>
<string name="disabled_in_phone_call_text" msgid="6568931334337318320">"No disponible durante las llamadas"</string>
<string name="disabled" msgid="8017887509554714950">"Inhabilitada"</string>
- <!-- no translation found for enabled (3997122818554810678) -->
- <skip />
+ <string name="enabled" msgid="3997122818554810678">"Habilitado"</string>
<string name="external_source_trusted" msgid="1146522036773132905">"Autorizadas"</string>
<string name="external_source_untrusted" msgid="5037891688911672227">"No autorizadas"</string>
<string name="install_other_apps" msgid="3232595082023199454">"Instalar aplicaciones desconocidas"</string>
@@ -628,6 +627,7 @@
<string name="tv_media_transfer_internal_speakers" msgid="4662765121700213785">"Altavoz integrado"</string>
<string name="tv_media_transfer_hdmi_title" msgid="6715658310934507444">"Audio de la televisión"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"No se ha podido conectar; reinicia el dispositivo"</string>
+ <string name="bluetooth_key_missing_subtext" msgid="1003639333895028298">"No se puede conectar"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Dispositivo de audio con cable"</string>
<string name="help_label" msgid="3528360748637781274">"Ayuda y comentarios"</string>
<string name="storage_category" msgid="2287342585424631813">"Almacenamiento"</string>
diff --git a/packages/SettingsLib/res/values-et/strings.xml b/packages/SettingsLib/res/values-et/strings.xml
index 59e1e4b4bc95..19df69dc1121 100644
--- a/packages/SettingsLib/res/values-et/strings.xml
+++ b/packages/SettingsLib/res/values-et/strings.xml
@@ -539,8 +539,7 @@
<string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Haldavad piiranguga seaded"</string>
<string name="disabled_in_phone_call_text" msgid="6568931334337318320">"Pole kõnede ajal saadaval"</string>
<string name="disabled" msgid="8017887509554714950">"Keelatud"</string>
- <!-- no translation found for enabled (3997122818554810678) -->
- <skip />
+ <string name="enabled" msgid="3997122818554810678">"Lubatud"</string>
<string name="external_source_trusted" msgid="1146522036773132905">"Lubatud"</string>
<string name="external_source_untrusted" msgid="5037891688911672227">"Pole lubatud"</string>
<string name="install_other_apps" msgid="3232595082023199454">"Tundmatute rakenduste installimine"</string>
@@ -628,6 +627,7 @@
<string name="tv_media_transfer_internal_speakers" msgid="4662765121700213785">"Sisseehitatud kõlar"</string>
<string name="tv_media_transfer_hdmi_title" msgid="6715658310934507444">"Teleri heli"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Probleem ühendamisel. Lülitage seade välja ja uuesti sisse"</string>
+ <string name="bluetooth_key_missing_subtext" msgid="1003639333895028298">"Ei saa ühendada"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Juhtmega heliseade"</string>
<string name="help_label" msgid="3528360748637781274">"Abi ja tagasiside"</string>
<string name="storage_category" msgid="2287342585424631813">"Salvestusruum"</string>
diff --git a/packages/SettingsLib/res/values-eu/strings.xml b/packages/SettingsLib/res/values-eu/strings.xml
index 6fd7e24b8c8b..788febba580b 100644
--- a/packages/SettingsLib/res/values-eu/strings.xml
+++ b/packages/SettingsLib/res/values-eu/strings.xml
@@ -539,8 +539,7 @@
<string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Ezarpen mugatuak kontrolatzen du"</string>
<string name="disabled_in_phone_call_text" msgid="6568931334337318320">"Ez dago erabilgarri deiak egin bitartean"</string>
<string name="disabled" msgid="8017887509554714950">"Desgaituta"</string>
- <!-- no translation found for enabled (3997122818554810678) -->
- <skip />
+ <string name="enabled" msgid="3997122818554810678">"Gaituta"</string>
<string name="external_source_trusted" msgid="1146522036773132905">"Baimenduta"</string>
<string name="external_source_untrusted" msgid="5037891688911672227">"Baimendu gabe"</string>
<string name="install_other_apps" msgid="3232595082023199454">"Instalatu aplikazio ezezagunak"</string>
@@ -628,6 +627,7 @@
<string name="tv_media_transfer_internal_speakers" msgid="4662765121700213785">"Bozgorailu integratua"</string>
<string name="tv_media_transfer_hdmi_title" msgid="6715658310934507444">"Telebistako audioa"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Arazo bat izan da konektatzean. Itzali gailua eta pitz ezazu berriro."</string>
+ <string name="bluetooth_key_missing_subtext" msgid="1003639333895028298">"Ezin da konektatu"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Audio-gailu kableduna"</string>
<string name="help_label" msgid="3528360748637781274">"Laguntza eta iritziak"</string>
<string name="storage_category" msgid="2287342585424631813">"Biltegiratzea"</string>
diff --git a/packages/SettingsLib/res/values-fa/strings.xml b/packages/SettingsLib/res/values-fa/strings.xml
index 483e76ab4369..75cf19cacb35 100644
--- a/packages/SettingsLib/res/values-fa/strings.xml
+++ b/packages/SettingsLib/res/values-fa/strings.xml
@@ -627,6 +627,7 @@
<string name="tv_media_transfer_internal_speakers" msgid="4662765121700213785">"بلندگوی داخلی"</string>
<string name="tv_media_transfer_hdmi_title" msgid="6715658310934507444">"صدای تلویزیون"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"مشکل در اتصال. دستگاه را خاموش و دوباره روشن کنید"</string>
+ <string name="bluetooth_key_missing_subtext" msgid="1003639333895028298">"اتصال برقرار نشد"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"دستگاه صوتی سیمی"</string>
<string name="help_label" msgid="3528360748637781274">"راهنما و بازخورد"</string>
<string name="storage_category" msgid="2287342585424631813">"فضای ذخیره‌سازی"</string>
diff --git a/packages/SettingsLib/res/values-fi/strings.xml b/packages/SettingsLib/res/values-fi/strings.xml
index f6af16e74ecb..7854c6762fe1 100644
--- a/packages/SettingsLib/res/values-fi/strings.xml
+++ b/packages/SettingsLib/res/values-fi/strings.xml
@@ -539,8 +539,7 @@
<string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Rajoitettujen asetusten mukaisesti"</string>
<string name="disabled_in_phone_call_text" msgid="6568931334337318320">"Ei käytettävissä puhelujen aikana"</string>
<string name="disabled" msgid="8017887509554714950">"Pois päältä"</string>
- <!-- no translation found for enabled (3997122818554810678) -->
- <skip />
+ <string name="enabled" msgid="3997122818554810678">"Käytössä"</string>
<string name="external_source_trusted" msgid="1146522036773132905">"Sallittu"</string>
<string name="external_source_untrusted" msgid="5037891688911672227">"Ei sallittu"</string>
<string name="install_other_apps" msgid="3232595082023199454">"Asenna tuntemattomia sovelluksia"</string>
@@ -628,6 +627,7 @@
<string name="tv_media_transfer_internal_speakers" msgid="4662765121700213785">"Sisäänrakennettu kaiutin"</string>
<string name="tv_media_transfer_hdmi_title" msgid="6715658310934507444">"TV:n audio"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Yhteysvirhe. Sammuta laite ja käynnistä se uudelleen."</string>
+ <string name="bluetooth_key_missing_subtext" msgid="1003639333895028298">"Ei yhteyttä"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Langallinen äänilaite"</string>
<string name="help_label" msgid="3528360748637781274">"Ohjeet ja palaute"</string>
<string name="storage_category" msgid="2287342585424631813">"Tallennustila"</string>
diff --git a/packages/SettingsLib/res/values-fr-rCA/strings.xml b/packages/SettingsLib/res/values-fr-rCA/strings.xml
index a5b212b432cb..3f267e07ee9d 100644
--- a/packages/SettingsLib/res/values-fr-rCA/strings.xml
+++ b/packages/SettingsLib/res/values-fr-rCA/strings.xml
@@ -627,6 +627,7 @@
<string name="tv_media_transfer_internal_speakers" msgid="4662765121700213785">"Haut-parleur intégré"</string>
<string name="tv_media_transfer_hdmi_title" msgid="6715658310934507444">"Sortie audio du téléviseur"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Problème de connexion. Éteignez et rallumez l\'appareil"</string>
+ <string name="bluetooth_key_missing_subtext" msgid="1003639333895028298">"Connexion impossible"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Appareil audio à câble"</string>
<string name="help_label" msgid="3528360748637781274">"Aide et commentaires"</string>
<string name="storage_category" msgid="2287342585424631813">"Stockage"</string>
diff --git a/packages/SettingsLib/res/values-fr/strings.xml b/packages/SettingsLib/res/values-fr/strings.xml
index 4cc8a9a37632..33c18837fecc 100644
--- a/packages/SettingsLib/res/values-fr/strings.xml
+++ b/packages/SettingsLib/res/values-fr/strings.xml
@@ -539,8 +539,7 @@
<string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Contrôlé par les paramètres restreints"</string>
<string name="disabled_in_phone_call_text" msgid="6568931334337318320">"Indisponible pendant les appels"</string>
<string name="disabled" msgid="8017887509554714950">"Désactivée"</string>
- <!-- no translation found for enabled (3997122818554810678) -->
- <skip />
+ <string name="enabled" msgid="3997122818554810678">"Activé"</string>
<string name="external_source_trusted" msgid="1146522036773132905">"Autorisé"</string>
<string name="external_source_untrusted" msgid="5037891688911672227">"Non autorisé"</string>
<string name="install_other_apps" msgid="3232595082023199454">"Installation d\'applis inconnues"</string>
@@ -628,6 +627,7 @@
<string name="tv_media_transfer_internal_speakers" msgid="4662765121700213785">"Haut-parleur intégré"</string>
<string name="tv_media_transfer_hdmi_title" msgid="6715658310934507444">"Audio TV"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Problème de connexion. Éteignez l\'appareil, puis rallumez-le"</string>
+ <string name="bluetooth_key_missing_subtext" msgid="1003639333895028298">"Impossible de se connecter"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Appareil audio filaire"</string>
<string name="help_label" msgid="3528360748637781274">"Aide et commentaires"</string>
<string name="storage_category" msgid="2287342585424631813">"Stockage"</string>
diff --git a/packages/SettingsLib/res/values-gl/strings.xml b/packages/SettingsLib/res/values-gl/strings.xml
index 352b6082081a..8044fd857dd4 100644
--- a/packages/SettingsLib/res/values-gl/strings.xml
+++ b/packages/SettingsLib/res/values-gl/strings.xml
@@ -539,8 +539,7 @@
<string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Baixo o control de opcións restrinxidas"</string>
<string name="disabled_in_phone_call_text" msgid="6568931334337318320">"Non dispoñible durante as chamadas"</string>
<string name="disabled" msgid="8017887509554714950">"Desactivada"</string>
- <!-- no translation found for enabled (3997122818554810678) -->
- <skip />
+ <string name="enabled" msgid="3997122818554810678">"Opción activada"</string>
<string name="external_source_trusted" msgid="1146522036773132905">"Permiso concedido"</string>
<string name="external_source_untrusted" msgid="5037891688911672227">"Permiso non concedido"</string>
<string name="install_other_apps" msgid="3232595082023199454">"Instalar aplicacións descoñecidas"</string>
@@ -626,8 +625,9 @@
<string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"Conectado mediante ARC"</string>
<string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"Conectado mediante eARC"</string>
<string name="tv_media_transfer_internal_speakers" msgid="4662765121700213785">"Altofalante integrado"</string>
- <string name="tv_media_transfer_hdmi_title" msgid="6715658310934507444">"Audio da televisión"</string>
+ <string name="tv_media_transfer_hdmi_title" msgid="6715658310934507444">"Saída de audio da televisión"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Produciuse un problema coa conexión. Apaga e acende o dispositivo."</string>
+ <string name="bluetooth_key_missing_subtext" msgid="1003639333895028298">"Non se puido establecer a conexión"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Dispositivo de audio con cable"</string>
<string name="help_label" msgid="3528360748637781274">"Axuda e comentarios"</string>
<string name="storage_category" msgid="2287342585424631813">"Almacenamento"</string>
diff --git a/packages/SettingsLib/res/values-gu/strings.xml b/packages/SettingsLib/res/values-gu/strings.xml
index aa27f2a8a09b..c8491e053fc2 100644
--- a/packages/SettingsLib/res/values-gu/strings.xml
+++ b/packages/SettingsLib/res/values-gu/strings.xml
@@ -539,8 +539,7 @@
<string name="disabled_by_app_ops_text" msgid="8373595926549098012">"પ્રતિબંધિત સેટિંગ દ્વારા નિયંત્રિત"</string>
<string name="disabled_in_phone_call_text" msgid="6568931334337318320">"કૉલ દરમિયાન અનુપલબ્ધ"</string>
<string name="disabled" msgid="8017887509554714950">"બંધ કરી"</string>
- <!-- no translation found for enabled (3997122818554810678) -->
- <skip />
+ <string name="enabled" msgid="3997122818554810678">"ચાલુ છે"</string>
<string name="external_source_trusted" msgid="1146522036773132905">"મંજૂરી છે"</string>
<string name="external_source_untrusted" msgid="5037891688911672227">"મંજૂરી નથી"</string>
<string name="install_other_apps" msgid="3232595082023199454">"અજાણી ઍપ ઇન્સ્ટૉલ કરો"</string>
@@ -612,7 +611,7 @@
<string name="media_transfer_this_phone" msgid="7194341457812151531">"આ ફોન"</string>
<string name="media_transfer_digital_line_name" msgid="312091711951124301">"S/PDIF"</string>
<string name="media_transfer_analog_line_name" msgid="1841163866716302104">"એનાલોગ"</string>
- <string name="media_transfer_aux_line_name" msgid="894135835967856689">"ઑગ્ઝિલરી"</string>
+ <string name="media_transfer_aux_line_name" msgid="894135835967856689">"AUX"</string>
<string name="media_output_status_unknown_error" msgid="5098565887497902222">"આ ડિવાઇસ પર ચલાવી શકતા નથી"</string>
<string name="media_output_status_require_premium" msgid="8411255800047014822">"સ્વિચ કરવા માટે એકાઉન્ટ અપગ્રેડ કરો"</string>
<string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"ડાઉનલોડ કરેલું કન્ટેન્ટ અહીં ચલાવી શકતા નથી"</string>
@@ -628,6 +627,7 @@
<string name="tv_media_transfer_internal_speakers" msgid="4662765121700213785">"બિલ્ટ-ઇન સ્પીકર"</string>
<string name="tv_media_transfer_hdmi_title" msgid="6715658310934507444">"ટીવીનો ઑડિયો"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"કનેક્ટ કરવામાં સમસ્યા આવી રહી છે. ડિવાઇસને બંધ કરીને ફરી ચાલુ કરો"</string>
+ <string name="bluetooth_key_missing_subtext" msgid="1003639333895028298">"કનેક્ટ કરી શકતા નથી"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"વાયરવાળો ઑડિયો ડિવાઇસ"</string>
<string name="help_label" msgid="3528360748637781274">"સહાય અને પ્રતિસાદ"</string>
<string name="storage_category" msgid="2287342585424631813">"સ્ટોરેજ"</string>
diff --git a/packages/SettingsLib/res/values-hi/strings.xml b/packages/SettingsLib/res/values-hi/strings.xml
index 7c4d64f32cda..407075a6bd86 100644
--- a/packages/SettingsLib/res/values-hi/strings.xml
+++ b/packages/SettingsLib/res/values-hi/strings.xml
@@ -539,8 +539,7 @@
<string name="disabled_by_app_ops_text" msgid="8373595926549098012">"इसे पाबंदी मोड वाली सेटिंग से कंट्रोल किया जाता है"</string>
<string name="disabled_in_phone_call_text" msgid="6568931334337318320">"कॉल के दौरान उपलब्ध नहीं है"</string>
<string name="disabled" msgid="8017887509554714950">"बंद किया गया"</string>
- <!-- no translation found for enabled (3997122818554810678) -->
- <skip />
+ <string name="enabled" msgid="3997122818554810678">"चालू है"</string>
<string name="external_source_trusted" msgid="1146522036773132905">"अनुमति है"</string>
<string name="external_source_untrusted" msgid="5037891688911672227">"अनुमति नहीं है"</string>
<string name="install_other_apps" msgid="3232595082023199454">"अनजान ऐप्लिकेशन इंस्टॉल करने की अनुमति"</string>
@@ -612,7 +611,7 @@
<string name="media_transfer_this_phone" msgid="7194341457812151531">"यह फ़ोन"</string>
<string name="media_transfer_digital_line_name" msgid="312091711951124301">"S/PDIF"</string>
<string name="media_transfer_analog_line_name" msgid="1841163866716302104">"ऐनालॉग"</string>
- <string name="media_transfer_aux_line_name" msgid="894135835967856689">"ऑक्स"</string>
+ <string name="media_transfer_aux_line_name" msgid="894135835967856689">"सहायक डिवाइस"</string>
<string name="media_output_status_unknown_error" msgid="5098565887497902222">"इस डिवाइस पर मीडिया नहीं चलाया जा सकता"</string>
<string name="media_output_status_require_premium" msgid="8411255800047014822">"प्रीमियम खाते में स्विच करने के लिए, अपना खाता अपग्रेड करें"</string>
<string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"डाउनलोड किए गए वीडियो यहां नहीं चलाए जा सकते"</string>
@@ -628,6 +627,7 @@
<string name="tv_media_transfer_internal_speakers" msgid="4662765121700213785">"डिवाइस में पहले से मौजूद स्पीकर"</string>
<string name="tv_media_transfer_hdmi_title" msgid="6715658310934507444">"टीवी ऑडियो"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"कनेक्ट करने में समस्या हो रही है. डिवाइस को बंद करके चालू करें"</string>
+ <string name="bluetooth_key_missing_subtext" msgid="1003639333895028298">"कनेक्ट नहीं किया जा सकता"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"वायर वाला ऑडियो डिवाइस"</string>
<string name="help_label" msgid="3528360748637781274">"सहायता और सुझाव"</string>
<string name="storage_category" msgid="2287342585424631813">"डिवाइस का स्टोरेज"</string>
diff --git a/packages/SettingsLib/res/values-hr/strings.xml b/packages/SettingsLib/res/values-hr/strings.xml
index ba5650f1d7a5..380fbe8c0d06 100644
--- a/packages/SettingsLib/res/values-hr/strings.xml
+++ b/packages/SettingsLib/res/values-hr/strings.xml
@@ -539,8 +539,7 @@
<string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Kontrolira ograničena postavka"</string>
<string name="disabled_in_phone_call_text" msgid="6568931334337318320">"Nije dostupno tijekom poziva"</string>
<string name="disabled" msgid="8017887509554714950">"Onemogućeno"</string>
- <!-- no translation found for enabled (3997122818554810678) -->
- <skip />
+ <string name="enabled" msgid="3997122818554810678">"Omogućeno"</string>
<string name="external_source_trusted" msgid="1146522036773132905">"Dopušteno"</string>
<string name="external_source_untrusted" msgid="5037891688911672227">"Nije dopušteno"</string>
<string name="install_other_apps" msgid="3232595082023199454">"Instalacija nepoznatih aplikacija"</string>
@@ -628,6 +627,7 @@
<string name="tv_media_transfer_internal_speakers" msgid="4662765121700213785">"Ugrađeni zvučnik"</string>
<string name="tv_media_transfer_hdmi_title" msgid="6715658310934507444">"TV zvuk"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Problem s povezivanjem. Isključite i ponovo uključite uređaj"</string>
+ <string name="bluetooth_key_missing_subtext" msgid="1003639333895028298">"Povezivanje nije uspjelo"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Žičani audiouređaj"</string>
<string name="help_label" msgid="3528360748637781274">"Pomoć i povratne informacije"</string>
<string name="storage_category" msgid="2287342585424631813">"Pohrana"</string>
diff --git a/packages/SettingsLib/res/values-hu/strings.xml b/packages/SettingsLib/res/values-hu/strings.xml
index faeec8d6ed24..9d08e0531df4 100644
--- a/packages/SettingsLib/res/values-hu/strings.xml
+++ b/packages/SettingsLib/res/values-hu/strings.xml
@@ -539,8 +539,7 @@
<string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Korlátozott beállítás vezérli"</string>
<string name="disabled_in_phone_call_text" msgid="6568931334337318320">"Nem áll rendelkezésre hívások közben"</string>
<string name="disabled" msgid="8017887509554714950">"Letiltva"</string>
- <!-- no translation found for enabled (3997122818554810678) -->
- <skip />
+ <string name="enabled" msgid="3997122818554810678">"Engedélyezve"</string>
<string name="external_source_trusted" msgid="1146522036773132905">"Engedélyezett"</string>
<string name="external_source_untrusted" msgid="5037891688911672227">"Nem engedélyezett"</string>
<string name="install_other_apps" msgid="3232595082023199454">"Ismeretlen alkalmazások telepítése"</string>
@@ -628,6 +627,7 @@
<string name="tv_media_transfer_internal_speakers" msgid="4662765121700213785">"Beépített hangszóró"</string>
<string name="tv_media_transfer_hdmi_title" msgid="6715658310934507444">"Tévés hangkimenet"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Sikertelen csatlakozás. Kapcsolja ki az eszközt, majd kapcsolja be újra."</string>
+ <string name="bluetooth_key_missing_subtext" msgid="1003639333895028298">"Sikertelen csatlakozás"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Vezetékes audioeszköz"</string>
<string name="help_label" msgid="3528360748637781274">"Súgó és visszajelzés"</string>
<string name="storage_category" msgid="2287342585424631813">"Tárhely"</string>
diff --git a/packages/SettingsLib/res/values-hy/strings.xml b/packages/SettingsLib/res/values-hy/strings.xml
index eedd262af0f4..425cb07289e7 100644
--- a/packages/SettingsLib/res/values-hy/strings.xml
+++ b/packages/SettingsLib/res/values-hy/strings.xml
@@ -627,6 +627,7 @@
<string name="tv_media_transfer_internal_speakers" msgid="4662765121700213785">"Ներկառուցված բարձրախոս"</string>
<string name="tv_media_transfer_hdmi_title" msgid="6715658310934507444">"Հեռուստացույցի աուդիո"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Կապի խնդիր կա: Սարքն անջատեք և նորից միացրեք:"</string>
+ <string name="bluetooth_key_missing_subtext" msgid="1003639333895028298">"Չի հաջողվում միանալ"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Լարով աուդիո սարք"</string>
<string name="help_label" msgid="3528360748637781274">"Օգնություն և հետադարձ կապ"</string>
<string name="storage_category" msgid="2287342585424631813">"Տարածք"</string>
diff --git a/packages/SettingsLib/res/values-in/strings.xml b/packages/SettingsLib/res/values-in/strings.xml
index 7af3de0da4fe..99b3bdca4094 100644
--- a/packages/SettingsLib/res/values-in/strings.xml
+++ b/packages/SettingsLib/res/values-in/strings.xml
@@ -539,8 +539,7 @@
<string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Dikontrol oleh Setelan Terbatas"</string>
<string name="disabled_in_phone_call_text" msgid="6568931334337318320">"Tidak tersedia selama panggilan berlangsung"</string>
<string name="disabled" msgid="8017887509554714950">"Dinonaktifkan"</string>
- <!-- no translation found for enabled (3997122818554810678) -->
- <skip />
+ <string name="enabled" msgid="3997122818554810678">"Aktif"</string>
<string name="external_source_trusted" msgid="1146522036773132905">"Diizinkan"</string>
<string name="external_source_untrusted" msgid="5037891688911672227">"Tidak diizinkan"</string>
<string name="install_other_apps" msgid="3232595082023199454">"Instal aplikasi tidak dikenal"</string>
@@ -628,6 +627,7 @@
<string name="tv_media_transfer_internal_speakers" msgid="4662765121700213785">"Speaker bawaan"</string>
<string name="tv_media_transfer_hdmi_title" msgid="6715658310934507444">"Audio TV"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Ada masalah saat menghubungkan. Nonaktifkan perangkat &amp; aktifkan kembali"</string>
+ <string name="bluetooth_key_missing_subtext" msgid="1003639333895028298">"Tidak dapat terhubung"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Perangkat audio berkabel"</string>
<string name="help_label" msgid="3528360748637781274">"Bantuan &amp; masukan"</string>
<string name="storage_category" msgid="2287342585424631813">"Penyimpanan"</string>
diff --git a/packages/SettingsLib/res/values-is/strings.xml b/packages/SettingsLib/res/values-is/strings.xml
index e6ef20fcd908..b337f353c350 100644
--- a/packages/SettingsLib/res/values-is/strings.xml
+++ b/packages/SettingsLib/res/values-is/strings.xml
@@ -539,8 +539,7 @@
<string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Stýrt af takmarkaði stillingu"</string>
<string name="disabled_in_phone_call_text" msgid="6568931334337318320">"Ekki í boði á meðan á símtölum stendur"</string>
<string name="disabled" msgid="8017887509554714950">"Óvirkt"</string>
- <!-- no translation found for enabled (3997122818554810678) -->
- <skip />
+ <string name="enabled" msgid="3997122818554810678">"Kveikt"</string>
<string name="external_source_trusted" msgid="1146522036773132905">"Heimilað"</string>
<string name="external_source_untrusted" msgid="5037891688911672227">"Ekki heimilað"</string>
<string name="install_other_apps" msgid="3232595082023199454">"Setja upp óþekkt forrit"</string>
@@ -628,6 +627,7 @@
<string name="tv_media_transfer_internal_speakers" msgid="4662765121700213785">"Innbyggður hátalari"</string>
<string name="tv_media_transfer_hdmi_title" msgid="6715658310934507444">"Sjónvarpshljóð"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Vandamál í tengingu. Slökktu og kveiktu á tækinu"</string>
+ <string name="bluetooth_key_missing_subtext" msgid="1003639333895028298">"Ekki tókst að tengjast"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Snúrutengt hljómtæki"</string>
<string name="help_label" msgid="3528360748637781274">"Hjálp og ábendingar"</string>
<string name="storage_category" msgid="2287342585424631813">"Geymsla"</string>
diff --git a/packages/SettingsLib/res/values-it/strings.xml b/packages/SettingsLib/res/values-it/strings.xml
index 24e7a4cfe45d..8347e6a4063a 100644
--- a/packages/SettingsLib/res/values-it/strings.xml
+++ b/packages/SettingsLib/res/values-it/strings.xml
@@ -539,8 +539,7 @@
<string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Gestita tramite impostazioni con restrizioni"</string>
<string name="disabled_in_phone_call_text" msgid="6568931334337318320">"Non disponibile durante le chiamate"</string>
<string name="disabled" msgid="8017887509554714950">"Disattivato"</string>
- <!-- no translation found for enabled (3997122818554810678) -->
- <skip />
+ <string name="enabled" msgid="3997122818554810678">"Attivata"</string>
<string name="external_source_trusted" msgid="1146522036773132905">"Autorizzazione concessa"</string>
<string name="external_source_untrusted" msgid="5037891688911672227">"Autorizzazione non concessa"</string>
<string name="install_other_apps" msgid="3232595082023199454">"Installa app sconosciute"</string>
@@ -611,7 +610,7 @@
<string name="media_transfer_default_device_name" msgid="4315604017399871828">"Dispositivo connesso"</string>
<string name="media_transfer_this_phone" msgid="7194341457812151531">"Questo smartphone"</string>
<string name="media_transfer_digital_line_name" msgid="312091711951124301">"S/PDIF"</string>
- <string name="media_transfer_analog_line_name" msgid="1841163866716302104">"Analogico"</string>
+ <string name="media_transfer_analog_line_name" msgid="1841163866716302104">"Analogica"</string>
<string name="media_transfer_aux_line_name" msgid="894135835967856689">"AUX"</string>
<string name="media_output_status_unknown_error" msgid="5098565887497902222">"Impossibile riprodurre su questo dispositivo"</string>
<string name="media_output_status_require_premium" msgid="8411255800047014822">"Esegui l\'upgrade dell\'account per cambiare"</string>
@@ -628,6 +627,7 @@
<string name="tv_media_transfer_internal_speakers" msgid="4662765121700213785">"Altoparlante integrato"</string>
<string name="tv_media_transfer_hdmi_title" msgid="6715658310934507444">"Audio della TV"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Problema di connessione. Spegni e riaccendi il dispositivo"</string>
+ <string name="bluetooth_key_missing_subtext" msgid="1003639333895028298">"Impossibile connettersi"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Dispositivo audio cablato"</string>
<string name="help_label" msgid="3528360748637781274">"Guida e feedback"</string>
<string name="storage_category" msgid="2287342585424631813">"Archiviazione"</string>
diff --git a/packages/SettingsLib/res/values-iw/strings.xml b/packages/SettingsLib/res/values-iw/strings.xml
index 82d90430090b..0da5795be3c6 100644
--- a/packages/SettingsLib/res/values-iw/strings.xml
+++ b/packages/SettingsLib/res/values-iw/strings.xml
@@ -539,8 +539,7 @@
<string name="disabled_by_app_ops_text" msgid="8373595926549098012">"בשליטה של הגדרה מוגבלת"</string>
<string name="disabled_in_phone_call_text" msgid="6568931334337318320">"ההעדפה הזו לא זמינה במהלך שיחות"</string>
<string name="disabled" msgid="8017887509554714950">"מושבת"</string>
- <!-- no translation found for enabled (3997122818554810678) -->
- <skip />
+ <string name="enabled" msgid="3997122818554810678">"מופעלת"</string>
<string name="external_source_trusted" msgid="1146522036773132905">"מורשה"</string>
<string name="external_source_untrusted" msgid="5037891688911672227">"לא מורשה"</string>
<string name="install_other_apps" msgid="3232595082023199454">"התקנת אפליקציות לא מוכרות"</string>
@@ -611,7 +610,7 @@
<string name="media_transfer_default_device_name" msgid="4315604017399871828">"המכשיר המחובר"</string>
<string name="media_transfer_this_phone" msgid="7194341457812151531">"הטלפון הזה"</string>
<string name="media_transfer_digital_line_name" msgid="312091711951124301">"S/PDIF"</string>
- <string name="media_transfer_analog_line_name" msgid="1841163866716302104">"אנלוגי"</string>
+ <string name="media_transfer_analog_line_name" msgid="1841163866716302104">"חיבור אנלוגי"</string>
<string name="media_transfer_aux_line_name" msgid="894135835967856689">"AUX"</string>
<string name="media_output_status_unknown_error" msgid="5098565887497902222">"לא ניתן להפעיל במכשיר"</string>
<string name="media_output_status_require_premium" msgid="8411255800047014822">"יש לשדרג חשבון כדי לעבור"</string>
@@ -628,6 +627,8 @@
<string name="tv_media_transfer_internal_speakers" msgid="4662765121700213785">"רמקול מובנה"</string>
<string name="tv_media_transfer_hdmi_title" msgid="6715658310934507444">"אודיו בטלוויזיה"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"יש בעיה בחיבור. עליך לכבות את המכשיר ולהפעיל אותו מחדש"</string>
+ <!-- no translation found for bluetooth_key_missing_subtext (1003639333895028298) -->
+ <skip />
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"התקן אודיו חוטי"</string>
<string name="help_label" msgid="3528360748637781274">"עזרה ומשוב"</string>
<string name="storage_category" msgid="2287342585424631813">"אחסון"</string>
diff --git a/packages/SettingsLib/res/values-ja/strings.xml b/packages/SettingsLib/res/values-ja/strings.xml
index adf5b71a7ea3..748502610376 100644
--- a/packages/SettingsLib/res/values-ja/strings.xml
+++ b/packages/SettingsLib/res/values-ja/strings.xml
@@ -627,6 +627,7 @@
<string name="tv_media_transfer_internal_speakers" msgid="4662765121700213785">"内蔵スピーカー"</string>
<string name="tv_media_transfer_hdmi_title" msgid="6715658310934507444">"TV オーディオ"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"接続エラーです。デバイスを OFF にしてから ON に戻してください"</string>
+ <string name="bluetooth_key_missing_subtext" msgid="1003639333895028298">"接続できません"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"有線オーディオ デバイス"</string>
<string name="help_label" msgid="3528360748637781274">"ヘルプとフィードバック"</string>
<string name="storage_category" msgid="2287342585424631813">"ストレージ"</string>
diff --git a/packages/SettingsLib/res/values-ka/strings.xml b/packages/SettingsLib/res/values-ka/strings.xml
index e9368aa05904..fe7e65c6f5f0 100644
--- a/packages/SettingsLib/res/values-ka/strings.xml
+++ b/packages/SettingsLib/res/values-ka/strings.xml
@@ -627,6 +627,7 @@
<string name="tv_media_transfer_internal_speakers" msgid="4662765121700213785">"ჩაშენებული დინამიკი"</string>
<string name="tv_media_transfer_hdmi_title" msgid="6715658310934507444">"TV Audio"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"დაკავშირებისას წარმოიქმნა პრობლემა. გამორთეთ და კვლავ ჩართეთ მოწყობილობა"</string>
+ <string name="bluetooth_key_missing_subtext" msgid="1003639333895028298">"დაკავშირება შეუძლებელია"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"სადენიანი აუდიო მოწყობილობა"</string>
<string name="help_label" msgid="3528360748637781274">"დახმარება და გამოხმაურება"</string>
<string name="storage_category" msgid="2287342585424631813">"საცავი"</string>
diff --git a/packages/SettingsLib/res/values-kk/strings.xml b/packages/SettingsLib/res/values-kk/strings.xml
index 16b7c22b6bfe..65ad80ab18b7 100644
--- a/packages/SettingsLib/res/values-kk/strings.xml
+++ b/packages/SettingsLib/res/values-kk/strings.xml
@@ -539,8 +539,7 @@
<string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Шектелген параметрлер арқылы басқарылады."</string>
<string name="disabled_in_phone_call_text" msgid="6568931334337318320">"Қоңырау шалу кезінде қолжетімді емес."</string>
<string name="disabled" msgid="8017887509554714950">"Өшірілген"</string>
- <!-- no translation found for enabled (3997122818554810678) -->
- <skip />
+ <string name="enabled" msgid="3997122818554810678">"Қосулы"</string>
<string name="external_source_trusted" msgid="1146522036773132905">"Рұқсат берілген"</string>
<string name="external_source_untrusted" msgid="5037891688911672227">"Рұқсат етілмеген"</string>
<string name="install_other_apps" msgid="3232595082023199454">"Белгісіз қолданбаларды орнату"</string>
@@ -628,6 +627,8 @@
<string name="tv_media_transfer_internal_speakers" msgid="4662765121700213785">"Ендірілген динамик"</string>
<string name="tv_media_transfer_hdmi_title" msgid="6715658310934507444">"Теледидардың аудио шығысы"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Байланыс орнату қатесі шығуып жатыр. Құрылғыны өшіріп, қайта қосыңыз."</string>
+ <!-- no translation found for bluetooth_key_missing_subtext (1003639333895028298) -->
+ <skip />
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Сымды аудио құрылғысы"</string>
<string name="help_label" msgid="3528360748637781274">"Анықтама және пікір"</string>
<string name="storage_category" msgid="2287342585424631813">"Жад"</string>
diff --git a/packages/SettingsLib/res/values-km/strings.xml b/packages/SettingsLib/res/values-km/strings.xml
index b183dc926c2d..6053ff70512d 100644
--- a/packages/SettingsLib/res/values-km/strings.xml
+++ b/packages/SettingsLib/res/values-km/strings.xml
@@ -624,9 +624,10 @@
<string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string>
<string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"បានភ្ជាប់តាមរយៈ ARC"</string>
<string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"បានភ្ជាប់តាមរយៈ eARC"</string>
- <string name="tv_media_transfer_internal_speakers" msgid="4662765121700213785">"ឧបករណ៍បំពងសំឡេង​ភ្ជាប់មកជាមួយ"</string>
+ <string name="tv_media_transfer_internal_speakers" msgid="4662765121700213785">"ឧបករណ៍សំឡេងមកជាមួយ"</string>
<string name="tv_media_transfer_hdmi_title" msgid="6715658310934507444">"សំឡេងទូរទស្សន៍"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"មាន​បញ្ហា​ក្នុងការ​ភ្ជាប់។ បិទ រួច​បើក​ឧបករណ៍​វិញ"</string>
+ <string name="bluetooth_key_missing_subtext" msgid="1003639333895028298">"មិនអាចភ្ជាប់​បានទេ"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"ឧបករណ៍​សំឡេងប្រើខ្សែ"</string>
<string name="help_label" msgid="3528360748637781274">"ជំនួយ និងមតិកែលម្អ"</string>
<string name="storage_category" msgid="2287342585424631813">"ទំហំផ្ទុក"</string>
diff --git a/packages/SettingsLib/res/values-kn/strings.xml b/packages/SettingsLib/res/values-kn/strings.xml
index a4e01d96761a..61b235e805ea 100644
--- a/packages/SettingsLib/res/values-kn/strings.xml
+++ b/packages/SettingsLib/res/values-kn/strings.xml
@@ -627,6 +627,7 @@
<string name="tv_media_transfer_internal_speakers" msgid="4662765121700213785">"ಅಂತರ್ ನಿರ್ಮಿತ ಧ್ವನಿ ವರ್ಧಕ"</string>
<string name="tv_media_transfer_hdmi_title" msgid="6715658310934507444">"TV ಆಡಿಯೊ"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"ಕನೆಕ್ಟ್ ಮಾಡುವಾಗ ಸಮಸ್ಯೆ ಎದುರಾಗಿದೆ ಸಾಧನವನ್ನು ಆಫ್ ಮಾಡಿ ಹಾಗೂ ನಂತರ ಪುನಃ ಆನ್ ಮಾಡಿ"</string>
+ <string name="bluetooth_key_missing_subtext" msgid="1003639333895028298">"ಕನೆಕ್ಟ್ ಮಾಡಲು ಸಾಧ್ಯವಿಲ್ಲ"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"ವೈರ್ ಹೊಂದಿರುವ ಆಡಿಯೋ ಸಾಧನ"</string>
<string name="help_label" msgid="3528360748637781274">"ಸಹಾಯ ಮತ್ತು ಪ್ರತಿಕ್ರಿಯೆ"</string>
<string name="storage_category" msgid="2287342585424631813">"ಸಂಗ್ರಹಣೆ"</string>
diff --git a/packages/SettingsLib/res/values-ko/strings.xml b/packages/SettingsLib/res/values-ko/strings.xml
index 0488543f7790..d0a583bd8ede 100644
--- a/packages/SettingsLib/res/values-ko/strings.xml
+++ b/packages/SettingsLib/res/values-ko/strings.xml
@@ -539,8 +539,7 @@
<string name="disabled_by_app_ops_text" msgid="8373595926549098012">"제한된 설정으로 제어됨"</string>
<string name="disabled_in_phone_call_text" msgid="6568931334337318320">"통화 중에는 사용할 수 없습니다."</string>
<string name="disabled" msgid="8017887509554714950">"사용 안함"</string>
- <!-- no translation found for enabled (3997122818554810678) -->
- <skip />
+ <string name="enabled" msgid="3997122818554810678">"사용 설정됨"</string>
<string name="external_source_trusted" msgid="1146522036773132905">"허용됨"</string>
<string name="external_source_untrusted" msgid="5037891688911672227">"허용되지 않음"</string>
<string name="install_other_apps" msgid="3232595082023199454">"알 수 없는 앱 설치"</string>
@@ -628,6 +627,7 @@
<string name="tv_media_transfer_internal_speakers" msgid="4662765121700213785">"내장 스피커"</string>
<string name="tv_media_transfer_hdmi_title" msgid="6715658310934507444">"TV 오디오"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"연결 중에 문제가 발생했습니다. 기기를 껐다가 다시 켜 보세요."</string>
+ <string name="bluetooth_key_missing_subtext" msgid="1003639333895028298">"연결할 수 없습니다."</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"유선 오디오 기기"</string>
<string name="help_label" msgid="3528360748637781274">"고객센터"</string>
<string name="storage_category" msgid="2287342585424631813">"저장용량"</string>
diff --git a/packages/SettingsLib/res/values-ky/strings.xml b/packages/SettingsLib/res/values-ky/strings.xml
index b04883747ee7..c68fb70ff3bd 100644
--- a/packages/SettingsLib/res/values-ky/strings.xml
+++ b/packages/SettingsLib/res/values-ky/strings.xml
@@ -539,8 +539,7 @@
<string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Чектелген параметр аркылуу көзөмөлдөнөт"</string>
<string name="disabled_in_phone_call_text" msgid="6568931334337318320">"Сүйлөшүп жаткан учурда жеткиликсиз"</string>
<string name="disabled" msgid="8017887509554714950">"Өчүрүлгөн"</string>
- <!-- no translation found for enabled (3997122818554810678) -->
- <skip />
+ <string name="enabled" msgid="3997122818554810678">"Иштетилди"</string>
<string name="external_source_trusted" msgid="1146522036773132905">"Уруксат берилген"</string>
<string name="external_source_untrusted" msgid="5037891688911672227">"Тыюу салынган"</string>
<string name="install_other_apps" msgid="3232595082023199454">"Белгисиз колдонмолорду орнотуу"</string>
@@ -612,7 +611,7 @@
<string name="media_transfer_this_phone" msgid="7194341457812151531">"Ушул телефон"</string>
<string name="media_transfer_digital_line_name" msgid="312091711951124301">"S/PDIF"</string>
<string name="media_transfer_analog_line_name" msgid="1841163866716302104">"Аналог"</string>
- <string name="media_transfer_aux_line_name" msgid="894135835967856689">"КШМЧ"</string>
+ <string name="media_transfer_aux_line_name" msgid="894135835967856689">"AUX"</string>
<string name="media_output_status_unknown_error" msgid="5098565887497902222">"Бул түзмөктө ойнотууга болбойт"</string>
<string name="media_output_status_require_premium" msgid="8411255800047014822">"Премиум аккаунтка которулуу керек"</string>
<string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"Жүктөлүп алынгандар ойнотулбайт"</string>
@@ -628,6 +627,7 @@
<string name="tv_media_transfer_internal_speakers" msgid="4662765121700213785">"Алдын ала орнотулган динамик"</string>
<string name="tv_media_transfer_hdmi_title" msgid="6715658310934507444">"Сыналгы аудиосу"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Туташууда маселе келип чыкты. Түзмөктү өчүрүп, кайра күйгүзүп көрүңүз"</string>
+ <string name="bluetooth_key_missing_subtext" msgid="1003639333895028298">"Туташпай жатат"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Зымдуу аудио түзмөк"</string>
<string name="help_label" msgid="3528360748637781274">"Жардам/Пикир билдирүү"</string>
<string name="storage_category" msgid="2287342585424631813">"Сактагыч"</string>
diff --git a/packages/SettingsLib/res/values-lo/strings.xml b/packages/SettingsLib/res/values-lo/strings.xml
index ebba474c243e..c4e25f8eb9fb 100644
--- a/packages/SettingsLib/res/values-lo/strings.xml
+++ b/packages/SettingsLib/res/values-lo/strings.xml
@@ -627,6 +627,7 @@
<string name="tv_media_transfer_internal_speakers" msgid="4662765121700213785">"ລຳໂພງທີ່ມີໃນຕົວ"</string>
<string name="tv_media_transfer_hdmi_title" msgid="6715658310934507444">"ສຽງນີ້"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"ເກີດບັນຫາໃນການເຊື່ອມຕໍ່. ປິດອຸປະກອນແລ້ວເປີດກັບຄືນມາໃໝ່"</string>
+ <string name="bluetooth_key_missing_subtext" msgid="1003639333895028298">"ບໍ່ສາມາດເຊື່ອມຕໍ່ໄດ້"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"ອຸປະກອນສຽງແບບມີສາຍ"</string>
<string name="help_label" msgid="3528360748637781274">"ຊ່ວຍເຫຼືອ ແລະ ຕິຊົມ"</string>
<string name="storage_category" msgid="2287342585424631813">"ບ່ອນເກັບຂໍ້ມູນ"</string>
diff --git a/packages/SettingsLib/res/values-lt/strings.xml b/packages/SettingsLib/res/values-lt/strings.xml
index 058ca607a078..0379d5c636ea 100644
--- a/packages/SettingsLib/res/values-lt/strings.xml
+++ b/packages/SettingsLib/res/values-lt/strings.xml
@@ -627,6 +627,7 @@
<string name="tv_media_transfer_internal_speakers" msgid="4662765121700213785">"Įtaisytas garsiakalbis"</string>
<string name="tv_media_transfer_hdmi_title" msgid="6715658310934507444">"TV garso įrašas"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Prisijungiant kilo problema. Išjunkite įrenginį ir vėl jį įjunkite"</string>
+ <string name="bluetooth_key_missing_subtext" msgid="1003639333895028298">"Nepavyksta prisijungti"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Laidinis garso įrenginys"</string>
<string name="help_label" msgid="3528360748637781274">"Pagalba ir atsiliepimai"</string>
<string name="storage_category" msgid="2287342585424631813">"Saugykla"</string>
diff --git a/packages/SettingsLib/res/values-lv/strings.xml b/packages/SettingsLib/res/values-lv/strings.xml
index cdca19b8e0c8..ab9dfd54d6ab 100644
--- a/packages/SettingsLib/res/values-lv/strings.xml
+++ b/packages/SettingsLib/res/values-lv/strings.xml
@@ -539,8 +539,7 @@
<string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Kontrolē ierobežots iestatījums"</string>
<string name="disabled_in_phone_call_text" msgid="6568931334337318320">"Ierīce nav pieejama zvanu laikā"</string>
<string name="disabled" msgid="8017887509554714950">"Atspējots"</string>
- <!-- no translation found for enabled (3997122818554810678) -->
- <skip />
+ <string name="enabled" msgid="3997122818554810678">"Iespējots"</string>
<string name="external_source_trusted" msgid="1146522036773132905">"Atļauts"</string>
<string name="external_source_untrusted" msgid="5037891688911672227">"Nav atļauts"</string>
<string name="install_other_apps" msgid="3232595082023199454">"Nezināmu lietotņu instalēšana"</string>
@@ -628,6 +627,8 @@
<string name="tv_media_transfer_internal_speakers" msgid="4662765121700213785">"Iebūvēts skaļrunis"</string>
<string name="tv_media_transfer_hdmi_title" msgid="6715658310934507444">"Televizora audio"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Radās problēma ar savienojuma izveidi. Izslēdziet un atkal ieslēdziet ierīci."</string>
+ <!-- no translation found for bluetooth_key_missing_subtext (1003639333895028298) -->
+ <skip />
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Vadu audioierīce"</string>
<string name="help_label" msgid="3528360748637781274">"Palīdzība un atsauksmes"</string>
<string name="storage_category" msgid="2287342585424631813">"Krātuve"</string>
diff --git a/packages/SettingsLib/res/values-mk/strings.xml b/packages/SettingsLib/res/values-mk/strings.xml
index e51ca04022a7..a1812490c94f 100644
--- a/packages/SettingsLib/res/values-mk/strings.xml
+++ b/packages/SettingsLib/res/values-mk/strings.xml
@@ -539,8 +539,7 @@
<string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Контролирано со ограничени поставки"</string>
<string name="disabled_in_phone_call_text" msgid="6568931334337318320">"Недостапно при повици"</string>
<string name="disabled" msgid="8017887509554714950">"Оневозможено"</string>
- <!-- no translation found for enabled (3997122818554810678) -->
- <skip />
+ <string name="enabled" msgid="3997122818554810678">"Овозможено"</string>
<string name="external_source_trusted" msgid="1146522036773132905">"Со дозвола"</string>
<string name="external_source_untrusted" msgid="5037891688911672227">"Без дозвола"</string>
<string name="install_other_apps" msgid="3232595082023199454">"Инсталирање непознати апликации"</string>
@@ -628,6 +627,7 @@
<string name="tv_media_transfer_internal_speakers" msgid="4662765121700213785">"Вграден звучник"</string>
<string name="tv_media_transfer_hdmi_title" msgid="6715658310934507444">"Аудио на телевизор"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Проблем со поврзување. Исклучете го уредот и повторно вклучете го"</string>
+ <string name="bluetooth_key_missing_subtext" msgid="1003639333895028298">"Не може да се поврзе"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Жичен аудиоуред"</string>
<string name="help_label" msgid="3528360748637781274">"Помош и повратни информации"</string>
<string name="storage_category" msgid="2287342585424631813">"Простор"</string>
diff --git a/packages/SettingsLib/res/values-ml/strings.xml b/packages/SettingsLib/res/values-ml/strings.xml
index 54b6ff8ab524..b1d1d4fc2c02 100644
--- a/packages/SettingsLib/res/values-ml/strings.xml
+++ b/packages/SettingsLib/res/values-ml/strings.xml
@@ -539,8 +539,7 @@
<string name="disabled_by_app_ops_text" msgid="8373595926549098012">"നിയന്ത്രിത ക്രമീകരണം ഉപയോഗിച്ച് നിയന്ത്രിക്കുന്നത്"</string>
<string name="disabled_in_phone_call_text" msgid="6568931334337318320">"കോളുകൾ ചെയ്യുമ്പോൾ ലഭ്യമല്ല"</string>
<string name="disabled" msgid="8017887509554714950">"പ്രവർത്തനരഹിതമാക്കി"</string>
- <!-- no translation found for enabled (3997122818554810678) -->
- <skip />
+ <string name="enabled" msgid="3997122818554810678">"പ്രവർത്തനക്ഷമമാക്കി"</string>
<string name="external_source_trusted" msgid="1146522036773132905">"അനുവദനീയം"</string>
<string name="external_source_untrusted" msgid="5037891688911672227">"അനുവദിച്ചിട്ടില്ല"</string>
<string name="install_other_apps" msgid="3232595082023199454">"പരിചയമില്ലാത്ത ആപ്പുകൾ ഇൻസ്റ്റാൾ ചെയ്യുക"</string>
@@ -628,6 +627,7 @@
<string name="tv_media_transfer_internal_speakers" msgid="4662765121700213785">"ബിൽട്ട്-ഇൻ സ്പീക്കർ"</string>
<string name="tv_media_transfer_hdmi_title" msgid="6715658310934507444">"ടിവി ഓഡിയോ"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"കണക്‌റ്റ് ചെയ്യുന്നതിൽ പ്രശ്‌നമുണ്ടായി. ഉപകരണം ഓഫാക്കി വീണ്ടും ഓണാക്കുക"</string>
+ <string name="bluetooth_key_missing_subtext" msgid="1003639333895028298">"കണക്റ്റ് ചെയ്യാനാകുന്നില്ല"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"വയർ മുഖേന ബന്ധിപ്പിച്ച ഓഡിയോ ഉപകരണം"</string>
<string name="help_label" msgid="3528360748637781274">"സഹായവും ഫീഡ്‌ബാക്കും"</string>
<string name="storage_category" msgid="2287342585424631813">"സ്റ്റോറേജ്"</string>
diff --git a/packages/SettingsLib/res/values-mn/strings.xml b/packages/SettingsLib/res/values-mn/strings.xml
index ed42362e5c1e..9b128bd55bfe 100644
--- a/packages/SettingsLib/res/values-mn/strings.xml
+++ b/packages/SettingsLib/res/values-mn/strings.xml
@@ -539,8 +539,7 @@
<string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Хязгаарлагдсан тохиргоогоор хянадаг"</string>
<string name="disabled_in_phone_call_text" msgid="6568931334337318320">"Дуудлагын үер боломжгүй"</string>
<string name="disabled" msgid="8017887509554714950">"Идэвхгүйжүүлсэн"</string>
- <!-- no translation found for enabled (3997122818554810678) -->
- <skip />
+ <string name="enabled" msgid="3997122818554810678">"Идэвхжүүлсэн"</string>
<string name="external_source_trusted" msgid="1146522036773132905">"Зөвшөөрсөн"</string>
<string name="external_source_untrusted" msgid="5037891688911672227">"Зөвшөөрөөгүй"</string>
<string name="install_other_apps" msgid="3232595082023199454">"Тодорхойгүй апп суулгах"</string>
@@ -628,6 +627,7 @@
<string name="tv_media_transfer_internal_speakers" msgid="4662765121700213785">"Бүрэлдэхүүн чанга яригч"</string>
<string name="tv_media_transfer_hdmi_title" msgid="6715658310934507444">"ТВ-ийн аудио"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Холбогдоход асуудал гарлаа. Төхөөрөмжийг унтраагаад дахин асаана уу"</string>
+ <string name="bluetooth_key_missing_subtext" msgid="1003639333895028298">"Холбогдох боломжгүй байна"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Утастай аудио төхөөрөмж"</string>
<string name="help_label" msgid="3528360748637781274">"Тусламж, санал хүсэлт"</string>
<string name="storage_category" msgid="2287342585424631813">"Хадгалах сан"</string>
diff --git a/packages/SettingsLib/res/values-mr/strings.xml b/packages/SettingsLib/res/values-mr/strings.xml
index 6e7cd02b1e61..7c8c6aac2296 100644
--- a/packages/SettingsLib/res/values-mr/strings.xml
+++ b/packages/SettingsLib/res/values-mr/strings.xml
@@ -539,8 +539,7 @@
<string name="disabled_by_app_ops_text" msgid="8373595926549098012">"प्रतिबंधित केलेल्या सेटिंग द्वारे नियंत्रित"</string>
<string name="disabled_in_phone_call_text" msgid="6568931334337318320">"कॉल दरम्‍यान उपलब्ध नाही"</string>
<string name="disabled" msgid="8017887509554714950">"अक्षम"</string>
- <!-- no translation found for enabled (3997122818554810678) -->
- <skip />
+ <string name="enabled" msgid="3997122818554810678">"सुरू केले आहे"</string>
<string name="external_source_trusted" msgid="1146522036773132905">"अनुमती आहे"</string>
<string name="external_source_untrusted" msgid="5037891688911672227">"अनुमती नाही"</string>
<string name="install_other_apps" msgid="3232595082023199454">"अज्ञात अ‍ॅप्स इंस्टॉल करा"</string>
@@ -628,6 +627,7 @@
<string name="tv_media_transfer_internal_speakers" msgid="4662765121700213785">"बिल्ट-इन स्पीकर"</string>
<string name="tv_media_transfer_hdmi_title" msgid="6715658310934507444">"टीव्ही ऑडिओ"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"कनेक्‍ट करण्‍यात समस्‍या आली. डिव्हाइस बंद करा आणि नंतर सुरू करा"</string>
+ <string name="bluetooth_key_missing_subtext" msgid="1003639333895028298">"कनेक्ट करू शकत नाही"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"वायर असलेले ऑडिओ डिव्हाइस"</string>
<string name="help_label" msgid="3528360748637781274">"मदत आणि फीडबॅक"</string>
<string name="storage_category" msgid="2287342585424631813">"स्टोरेज"</string>
diff --git a/packages/SettingsLib/res/values-ms/strings.xml b/packages/SettingsLib/res/values-ms/strings.xml
index 384635a97ef8..52d1d5ac849d 100644
--- a/packages/SettingsLib/res/values-ms/strings.xml
+++ b/packages/SettingsLib/res/values-ms/strings.xml
@@ -627,6 +627,7 @@
<string name="tv_media_transfer_internal_speakers" msgid="4662765121700213785">"Pembesar suara terbina dalam"</string>
<string name="tv_media_transfer_hdmi_title" msgid="6715658310934507444">"Audio TV"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Masalah penyambungan. Matikan &amp; hidupkan kembali peranti"</string>
+ <string name="bluetooth_key_missing_subtext" msgid="1003639333895028298">"Tidak dapat disambungkan"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Peranti audio berwayar"</string>
<string name="help_label" msgid="3528360748637781274">"Bantuan &amp; maklum balas"</string>
<string name="storage_category" msgid="2287342585424631813">"Storan"</string>
diff --git a/packages/SettingsLib/res/values-my/strings.xml b/packages/SettingsLib/res/values-my/strings.xml
index fc26db0df2e0..e3d1005c24eb 100644
--- a/packages/SettingsLib/res/values-my/strings.xml
+++ b/packages/SettingsLib/res/values-my/strings.xml
@@ -624,9 +624,10 @@
<string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string>
<string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"ARC မှတစ်ဆင့် ချိတ်ဆက်ထားသည်"</string>
<string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"eARC မှတစ်ဆင့် ချိတ်ဆက်ထားသည်"</string>
- <string name="tv_media_transfer_internal_speakers" msgid="4662765121700213785">"မူလပါရှိသည့် စပီကာ"</string>
+ <string name="tv_media_transfer_internal_speakers" msgid="4662765121700213785">"အသင့်ပါ စပီကာ"</string>
<string name="tv_media_transfer_hdmi_title" msgid="6715658310934507444">"TV အသံ"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"ချိတ်ဆက်ရာတွင် ပြဿနာရှိပါသည်။ စက်ကိုပိတ်ပြီး ပြန်ဖွင့်ပါ"</string>
+ <string name="bluetooth_key_missing_subtext" msgid="1003639333895028298">"ချိတ်ဆက်၍မရပါ"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"ကြိုးတပ် အသံစက်ပစ္စည်း"</string>
<string name="help_label" msgid="3528360748637781274">"အကူအညီနှင့် အကြံပြုချက်"</string>
<string name="storage_category" msgid="2287342585424631813">"သိုလှောင်ခန်း"</string>
diff --git a/packages/SettingsLib/res/values-nb/strings.xml b/packages/SettingsLib/res/values-nb/strings.xml
index c8fc415528b4..9b2fbd8184c0 100644
--- a/packages/SettingsLib/res/values-nb/strings.xml
+++ b/packages/SettingsLib/res/values-nb/strings.xml
@@ -539,8 +539,7 @@
<string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Kontrollert av en begrenset innstilling"</string>
<string name="disabled_in_phone_call_text" msgid="6568931334337318320">"Utilgjengelig under samtaler"</string>
<string name="disabled" msgid="8017887509554714950">"Deaktivert"</string>
- <!-- no translation found for enabled (3997122818554810678) -->
- <skip />
+ <string name="enabled" msgid="3997122818554810678">"Slått på"</string>
<string name="external_source_trusted" msgid="1146522036773132905">"Tillatt"</string>
<string name="external_source_untrusted" msgid="5037891688911672227">"Ikke tillatt"</string>
<string name="install_other_apps" msgid="3232595082023199454">"Installer ukjente apper"</string>
@@ -628,6 +627,7 @@
<string name="tv_media_transfer_internal_speakers" msgid="4662765121700213785">"Innebygd høyttaler"</string>
<string name="tv_media_transfer_hdmi_title" msgid="6715658310934507444">"TV-lyd"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Tilkoblingsproblemer. Slå enheten av og på igjen"</string>
+ <string name="bluetooth_key_missing_subtext" msgid="1003639333895028298">"Kan ikke koble til"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Lydenhet med kabel"</string>
<string name="help_label" msgid="3528360748637781274">"Hjelp og tilbakemelding"</string>
<string name="storage_category" msgid="2287342585424631813">"Lagring"</string>
diff --git a/packages/SettingsLib/res/values-ne/strings.xml b/packages/SettingsLib/res/values-ne/strings.xml
index 5f09635e19c7..005f0cd3738d 100644
--- a/packages/SettingsLib/res/values-ne/strings.xml
+++ b/packages/SettingsLib/res/values-ne/strings.xml
@@ -539,8 +539,7 @@
<string name="disabled_by_app_ops_text" msgid="8373595926549098012">"प्रतिबन्धित सेटिङले नियन्त्रण गरेको"</string>
<string name="disabled_in_phone_call_text" msgid="6568931334337318320">"कल चलिरहेका बेला उपलब्ध छैन"</string>
<string name="disabled" msgid="8017887509554714950">"असक्षम पारियो"</string>
- <!-- no translation found for enabled (3997122818554810678) -->
- <skip />
+ <string name="enabled" msgid="3997122818554810678">"अन गरियो"</string>
<string name="external_source_trusted" msgid="1146522036773132905">"अनुमति छ"</string>
<string name="external_source_untrusted" msgid="5037891688911672227">"अनुमति छैन"</string>
<string name="install_other_apps" msgid="3232595082023199454">"अज्ञात एप इन्स्टल गर्ने अनुमति"</string>
@@ -628,6 +627,7 @@
<string name="tv_media_transfer_internal_speakers" msgid="4662765121700213785">"अन्तर्निर्मित स्पिकर"</string>
<string name="tv_media_transfer_hdmi_title" msgid="6715658310934507444">"टिभीको अडियो"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"जोड्ने क्रममा समस्या भयो। यन्त्रलाई निष्क्रिय पारेर फेरि अन गर्नुहोस्"</string>
+ <string name="bluetooth_key_missing_subtext" msgid="1003639333895028298">"कनेक्ट गर्न सकिएन"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"तारयुक्त अडियो यन्त्र"</string>
<string name="help_label" msgid="3528360748637781274">"मद्दत र प्रतिक्रिया"</string>
<string name="storage_category" msgid="2287342585424631813">"भण्डारण"</string>
diff --git a/packages/SettingsLib/res/values-nl/strings.xml b/packages/SettingsLib/res/values-nl/strings.xml
index 78dfdcead634..ffde98a5f303 100644
--- a/packages/SettingsLib/res/values-nl/strings.xml
+++ b/packages/SettingsLib/res/values-nl/strings.xml
@@ -539,8 +539,7 @@
<string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Beheerd door beperkte instelling"</string>
<string name="disabled_in_phone_call_text" msgid="6568931334337318320">"Niet beschikbaar tijdens gesprekken"</string>
<string name="disabled" msgid="8017887509554714950">"Uitgezet"</string>
- <!-- no translation found for enabled (3997122818554810678) -->
- <skip />
+ <string name="enabled" msgid="3997122818554810678">"Aangezet"</string>
<string name="external_source_trusted" msgid="1146522036773132905">"Toegestaan"</string>
<string name="external_source_untrusted" msgid="5037891688911672227">"Niet toegestaan"</string>
<string name="install_other_apps" msgid="3232595082023199454">"Onbekende apps installeren"</string>
@@ -628,6 +627,7 @@
<string name="tv_media_transfer_internal_speakers" msgid="4662765121700213785">"Ingebouwde speaker"</string>
<string name="tv_media_transfer_hdmi_title" msgid="6715658310934507444">"Tv-audio"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Probleem bij verbinding maken. Zet het apparaat uit en weer aan."</string>
+ <string name="bluetooth_key_missing_subtext" msgid="1003639333895028298">"Kan geen verbinding maken"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Bedraad audioapparaat"</string>
<string name="help_label" msgid="3528360748637781274">"Hulp en feedback"</string>
<string name="storage_category" msgid="2287342585424631813">"Opslag"</string>
diff --git a/packages/SettingsLib/res/values-or/strings.xml b/packages/SettingsLib/res/values-or/strings.xml
index d6e26314a0f9..0ba9cb6a75c2 100644
--- a/packages/SettingsLib/res/values-or/strings.xml
+++ b/packages/SettingsLib/res/values-or/strings.xml
@@ -539,8 +539,7 @@
<string name="disabled_by_app_ops_text" msgid="8373595926549098012">"ପ୍ରତିବନ୍ଧିତ ସେଟିଂ ଦ୍ୱାରା ନିୟନ୍ତ୍ରଣ କରାଯାଇଛି"</string>
<string name="disabled_in_phone_call_text" msgid="6568931334337318320">"କଲ କରିବାବେଳେ ଉପଲବ୍ଧ ନଥାଏ"</string>
<string name="disabled" msgid="8017887509554714950">"ଅକ୍ଷମ ହୋଇଛି"</string>
- <!-- no translation found for enabled (3997122818554810678) -->
- <skip />
+ <string name="enabled" msgid="3997122818554810678">"ସକ୍ଷମ କରାଯାଇଛି"</string>
<string name="external_source_trusted" msgid="1146522036773132905">"ଅନୁମତି ଦିଆଯାଇଛି"</string>
<string name="external_source_untrusted" msgid="5037891688911672227">"ଅନୁମତି ନାହିଁ"</string>
<string name="install_other_apps" msgid="3232595082023199454">"ଅଜଣା ଆପ ଇନଷ୍ଟଲ କରନ୍ତୁ"</string>
@@ -612,7 +611,7 @@
<string name="media_transfer_this_phone" msgid="7194341457812151531">"ଏହି ଫୋନ୍"</string>
<string name="media_transfer_digital_line_name" msgid="312091711951124301">"S/PDIF"</string>
<string name="media_transfer_analog_line_name" msgid="1841163866716302104">"ଆନାଲଗ"</string>
- <string name="media_transfer_aux_line_name" msgid="894135835967856689">"ଅକ୍ସିଲାରି"</string>
+ <string name="media_transfer_aux_line_name" msgid="894135835967856689">"AUX"</string>
<string name="media_output_status_unknown_error" msgid="5098565887497902222">"ଏହି ଡିଭାଇସରେ ପ୍ଲେ କରାଯାଇପାରିବ ନାହିଁ"</string>
<string name="media_output_status_require_premium" msgid="8411255800047014822">"ସ୍ୱିଚ କରିବା ପାଇଁ ଆକାଉଣ୍ଟକୁ ଅପଗ୍ରେଡ କରନ୍ତୁ"</string>
<string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"ଏଠାରେ ଡାଉନଲୋଡଗୁଡ଼ିକୁ ପ୍ଲେ କରାଯାଇପାରିବ ନାହିଁ"</string>
@@ -628,6 +627,7 @@
<string name="tv_media_transfer_internal_speakers" msgid="4662765121700213785">"ବିଲ୍ଟ-ଇନ ସ୍ପିକର"</string>
<string name="tv_media_transfer_hdmi_title" msgid="6715658310934507444">"ଟିଭି ଅଡିଓ"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"ସଂଯୋଗ କରିବାରେ ସମସ୍ୟା ହେଉଛି। ଡିଭାଇସ୍ ବନ୍ଦ କରି ପୁଣି ଚାଲୁ କରନ୍ତୁ"</string>
+ <string name="bluetooth_key_missing_subtext" msgid="1003639333895028298">"କନେକ୍ଟ କରାଯାଇପାରିବ ନାହିଁ"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"ତାରଯୁକ୍ତ ଅଡିଓ ଡିଭାଇସ୍"</string>
<string name="help_label" msgid="3528360748637781274">"ସାହାଯ୍ୟ ଓ ମତାମତ"</string>
<string name="storage_category" msgid="2287342585424631813">"ଷ୍ଟୋରେଜ"</string>
diff --git a/packages/SettingsLib/res/values-pa/strings.xml b/packages/SettingsLib/res/values-pa/strings.xml
index ab5f4afd07f3..08106e469bfd 100644
--- a/packages/SettingsLib/res/values-pa/strings.xml
+++ b/packages/SettingsLib/res/values-pa/strings.xml
@@ -539,8 +539,7 @@
<string name="disabled_by_app_ops_text" msgid="8373595926549098012">"ਪ੍ਰਤਿਬੰਧਿਤ ਸੈਟਿੰਗ ਰਾਹੀਂ ਕੰਟਰੋਲ ਕੀਤੀ ਜਾਂਦੀ ਹੈ"</string>
<string name="disabled_in_phone_call_text" msgid="6568931334337318320">"ਕਾਲਾਂ ਦੌਰਾਨ ਉਪਲਬਧ ਨਹੀਂ ਹੈ"</string>
<string name="disabled" msgid="8017887509554714950">"ਅਯੋਗ ਬਣਾਇਆ"</string>
- <!-- no translation found for enabled (3997122818554810678) -->
- <skip />
+ <string name="enabled" msgid="3997122818554810678">"ਚਾਲੂ ਹੈ"</string>
<string name="external_source_trusted" msgid="1146522036773132905">"ਮਨਜ਼ੂਰਸ਼ੁਦਾ"</string>
<string name="external_source_untrusted" msgid="5037891688911672227">"ਗੈਰ-ਮਨਜ਼ੂਰਸ਼ੁਦਾ"</string>
<string name="install_other_apps" msgid="3232595082023199454">"ਅਗਿਆਤ ਐਪਾਂ ਦੀ ਸਥਾਪਨਾ"</string>
@@ -628,6 +627,7 @@
<string name="tv_media_transfer_internal_speakers" msgid="4662765121700213785">"ਬਿਲਟ-ਇਨ ਸਪੀਕਰ"</string>
<string name="tv_media_transfer_hdmi_title" msgid="6715658310934507444">"ਟੀਵੀ ਆਡੀਓ"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"ਕਨੈਕਟ ਕਰਨ ਵਿੱਚ ਸਮੱਸਿਆ ਆਈ। ਡੀਵਾਈਸ ਨੂੰ ਬੰਦ ਕਰਕੇ ਵਾਪਸ ਚਾਲੂ ਕਰੋ"</string>
+ <string name="bluetooth_key_missing_subtext" msgid="1003639333895028298">"ਕਨੈਕਟ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਦਾ"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"ਤਾਰ ਵਾਲਾ ਆਡੀਓ ਡੀਵਾਈਸ"</string>
<string name="help_label" msgid="3528360748637781274">"ਮਦਦ ਅਤੇ ਵਿਚਾਰ"</string>
<string name="storage_category" msgid="2287342585424631813">"ਸਟੋਰੇਜ"</string>
diff --git a/packages/SettingsLib/res/values-pl/strings.xml b/packages/SettingsLib/res/values-pl/strings.xml
index 462142468e26..d8d2b0befde5 100644
--- a/packages/SettingsLib/res/values-pl/strings.xml
+++ b/packages/SettingsLib/res/values-pl/strings.xml
@@ -539,8 +539,7 @@
<string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Obowiązują ustawienia z ograniczonym dostępem"</string>
<string name="disabled_in_phone_call_text" msgid="6568931334337318320">"Niedostępne w trakcie połączeń"</string>
<string name="disabled" msgid="8017887509554714950">"Wyłączona"</string>
- <!-- no translation found for enabled (3997122818554810678) -->
- <skip />
+ <string name="enabled" msgid="3997122818554810678">"Włączono"</string>
<string name="external_source_trusted" msgid="1146522036773132905">"Dozwolone"</string>
<string name="external_source_untrusted" msgid="5037891688911672227">"Niedozwolone"</string>
<string name="install_other_apps" msgid="3232595082023199454">"Instalowanie nieznanych aplikacji"</string>
@@ -626,8 +625,9 @@
<string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"Połączono przez ARC"</string>
<string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"Połączono przez eARC"</string>
<string name="tv_media_transfer_internal_speakers" msgid="4662765121700213785">"Wbudowany głośnik"</string>
- <string name="tv_media_transfer_hdmi_title" msgid="6715658310934507444">"Telewizyjne urządzenie audio"</string>
+ <string name="tv_media_transfer_hdmi_title" msgid="6715658310934507444">"Audio z telewizora"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Problem z połączeniem. Wyłącz i ponownie włącz urządzenie"</string>
+ <string name="bluetooth_key_missing_subtext" msgid="1003639333895028298">"Nie można się połączyć"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Przewodowe urządzenie audio"</string>
<string name="help_label" msgid="3528360748637781274">"Pomoc i opinie"</string>
<string name="storage_category" msgid="2287342585424631813">"Pamięć wewnętrzna"</string>
diff --git a/packages/SettingsLib/res/values-pt-rBR/strings.xml b/packages/SettingsLib/res/values-pt-rBR/strings.xml
index 482f47931f4a..62d879b8e663 100644
--- a/packages/SettingsLib/res/values-pt-rBR/strings.xml
+++ b/packages/SettingsLib/res/values-pt-rBR/strings.xml
@@ -539,8 +539,7 @@
<string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Controlada pelas configurações restritas"</string>
<string name="disabled_in_phone_call_text" msgid="6568931334337318320">"Indisponível durante ligações"</string>
<string name="disabled" msgid="8017887509554714950">"Desativado"</string>
- <!-- no translation found for enabled (3997122818554810678) -->
- <skip />
+ <string name="enabled" msgid="3997122818554810678">"Ativado"</string>
<string name="external_source_trusted" msgid="1146522036773132905">"Permitido"</string>
<string name="external_source_untrusted" msgid="5037891688911672227">"Não permitido"</string>
<string name="install_other_apps" msgid="3232595082023199454">"Instalar apps desconhecidos"</string>
@@ -628,6 +627,8 @@
<string name="tv_media_transfer_internal_speakers" msgid="4662765121700213785">"Alto-falante integrado"</string>
<string name="tv_media_transfer_hdmi_title" msgid="6715658310934507444">"Áudio da TV"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Ocorreu um problema na conexão. Desligue o dispositivo e ligue-o novamente"</string>
+ <!-- no translation found for bluetooth_key_missing_subtext (1003639333895028298) -->
+ <skip />
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Dispositivo de áudio com fio"</string>
<string name="help_label" msgid="3528360748637781274">"Ajuda e feedback"</string>
<string name="storage_category" msgid="2287342585424631813">"Armazenamento"</string>
diff --git a/packages/SettingsLib/res/values-pt-rPT/strings.xml b/packages/SettingsLib/res/values-pt-rPT/strings.xml
index d6ac53390403..194af5b03337 100644
--- a/packages/SettingsLib/res/values-pt-rPT/strings.xml
+++ b/packages/SettingsLib/res/values-pt-rPT/strings.xml
@@ -610,7 +610,7 @@
<string name="media_transfer_default_device_name" msgid="4315604017399871828">"Dispositivo associado"</string>
<string name="media_transfer_this_phone" msgid="7194341457812151531">"Este telemóvel"</string>
<string name="media_transfer_digital_line_name" msgid="312091711951124301">"S/PDIF"</string>
- <string name="media_transfer_analog_line_name" msgid="1841163866716302104">"Analógico"</string>
+ <string name="media_transfer_analog_line_name" msgid="1841163866716302104">"Analógica"</string>
<string name="media_transfer_aux_line_name" msgid="894135835967856689">"AUX"</string>
<string name="media_output_status_unknown_error" msgid="5098565887497902222">"Não é possível reproduzir neste dispositivo"</string>
<string name="media_output_status_require_premium" msgid="8411255800047014822">"Atualize a conta para mudar"</string>
@@ -627,6 +627,7 @@
<string name="tv_media_transfer_internal_speakers" msgid="4662765121700213785">"Altifalante integrado"</string>
<string name="tv_media_transfer_hdmi_title" msgid="6715658310934507444">"Áudio da TV"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Problema ao ligar. Desligue e volte a ligar o dispositivo."</string>
+ <string name="bluetooth_key_missing_subtext" msgid="1003639333895028298">"Não é possível estabelecer ligação"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Dispositivo de áudio com fios"</string>
<string name="help_label" msgid="3528360748637781274">"Ajuda e feedback"</string>
<string name="storage_category" msgid="2287342585424631813">"Armazenamento"</string>
diff --git a/packages/SettingsLib/res/values-pt/strings.xml b/packages/SettingsLib/res/values-pt/strings.xml
index 482f47931f4a..62d879b8e663 100644
--- a/packages/SettingsLib/res/values-pt/strings.xml
+++ b/packages/SettingsLib/res/values-pt/strings.xml
@@ -539,8 +539,7 @@
<string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Controlada pelas configurações restritas"</string>
<string name="disabled_in_phone_call_text" msgid="6568931334337318320">"Indisponível durante ligações"</string>
<string name="disabled" msgid="8017887509554714950">"Desativado"</string>
- <!-- no translation found for enabled (3997122818554810678) -->
- <skip />
+ <string name="enabled" msgid="3997122818554810678">"Ativado"</string>
<string name="external_source_trusted" msgid="1146522036773132905">"Permitido"</string>
<string name="external_source_untrusted" msgid="5037891688911672227">"Não permitido"</string>
<string name="install_other_apps" msgid="3232595082023199454">"Instalar apps desconhecidos"</string>
@@ -628,6 +627,8 @@
<string name="tv_media_transfer_internal_speakers" msgid="4662765121700213785">"Alto-falante integrado"</string>
<string name="tv_media_transfer_hdmi_title" msgid="6715658310934507444">"Áudio da TV"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Ocorreu um problema na conexão. Desligue o dispositivo e ligue-o novamente"</string>
+ <!-- no translation found for bluetooth_key_missing_subtext (1003639333895028298) -->
+ <skip />
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Dispositivo de áudio com fio"</string>
<string name="help_label" msgid="3528360748637781274">"Ajuda e feedback"</string>
<string name="storage_category" msgid="2287342585424631813">"Armazenamento"</string>
diff --git a/packages/SettingsLib/res/values-ro/strings.xml b/packages/SettingsLib/res/values-ro/strings.xml
index 757fee9e110e..7d07d5438363 100644
--- a/packages/SettingsLib/res/values-ro/strings.xml
+++ b/packages/SettingsLib/res/values-ro/strings.xml
@@ -539,8 +539,7 @@
<string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Controlată de setarea restricționată"</string>
<string name="disabled_in_phone_call_text" msgid="6568931334337318320">"Indisponibil în timpul apelurilor"</string>
<string name="disabled" msgid="8017887509554714950">"Dezactivată"</string>
- <!-- no translation found for enabled (3997122818554810678) -->
- <skip />
+ <string name="enabled" msgid="3997122818554810678">"Activată"</string>
<string name="external_source_trusted" msgid="1146522036773132905">"Permise"</string>
<string name="external_source_untrusted" msgid="5037891688911672227">"Nepermise"</string>
<string name="install_other_apps" msgid="3232595082023199454">"Instalarea aplicațiilor necunoscute"</string>
@@ -628,6 +627,7 @@
<string name="tv_media_transfer_internal_speakers" msgid="4662765121700213785">"Difuzor încorporat"</string>
<string name="tv_media_transfer_hdmi_title" msgid="6715658310934507444">"Audio TV"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Problemă la conectare. Oprește și repornește dispozitivul."</string>
+ <string name="bluetooth_key_missing_subtext" msgid="1003639333895028298">"Nu se poate conecta"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Dispozitiv audio cu fir"</string>
<string name="help_label" msgid="3528360748637781274">"Ajutor și feedback"</string>
<string name="storage_category" msgid="2287342585424631813">"Stocare"</string>
diff --git a/packages/SettingsLib/res/values-ru/strings.xml b/packages/SettingsLib/res/values-ru/strings.xml
index 9e922a0b85b3..55ff696f418f 100644
--- a/packages/SettingsLib/res/values-ru/strings.xml
+++ b/packages/SettingsLib/res/values-ru/strings.xml
@@ -539,8 +539,7 @@
<string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Контролируется настройками с ограниченным доступом"</string>
<string name="disabled_in_phone_call_text" msgid="6568931334337318320">"Недоступно во время вызовов"</string>
<string name="disabled" msgid="8017887509554714950">"Отключено"</string>
- <!-- no translation found for enabled (3997122818554810678) -->
- <skip />
+ <string name="enabled" msgid="3997122818554810678">"Включено"</string>
<string name="external_source_trusted" msgid="1146522036773132905">"Разрешено"</string>
<string name="external_source_untrusted" msgid="5037891688911672227">"Запрещено"</string>
<string name="install_other_apps" msgid="3232595082023199454">"Установка неизвестных приложений"</string>
@@ -628,6 +627,7 @@
<string name="tv_media_transfer_internal_speakers" msgid="4662765121700213785">"Встроенный динамик"</string>
<string name="tv_media_transfer_hdmi_title" msgid="6715658310934507444">"Аудиовыход телевизора"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Ошибка подключения. Выключите и снова включите устройство."</string>
+ <string name="bluetooth_key_missing_subtext" msgid="1003639333895028298">"Ошибка подключения"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Проводное аудиоустройство"</string>
<string name="help_label" msgid="3528360748637781274">"Справка/отзыв"</string>
<string name="storage_category" msgid="2287342585424631813">"Хранилище"</string>
diff --git a/packages/SettingsLib/res/values-si/strings.xml b/packages/SettingsLib/res/values-si/strings.xml
index 517b00c61efb..4546f4b75ebe 100644
--- a/packages/SettingsLib/res/values-si/strings.xml
+++ b/packages/SettingsLib/res/values-si/strings.xml
@@ -539,8 +539,7 @@
<string name="disabled_by_app_ops_text" msgid="8373595926549098012">"සීමා කළ සැකසීම මගින් පාලනය වේ"</string>
<string name="disabled_in_phone_call_text" msgid="6568931334337318320">"ඇමතුම් අතරතුර නොපවතී"</string>
<string name="disabled" msgid="8017887509554714950">"අබල කර ඇත"</string>
- <!-- no translation found for enabled (3997122818554810678) -->
- <skip />
+ <string name="enabled" msgid="3997122818554810678">"සබලයි"</string>
<string name="external_source_trusted" msgid="1146522036773132905">"ඉඩ දුන්"</string>
<string name="external_source_untrusted" msgid="5037891688911672227">"ඉඩ නොදෙන"</string>
<string name="install_other_apps" msgid="3232595082023199454">"නොදන්නා යෙදුම් ස්ථාපනය"</string>
@@ -628,6 +627,7 @@
<string name="tv_media_transfer_internal_speakers" msgid="4662765121700213785">"එකට තැනූ ශබ්දවාහිනීය"</string>
<string name="tv_media_transfer_hdmi_title" msgid="6715658310934507444">"රූපවාහිනී ශ්‍රව්‍ය"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"සම්බන්ධ කිරීමේ ගැටලුවකි උපාංගය ක්‍රියාවිරහිත කර &amp; ආපසු ක්‍රියාත්මක කරන්න"</string>
+ <string name="bluetooth_key_missing_subtext" msgid="1003639333895028298">"සම්බන්ධ විය නොහැක"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"රැහැන්ගත කළ ඕඩියෝ උපාංගය"</string>
<string name="help_label" msgid="3528360748637781274">"උදවු &amp; ප්‍රතිපෝෂණ"</string>
<string name="storage_category" msgid="2287342585424631813">"ගබඩාව"</string>
diff --git a/packages/SettingsLib/res/values-sk/strings.xml b/packages/SettingsLib/res/values-sk/strings.xml
index e8fb53fdcef6..7578d063ebd4 100644
--- a/packages/SettingsLib/res/values-sk/strings.xml
+++ b/packages/SettingsLib/res/values-sk/strings.xml
@@ -539,8 +539,7 @@
<string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Ovládané obmedzeným nastavením"</string>
<string name="disabled_in_phone_call_text" msgid="6568931334337318320">"Počas hovorov nie je k dispozícii"</string>
<string name="disabled" msgid="8017887509554714950">"Deaktivované"</string>
- <!-- no translation found for enabled (3997122818554810678) -->
- <skip />
+ <string name="enabled" msgid="3997122818554810678">"Zapnuté"</string>
<string name="external_source_trusted" msgid="1146522036773132905">"Povolené"</string>
<string name="external_source_untrusted" msgid="5037891688911672227">"Nie je povolené"</string>
<string name="install_other_apps" msgid="3232595082023199454">"Inštalácia neznámych aplikácií"</string>
@@ -628,9 +627,10 @@
<string name="tv_media_transfer_internal_speakers" msgid="4662765121700213785">"Vstavaný reproduktor"</string>
<string name="tv_media_transfer_hdmi_title" msgid="6715658310934507444">"Zvuk televízora"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Pri pripájaní sa vyskytol problém. Zariadenie vypnite a znova zapnite."</string>
+ <string name="bluetooth_key_missing_subtext" msgid="1003639333895028298">"Nedá sa pripojiť"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Audio zariadenie s káblom"</string>
<string name="help_label" msgid="3528360748637781274">"Pomocník a spätná väzba"</string>
- <string name="storage_category" msgid="2287342585424631813">"Priestor"</string>
+ <string name="storage_category" msgid="2287342585424631813">"Ukladací priestor"</string>
<string name="shared_data_title" msgid="1017034836800864953">"Zdieľané údaje"</string>
<string name="shared_data_summary" msgid="5516326713822885652">"Zobrazenie a úprava zdieľaných údajov"</string>
<string name="shared_data_no_blobs_text" msgid="3108114670341737434">"Pre tohto používateľa nie sú k dispozícii žiadne zdieľané údaje."</string>
diff --git a/packages/SettingsLib/res/values-sl/strings.xml b/packages/SettingsLib/res/values-sl/strings.xml
index bae7722cc6e9..4b268cb7e223 100644
--- a/packages/SettingsLib/res/values-sl/strings.xml
+++ b/packages/SettingsLib/res/values-sl/strings.xml
@@ -627,6 +627,7 @@
<string name="tv_media_transfer_internal_speakers" msgid="4662765121700213785">"Vgrajen zvočnik"</string>
<string name="tv_media_transfer_hdmi_title" msgid="6715658310934507444">"Zvok televizorja"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Težava pri povezovanju. Napravo izklopite in znova vklopite."</string>
+ <string name="bluetooth_key_missing_subtext" msgid="1003639333895028298">"Povezave ni mogoče vzpostaviti"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Žična zvočna naprava"</string>
<string name="help_label" msgid="3528360748637781274">"Pomoč in povratne informacije"</string>
<string name="storage_category" msgid="2287342585424631813">"Shramba"</string>
diff --git a/packages/SettingsLib/res/values-sq/strings.xml b/packages/SettingsLib/res/values-sq/strings.xml
index 8a711d5ebcee..a296f87e1ed7 100644
--- a/packages/SettingsLib/res/values-sq/strings.xml
+++ b/packages/SettingsLib/res/values-sq/strings.xml
@@ -539,8 +539,7 @@
<string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Kontrollohet nga \"Cilësimet e kufizuara\""</string>
<string name="disabled_in_phone_call_text" msgid="6568931334337318320">"Nuk ofrohet gjatë telefonatave"</string>
<string name="disabled" msgid="8017887509554714950">"Çaktivizuar"</string>
- <!-- no translation found for enabled (3997122818554810678) -->
- <skip />
+ <string name="enabled" msgid="3997122818554810678">"Aktivizuar"</string>
<string name="external_source_trusted" msgid="1146522036773132905">"Lejohet"</string>
<string name="external_source_untrusted" msgid="5037891688911672227">"Nuk lejohet"</string>
<string name="install_other_apps" msgid="3232595082023199454">"Instalo aplikacione të panjohura"</string>
@@ -628,6 +627,7 @@
<string name="tv_media_transfer_internal_speakers" msgid="4662765121700213785">"Altoparlanti i integruar"</string>
<string name="tv_media_transfer_hdmi_title" msgid="6715658310934507444">"Audioja e televizorit"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Problem me lidhjen. Fike dhe ndize përsëri pajisjen"</string>
+ <string name="bluetooth_key_missing_subtext" msgid="1003639333895028298">"Nuk mund të lidhet"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Pajisja audio me tel"</string>
<string name="help_label" msgid="3528360748637781274">"Ndihma dhe komentet"</string>
<string name="storage_category" msgid="2287342585424631813">"Hapësira ruajtëse"</string>
diff --git a/packages/SettingsLib/res/values-sr/strings.xml b/packages/SettingsLib/res/values-sr/strings.xml
index d57eb5c4c830..defea3a5709f 100644
--- a/packages/SettingsLib/res/values-sr/strings.xml
+++ b/packages/SettingsLib/res/values-sr/strings.xml
@@ -539,8 +539,7 @@
<string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Контролишу ограничена подешавања"</string>
<string name="disabled_in_phone_call_text" msgid="6568931334337318320">"Недоступно током позива"</string>
<string name="disabled" msgid="8017887509554714950">"Онемогућено"</string>
- <!-- no translation found for enabled (3997122818554810678) -->
- <skip />
+ <string name="enabled" msgid="3997122818554810678">"Омогућено"</string>
<string name="external_source_trusted" msgid="1146522036773132905">"Дозвољено"</string>
<string name="external_source_untrusted" msgid="5037891688911672227">"Није дозвољено"</string>
<string name="install_other_apps" msgid="3232595082023199454">"Инсталирање непознатих апликација"</string>
@@ -628,6 +627,7 @@
<string name="tv_media_transfer_internal_speakers" msgid="4662765121700213785">"Уграђени звучник"</string>
<string name="tv_media_transfer_hdmi_title" msgid="6715658310934507444">"Звук ТВ-а"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Проблем при повезивању. Искључите уређај, па га поново укључите"</string>
+ <string name="bluetooth_key_missing_subtext" msgid="1003639333895028298">"Повезивање није успело"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Жичани аудио уређај"</string>
<string name="help_label" msgid="3528360748637781274">"Помоћ и повратне информације"</string>
<string name="storage_category" msgid="2287342585424631813">"Меморијски простор"</string>
diff --git a/packages/SettingsLib/res/values-sv/strings.xml b/packages/SettingsLib/res/values-sv/strings.xml
index 5d27839daa6a..d03b99b24414 100644
--- a/packages/SettingsLib/res/values-sv/strings.xml
+++ b/packages/SettingsLib/res/values-sv/strings.xml
@@ -539,8 +539,7 @@
<string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Styrs av spärrad inställning"</string>
<string name="disabled_in_phone_call_text" msgid="6568931334337318320">"Ej tillgänglig under samtal"</string>
<string name="disabled" msgid="8017887509554714950">"Inaktiverad"</string>
- <!-- no translation found for enabled (3997122818554810678) -->
- <skip />
+ <string name="enabled" msgid="3997122818554810678">"Aktiverat"</string>
<string name="external_source_trusted" msgid="1146522036773132905">"Tillåts"</string>
<string name="external_source_untrusted" msgid="5037891688911672227">"Tillåts inte"</string>
<string name="install_other_apps" msgid="3232595082023199454">"Installera okända appar"</string>
@@ -628,6 +627,7 @@
<string name="tv_media_transfer_internal_speakers" msgid="4662765121700213785">"Inbyggd högtalare"</string>
<string name="tv_media_transfer_hdmi_title" msgid="6715658310934507444">"Tv-ljud"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Det gick inte att ansluta. Stäng av enheten och slå på den igen"</string>
+ <string name="bluetooth_key_missing_subtext" msgid="1003639333895028298">"Det går inte att ansluta"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Ljudenhet med kabelanslutning"</string>
<string name="help_label" msgid="3528360748637781274">"Hjälp och feedback"</string>
<string name="storage_category" msgid="2287342585424631813">"Lagring"</string>
diff --git a/packages/SettingsLib/res/values-sw/strings.xml b/packages/SettingsLib/res/values-sw/strings.xml
index 9ddb7ff129ad..0755ce8b429e 100644
--- a/packages/SettingsLib/res/values-sw/strings.xml
+++ b/packages/SettingsLib/res/values-sw/strings.xml
@@ -539,8 +539,7 @@
<string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Imedhibitiwa na Mpangilio wenye Mipaka"</string>
<string name="disabled_in_phone_call_text" msgid="6568931334337318320">"Haipatikani wakati unaongea kwa simu"</string>
<string name="disabled" msgid="8017887509554714950">"Imezimwa"</string>
- <!-- no translation found for enabled (3997122818554810678) -->
- <skip />
+ <string name="enabled" msgid="3997122818554810678">"Imewashwa"</string>
<string name="external_source_trusted" msgid="1146522036773132905">"Imeruhusiwa"</string>
<string name="external_source_untrusted" msgid="5037891688911672227">"Hairuhusiwi"</string>
<string name="install_other_apps" msgid="3232595082023199454">"Kuweka programu zisizojulikana"</string>
@@ -625,9 +624,10 @@
<string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string>
<string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"Imeunganishwa kupitia ARC"</string>
<string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"Imeunganishwa kupitia eARC"</string>
- <string name="tv_media_transfer_internal_speakers" msgid="4662765121700213785">"Spika iliyojumuishwa ndani"</string>
+ <string name="tv_media_transfer_internal_speakers" msgid="4662765121700213785">"Spika iliyojumuishwa"</string>
<string name="tv_media_transfer_hdmi_title" msgid="6715658310934507444">"Sauti ya Televisheni"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Kuna tatizo la kuunganisha kwenye Intaneti. Zima kisha uwashe kifaa"</string>
+ <string name="bluetooth_key_missing_subtext" msgid="1003639333895028298">"Imeshindwa kuunganisha"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Kifaa cha sauti kinachotumia waya"</string>
<string name="help_label" msgid="3528360748637781274">"Usaidizi na maoni"</string>
<string name="storage_category" msgid="2287342585424631813">"Hifadhi"</string>
diff --git a/packages/SettingsLib/res/values-ta/strings.xml b/packages/SettingsLib/res/values-ta/strings.xml
index 68a60cf85a90..a67f35edc338 100644
--- a/packages/SettingsLib/res/values-ta/strings.xml
+++ b/packages/SettingsLib/res/values-ta/strings.xml
@@ -539,8 +539,7 @@
<string name="disabled_by_app_ops_text" msgid="8373595926549098012">"வரையறுக்கப்பட்ட அமைப்பால் கட்டுப்படுத்தப்படுகிறது"</string>
<string name="disabled_in_phone_call_text" msgid="6568931334337318320">"அழைப்புகளின்போது பயன்படுத்த முடியாது"</string>
<string name="disabled" msgid="8017887509554714950">"முடக்கப்பட்டது"</string>
- <!-- no translation found for enabled (3997122818554810678) -->
- <skip />
+ <string name="enabled" msgid="3997122818554810678">"இயக்கப்பட்டது"</string>
<string name="external_source_trusted" msgid="1146522036773132905">"அனுமதிக்கப்பட்டது"</string>
<string name="external_source_untrusted" msgid="5037891688911672227">"அனுமதிக்கப்படவில்லை"</string>
<string name="install_other_apps" msgid="3232595082023199454">"தெரியாத ஆப்ஸ்களை நிறுவுதல்"</string>
@@ -628,6 +627,7 @@
<string name="tv_media_transfer_internal_speakers" msgid="4662765121700213785">"உள்ளமைந்த ஸ்பீக்கர்"</string>
<string name="tv_media_transfer_hdmi_title" msgid="6715658310934507444">"டிவி ஆடியோ"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"இணைப்பதில் சிக்கல். சாதனத்தை ஆஃப் செய்து மீண்டும் ஆன் செய்யவும்"</string>
+ <string name="bluetooth_key_missing_subtext" msgid="1003639333895028298">"இணைக்க முடியவில்லை"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"வயருடன்கூடிய ஆடியோ சாதனம்"</string>
<string name="help_label" msgid="3528360748637781274">"உதவியும் கருத்தும்"</string>
<string name="storage_category" msgid="2287342585424631813">"சேமிப்பகம்"</string>
diff --git a/packages/SettingsLib/res/values-te/strings.xml b/packages/SettingsLib/res/values-te/strings.xml
index c2f284e0acb6..ffca18d1a3f9 100644
--- a/packages/SettingsLib/res/values-te/strings.xml
+++ b/packages/SettingsLib/res/values-te/strings.xml
@@ -627,6 +627,7 @@
<string name="tv_media_transfer_internal_speakers" msgid="4662765121700213785">"బిల్ట్-ఇన్ స్పీకర్"</string>
<string name="tv_media_transfer_hdmi_title" msgid="6715658310934507444">"టీవీ ఆడియో"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"కనెక్ట్ చేయడంలో సమస్య ఉంది. పరికరాన్ని ఆఫ్ చేసి, ఆపై తిరిగి ఆన్ చేయండి"</string>
+ <string name="bluetooth_key_missing_subtext" msgid="1003639333895028298">"కనెక్ట్ చేయడం సాధ్యపడలేదు"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"వైర్ గల ఆడియో పరికరం"</string>
<string name="help_label" msgid="3528360748637781274">"సహాయం &amp; ఫీడ్‌బ్యాక్"</string>
<string name="storage_category" msgid="2287342585424631813">"స్టోరేజ్"</string>
diff --git a/packages/SettingsLib/res/values-th/strings.xml b/packages/SettingsLib/res/values-th/strings.xml
index 035644f845f1..fce95ac055d5 100644
--- a/packages/SettingsLib/res/values-th/strings.xml
+++ b/packages/SettingsLib/res/values-th/strings.xml
@@ -539,8 +539,7 @@
<string name="disabled_by_app_ops_text" msgid="8373595926549098012">"ควบคุมโดยการตั้งค่าที่จำกัด"</string>
<string name="disabled_in_phone_call_text" msgid="6568931334337318320">"ใช้งานไม่ได้ขณะสนทนาโทรศัพท์"</string>
<string name="disabled" msgid="8017887509554714950">"ปิดอยู่"</string>
- <!-- no translation found for enabled (3997122818554810678) -->
- <skip />
+ <string name="enabled" msgid="3997122818554810678">"เปิดใช้อยู่"</string>
<string name="external_source_trusted" msgid="1146522036773132905">"อนุญาต"</string>
<string name="external_source_untrusted" msgid="5037891688911672227">"ไม่อนุญาต"</string>
<string name="install_other_apps" msgid="3232595082023199454">"ติดตั้งแอปที่ไม่รู้จัก"</string>
@@ -628,6 +627,7 @@
<string name="tv_media_transfer_internal_speakers" msgid="4662765121700213785">"ลำโพงในตัว"</string>
<string name="tv_media_transfer_hdmi_title" msgid="6715658310934507444">"เสียงจากทีวี"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"เกิดปัญหาในการเชื่อมต่อ ปิดอุปกรณ์แล้วเปิดใหม่อีกครั้ง"</string>
+ <string name="bluetooth_key_missing_subtext" msgid="1003639333895028298">"เชื่อมต่อไม่ได้"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"อุปกรณ์เสียงแบบมีสาย"</string>
<string name="help_label" msgid="3528360748637781274">"ความช่วยเหลือและความคิดเห็น"</string>
<string name="storage_category" msgid="2287342585424631813">"พื้นที่เก็บข้อมูล"</string>
diff --git a/packages/SettingsLib/res/values-tl/strings.xml b/packages/SettingsLib/res/values-tl/strings.xml
index 4ef2f436898a..011c8d7127cb 100644
--- a/packages/SettingsLib/res/values-tl/strings.xml
+++ b/packages/SettingsLib/res/values-tl/strings.xml
@@ -539,8 +539,7 @@
<string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Kinokontrol ng Pinaghihigpitang Setting"</string>
<string name="disabled_in_phone_call_text" msgid="6568931334337318320">"Hindi available habang may tawag"</string>
<string name="disabled" msgid="8017887509554714950">"Naka-disable"</string>
- <!-- no translation found for enabled (3997122818554810678) -->
- <skip />
+ <string name="enabled" msgid="3997122818554810678">"Naka-enable"</string>
<string name="external_source_trusted" msgid="1146522036773132905">"Pinapayagan"</string>
<string name="external_source_untrusted" msgid="5037891688911672227">"Hindi pinapayagan"</string>
<string name="install_other_apps" msgid="3232595082023199454">"Mag-install ng di-kilalang app"</string>
@@ -628,6 +627,7 @@
<string name="tv_media_transfer_internal_speakers" msgid="4662765121700213785">"Built-in na speaker"</string>
<string name="tv_media_transfer_hdmi_title" msgid="6715658310934507444">"Audio ng TV"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Nagkaproblema sa pagkonekta. I-off at pagkatapos ay i-on ang device"</string>
+ <string name="bluetooth_key_missing_subtext" msgid="1003639333895028298">"Hindi makakonekta"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Wired na audio device"</string>
<string name="help_label" msgid="3528360748637781274">"Tulong at feedback"</string>
<string name="storage_category" msgid="2287342585424631813">"Storage"</string>
diff --git a/packages/SettingsLib/res/values-tr/strings.xml b/packages/SettingsLib/res/values-tr/strings.xml
index 19295aa169ef..42852306fae5 100644
--- a/packages/SettingsLib/res/values-tr/strings.xml
+++ b/packages/SettingsLib/res/values-tr/strings.xml
@@ -539,8 +539,7 @@
<string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Kısıtlanmış ayar tarafından kontrol ediliyor"</string>
<string name="disabled_in_phone_call_text" msgid="6568931334337318320">"Telefon aramaları sırasında kullanılamaz"</string>
<string name="disabled" msgid="8017887509554714950">"Devre dışı"</string>
- <!-- no translation found for enabled (3997122818554810678) -->
- <skip />
+ <string name="enabled" msgid="3997122818554810678">"Etkin"</string>
<string name="external_source_trusted" msgid="1146522036773132905">"İzin verildi"</string>
<string name="external_source_untrusted" msgid="5037891688911672227">"İzin verilmiyor"</string>
<string name="install_other_apps" msgid="3232595082023199454">"Bilinmeyen uygulamaları yükleme"</string>
@@ -628,6 +627,7 @@
<string name="tv_media_transfer_internal_speakers" msgid="4662765121700213785">"Dahili hoparlör"</string>
<string name="tv_media_transfer_hdmi_title" msgid="6715658310934507444">"TV Sesi"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Bağlanırken sorun oluştu. Cihazı kapatıp tekrar açın"</string>
+ <string name="bluetooth_key_missing_subtext" msgid="1003639333895028298">"Bağlanılamıyor"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Kablolu ses cihazı"</string>
<string name="help_label" msgid="3528360748637781274">"Yardım ve geri bildirim"</string>
<string name="storage_category" msgid="2287342585424631813">"Depolama"</string>
diff --git a/packages/SettingsLib/res/values-uk/strings.xml b/packages/SettingsLib/res/values-uk/strings.xml
index e46137fd237e..e8fb02e1ee82 100644
--- a/packages/SettingsLib/res/values-uk/strings.xml
+++ b/packages/SettingsLib/res/values-uk/strings.xml
@@ -539,8 +539,7 @@
<string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Керується налаштуваннями з обмеженнями"</string>
<string name="disabled_in_phone_call_text" msgid="6568931334337318320">"Недоступно під час викликів"</string>
<string name="disabled" msgid="8017887509554714950">"Вимкнено"</string>
- <!-- no translation found for enabled (3997122818554810678) -->
- <skip />
+ <string name="enabled" msgid="3997122818554810678">"Увімкнено"</string>
<string name="external_source_trusted" msgid="1146522036773132905">"Дозволено"</string>
<string name="external_source_untrusted" msgid="5037891688911672227">"Заборонено"</string>
<string name="install_other_apps" msgid="3232595082023199454">"Встановлювати невідомі додатки"</string>
@@ -628,6 +627,8 @@
<string name="tv_media_transfer_internal_speakers" msgid="4662765121700213785">"Вбудований динамік"</string>
<string name="tv_media_transfer_hdmi_title" msgid="6715658310934507444">"Аудіо з телевізора"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Не вдається підключитися. Перезавантажте пристрій."</string>
+ <!-- no translation found for bluetooth_key_missing_subtext (1003639333895028298) -->
+ <skip />
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Дротовий аудіопристрій"</string>
<string name="help_label" msgid="3528360748637781274">"Довідка й відгуки"</string>
<string name="storage_category" msgid="2287342585424631813">"Сховище"</string>
diff --git a/packages/SettingsLib/res/values-ur/strings.xml b/packages/SettingsLib/res/values-ur/strings.xml
index e4b4001352cd..48aa063feb34 100644
--- a/packages/SettingsLib/res/values-ur/strings.xml
+++ b/packages/SettingsLib/res/values-ur/strings.xml
@@ -627,6 +627,7 @@
<string name="tv_media_transfer_internal_speakers" msgid="4662765121700213785">"پہلے سے شامل اسپیکر"</string>
<string name="tv_media_transfer_hdmi_title" msgid="6715658310934507444">"‏‫TV آڈیو"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"منسلک کرنے میں مسئلہ پیش آ گیا۔ آلہ کو آف اور بیک آن کریں"</string>
+ <string name="bluetooth_key_missing_subtext" msgid="1003639333895028298">"منسلک نہیں ہو سکتا"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"وائرڈ آڈیو آلہ"</string>
<string name="help_label" msgid="3528360748637781274">"مدد اور تاثرات"</string>
<string name="storage_category" msgid="2287342585424631813">"اسٹوریج"</string>
diff --git a/packages/SettingsLib/res/values-uz/strings.xml b/packages/SettingsLib/res/values-uz/strings.xml
index 2608a0440804..8969c0d84f17 100644
--- a/packages/SettingsLib/res/values-uz/strings.xml
+++ b/packages/SettingsLib/res/values-uz/strings.xml
@@ -627,6 +627,7 @@
<string name="tv_media_transfer_internal_speakers" msgid="4662765121700213785">"Ichki karnay"</string>
<string name="tv_media_transfer_hdmi_title" msgid="6715658310934507444">"TV Audio"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Ulanishda muammo yuz berdi. Qurilmani oʻchiring va yoqing"</string>
+ <string name="bluetooth_key_missing_subtext" msgid="1003639333895028298">"Ulanmadi"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Simli audio qurilma"</string>
<string name="help_label" msgid="3528360748637781274">"Yordam/fikr-mulohaza"</string>
<string name="storage_category" msgid="2287342585424631813">"Xotira"</string>
diff --git a/packages/SettingsLib/res/values-vi/strings.xml b/packages/SettingsLib/res/values-vi/strings.xml
index 8f5c0c2675e4..e45661721a0b 100644
--- a/packages/SettingsLib/res/values-vi/strings.xml
+++ b/packages/SettingsLib/res/values-vi/strings.xml
@@ -539,8 +539,7 @@
<string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Do chế độ Cài đặt hạn chế kiểm soát"</string>
<string name="disabled_in_phone_call_text" msgid="6568931334337318320">"Không dùng được khi có cuộc gọi"</string>
<string name="disabled" msgid="8017887509554714950">"Đã tắt"</string>
- <!-- no translation found for enabled (3997122818554810678) -->
- <skip />
+ <string name="enabled" msgid="3997122818554810678">"Đã bật"</string>
<string name="external_source_trusted" msgid="1146522036773132905">"Được phép"</string>
<string name="external_source_untrusted" msgid="5037891688911672227">"Không được phép"</string>
<string name="install_other_apps" msgid="3232595082023199454">"Cài ứng dụng không rõ nguồn"</string>
@@ -628,6 +627,7 @@
<string name="tv_media_transfer_internal_speakers" msgid="4662765121700213785">"Loa tích hợp"</string>
<string name="tv_media_transfer_hdmi_title" msgid="6715658310934507444">"Âm thanh TV"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Sự cố kết nối. Hãy tắt thiết bị rồi bật lại"</string>
+ <string name="bluetooth_key_missing_subtext" msgid="1003639333895028298">"Không kết nối được"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Thiết bị âm thanh có dây"</string>
<string name="help_label" msgid="3528360748637781274">"Trợ giúp và phản hồi"</string>
<string name="storage_category" msgid="2287342585424631813">"Bộ nhớ"</string>
diff --git a/packages/SettingsLib/res/values-zh-rCN/strings.xml b/packages/SettingsLib/res/values-zh-rCN/strings.xml
index 85d3c179a623..6fe9e2a2ecfc 100644
--- a/packages/SettingsLib/res/values-zh-rCN/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rCN/strings.xml
@@ -539,8 +539,7 @@
<string name="disabled_by_app_ops_text" msgid="8373595926549098012">"由受限设置控制"</string>
<string name="disabled_in_phone_call_text" msgid="6568931334337318320">"通话期间无法使用"</string>
<string name="disabled" msgid="8017887509554714950">"已停用"</string>
- <!-- no translation found for enabled (3997122818554810678) -->
- <skip />
+ <string name="enabled" msgid="3997122818554810678">"已启用"</string>
<string name="external_source_trusted" msgid="1146522036773132905">"允许"</string>
<string name="external_source_untrusted" msgid="5037891688911672227">"不允许"</string>
<string name="install_other_apps" msgid="3232595082023199454">"安装未知应用"</string>
@@ -628,6 +627,7 @@
<string name="tv_media_transfer_internal_speakers" msgid="4662765121700213785">"内置扬声器"</string>
<string name="tv_media_transfer_hdmi_title" msgid="6715658310934507444">"电视音频"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"连接时遇到问题。请关闭并重新开启设备"</string>
+ <string name="bluetooth_key_missing_subtext" msgid="1003639333895028298">"无法连接"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"有线音频设备"</string>
<string name="help_label" msgid="3528360748637781274">"帮助和反馈"</string>
<string name="storage_category" msgid="2287342585424631813">"存储空间"</string>
diff --git a/packages/SettingsLib/res/values-zh-rHK/strings.xml b/packages/SettingsLib/res/values-zh-rHK/strings.xml
index 3ff554000a6c..23ee3c98aa03 100644
--- a/packages/SettingsLib/res/values-zh-rHK/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rHK/strings.xml
@@ -539,8 +539,7 @@
<string name="disabled_by_app_ops_text" msgid="8373595926549098012">"由「受限設定」控制"</string>
<string name="disabled_in_phone_call_text" msgid="6568931334337318320">"通話時無法使用"</string>
<string name="disabled" msgid="8017887509554714950">"已停用"</string>
- <!-- no translation found for enabled (3997122818554810678) -->
- <skip />
+ <string name="enabled" msgid="3997122818554810678">"已啟用"</string>
<string name="external_source_trusted" msgid="1146522036773132905">"允許"</string>
<string name="external_source_untrusted" msgid="5037891688911672227">"不允許"</string>
<string name="install_other_apps" msgid="3232595082023199454">"安裝不明的應用程式"</string>
@@ -602,7 +601,7 @@
<string name="zen_mode_forever" msgid="3339224497605461291">"直至你關閉為止"</string>
<string name="zen_mode_starred_contacts_empty_name" msgid="933552939706125937">"(沒有名稱)"</string>
<string name="time_unit_just_now" msgid="3006134267292728099">"剛剛"</string>
- <string name="media_transfer_this_device_name" msgid="2357329267148436433">"此手機"</string>
+ <string name="media_transfer_this_device_name" msgid="2357329267148436433">"這部手機"</string>
<string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"此平板電腦"</string>
<string name="media_transfer_this_device_name_desktop" msgid="7912386128141470452">"此電腦 (內置)"</string>
<string name="media_transfer_this_device_name_tv" msgid="8508713779441163887">"這部電視"</string>
@@ -628,6 +627,7 @@
<string name="tv_media_transfer_internal_speakers" msgid="4662765121700213785">"內置喇叭"</string>
<string name="tv_media_transfer_hdmi_title" msgid="6715658310934507444">"電視音訊"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"無法連接,請關閉裝置然後重新開機"</string>
+ <string name="bluetooth_key_missing_subtext" msgid="1003639333895028298">"無法連線"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"有線音響裝置"</string>
<string name="help_label" msgid="3528360748637781274">"說明與意見反映"</string>
<string name="storage_category" msgid="2287342585424631813">"儲存空間"</string>
diff --git a/packages/SettingsLib/res/values-zh-rTW/strings.xml b/packages/SettingsLib/res/values-zh-rTW/strings.xml
index 5362d380564f..32151eeabd78 100644
--- a/packages/SettingsLib/res/values-zh-rTW/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rTW/strings.xml
@@ -539,8 +539,7 @@
<string name="disabled_by_app_ops_text" msgid="8373595926549098012">"由受限制的設定控管"</string>
<string name="disabled_in_phone_call_text" msgid="6568931334337318320">"通話時無法使用"</string>
<string name="disabled" msgid="8017887509554714950">"已停用"</string>
- <!-- no translation found for enabled (3997122818554810678) -->
- <skip />
+ <string name="enabled" msgid="3997122818554810678">"已啟用"</string>
<string name="external_source_trusted" msgid="1146522036773132905">"允許"</string>
<string name="external_source_untrusted" msgid="5037891688911672227">"不允許"</string>
<string name="install_other_apps" msgid="3232595082023199454">"安裝不明應用程式"</string>
@@ -628,6 +627,7 @@
<string name="tv_media_transfer_internal_speakers" msgid="4662765121700213785">"內建喇叭"</string>
<string name="tv_media_transfer_hdmi_title" msgid="6715658310934507444">"電視音訊"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"無法連線,請關閉裝置後再重新開啟"</string>
+ <string name="bluetooth_key_missing_subtext" msgid="1003639333895028298">"無法連線"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"有線音訊裝置"</string>
<string name="help_label" msgid="3528360748637781274">"說明與意見回饋"</string>
<string name="storage_category" msgid="2287342585424631813">"儲存空間"</string>
diff --git a/packages/SettingsLib/res/values-zu/strings.xml b/packages/SettingsLib/res/values-zu/strings.xml
index 79ce6d7b25d0..e4f3c573a275 100644
--- a/packages/SettingsLib/res/values-zu/strings.xml
+++ b/packages/SettingsLib/res/values-zu/strings.xml
@@ -539,8 +539,7 @@
<string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Kulawulwe Isethingi Elikhawulelwe"</string>
<string name="disabled_in_phone_call_text" msgid="6568931334337318320">"Akutholakali ngesikhathi samakholi"</string>
<string name="disabled" msgid="8017887509554714950">"Akusebenzi"</string>
- <!-- no translation found for enabled (3997122818554810678) -->
- <skip />
+ <string name="enabled" msgid="3997122818554810678">"Ukwenza kusebenze"</string>
<string name="external_source_trusted" msgid="1146522036773132905">"Kuvumelekile"</string>
<string name="external_source_untrusted" msgid="5037891688911672227">"Akuvumelekile"</string>
<string name="install_other_apps" msgid="3232595082023199454">"Faka ama-app angaziwa"</string>
@@ -626,8 +625,9 @@
<string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"Ixhunywe nge-ARC"</string>
<string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"Ixhunywe nge-eARC"</string>
<string name="tv_media_transfer_internal_speakers" msgid="4662765121700213785">"Isipikha esakhelwe ngaphakathi"</string>
- <string name="tv_media_transfer_hdmi_title" msgid="6715658310934507444">"Umsondo weTV"</string>
+ <string name="tv_media_transfer_hdmi_title" msgid="6715658310934507444">"Umsindo we-TV"</string>
<string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Inkinga yokuxhumeka. Vala idivayisi futhi uphinde uyivule"</string>
+ <string name="bluetooth_key_missing_subtext" msgid="1003639333895028298">"Ayikwazi ukuxhuma"</string>
<string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Idivayisi yomsindo enentambo"</string>
<string name="help_label" msgid="3528360748637781274">"Usizo nempendulo"</string>
<string name="storage_category" msgid="2287342585424631813">"Isitoreji"</string>
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index 1297aa3ff7d5..6a7e048b577a 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -288,7 +288,7 @@
<string name="bluetooth_profile_hid">Input device</string>
<!-- Bluetooth settings. The user-visible string that is used whenever referring to the PAN profile (accessing Internet through remote device). [CHAR LIMIT=40] -->
<string name="bluetooth_profile_pan">Internet access</string>
- <!-- Bluetooth settings. The user-visible string that is used whenever referring to the PBAP profile. [CHAR LIMIT=40] -->
+ <!-- Bluetooth settings. The user-visible string that is used whenever referring to the PBAP profile. [CHAR LIMIT=80] -->
<string name="bluetooth_profile_pbap">Allow access to contacts and call history</string>
<!-- Bluetooth settings. The user-visible summary string that is used whenever referring to the PBAP profile (sharing contacts). [CHAR LIMIT=60] -->
<string name="bluetooth_profile_pbap_summary">Info will be used for call announcements and more</string>
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/AmbientVolumeUiController.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/AmbientVolumeUiController.java
index e7ddc46093e3..f7da8a57410f 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/AmbientVolumeUiController.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/AmbientVolumeUiController.java
@@ -80,6 +80,7 @@ public class AmbientVolumeUiController implements
mLocalDataManager = new HearingDeviceLocalDataManager(context);
mLocalDataManager.setOnDeviceLocalDataChangeListener(this,
ThreadUtils.getBackgroundExecutor());
+ mLocalDataManager.start();
}
@VisibleForTesting
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
index 3625c002e9d8..7cdc13cb9189 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
@@ -36,6 +36,7 @@ import android.util.Log;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
+import androidx.collection.ArraySet;
import com.android.settingslib.R;
import com.android.settingslib.flags.Flags;
@@ -274,29 +275,37 @@ public class BluetoothEventManager {
@VisibleForTesting
void dispatchActiveDeviceChanged(
@Nullable CachedBluetoothDevice activeDevice, int bluetoothProfile) {
- CachedBluetoothDevice targetDevice = activeDevice;
+ CachedBluetoothDevice mainActiveDevice = activeDevice;
for (CachedBluetoothDevice cachedDevice : mDeviceManager.getCachedDevicesCopy()) {
- // should report isActive from main device or it will cause trouble to other callers.
CachedBluetoothDevice subDevice = cachedDevice.getSubDevice();
- CachedBluetoothDevice finalTargetDevice = targetDevice;
- if (targetDevice != null
- && ((subDevice != null && subDevice.equals(targetDevice))
- || cachedDevice.getMemberDevice().stream().anyMatch(
- memberDevice -> memberDevice.equals(finalTargetDevice)))) {
- Log.d(TAG,
- "The active device is the sub/member device "
- + targetDevice.getDevice().getAnonymizedAddress()
- + ". change targetDevice as main device "
- + cachedDevice.getDevice().getAnonymizedAddress());
- targetDevice = cachedDevice;
+ Set<CachedBluetoothDevice> memberDevices = cachedDevice.getMemberDevice();
+ final Set<CachedBluetoothDevice> cachedDevices = new ArraySet<>();
+ cachedDevices.add(cachedDevice);
+ if (!memberDevices.isEmpty()) {
+ cachedDevices.addAll(memberDevices);
+ } else if (subDevice != null) {
+ cachedDevices.add(subDevice);
+ }
+
+ // should report isActive from main device or it will cause trouble to other callers.
+ if (activeDevice != null
+ && (cachedDevices.stream().anyMatch(
+ device -> device.equals(activeDevice)))) {
+ Log.d(TAG, "The active device is in the set, report main device as active device:"
+ + cachedDevice.getDevice() + ", active device:" + activeDevice.getDevice());
+ mainActiveDevice = cachedDevice;
}
- boolean isActiveDevice = cachedDevice.equals(targetDevice);
- cachedDevice.onActiveDeviceChanged(isActiveDevice, bluetoothProfile);
+ boolean isActiveDevice = cachedDevice.equals(mainActiveDevice);
+ cachedDevices.forEach(
+ device -> device.onActiveDeviceChanged(isActiveDevice, bluetoothProfile));
+ //TODO: b/400440223 - Check if we can call DeviceManager.onActiveDeviceChanged &
+ // Callback.onActiveDeviceChanged for cachedDevices Set also, so we don't need to report
+ // isActive from main device.
mDeviceManager.onActiveDeviceChanged(cachedDevice);
}
for (BluetoothCallback callback : mCallbacks) {
- callback.onActiveDeviceChanged(targetDevice, bluetoothProfile);
+ callback.onActiveDeviceChanged(mainActiveDevice, bluetoothProfile);
}
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java
index ae9ad958b287..33dcb051d194 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java
@@ -1068,18 +1068,42 @@ public class BluetoothUtils {
/** Get primary device Uri in broadcast. */
@NonNull
public static String getPrimaryGroupIdUriForBroadcast() {
+ // TODO: once API is stable, deprecate SettingsProvider solution
return "bluetooth_le_broadcast_fallback_active_group_id";
}
- /** Get primary device group id in broadcast. */
+ /** Get primary device group id in broadcast from SettingsProvider. */
@WorkerThread
public static int getPrimaryGroupIdForBroadcast(@NonNull ContentResolver contentResolver) {
+ // TODO: once API is stable, deprecate SettingsProvider solution
return Settings.Secure.getInt(
contentResolver,
getPrimaryGroupIdUriForBroadcast(),
BluetoothCsipSetCoordinator.GROUP_ID_INVALID);
}
+ /**
+ * Get primary device group id in broadcast.
+ *
+ * If Flags.adoptPrimaryGroupManagementApiV2 is enabled, get group id by API,
+ * Otherwise, still get value from SettingsProvider.
+ */
+ @WorkerThread
+ public static int getPrimaryGroupIdForBroadcast(@NonNull ContentResolver contentResolver,
+ @Nullable LocalBluetoothManager manager) {
+ if (Flags.adoptPrimaryGroupManagementApiV2()) {
+ LeAudioProfile leaProfile = manager == null ? null :
+ manager.getProfileManager().getLeAudioProfile();
+ if (leaProfile == null) {
+ Log.d(TAG, "getPrimaryGroupIdForBroadcast: profile is null");
+ return BluetoothCsipSetCoordinator.GROUP_ID_INVALID;
+ }
+ return leaProfile.getBroadcastToUnicastFallbackGroup();
+ } else {
+ return getPrimaryGroupIdForBroadcast(contentResolver);
+ }
+ }
+
/** Get develop option value for audio sharing preview. */
@WorkerThread
public static boolean getAudioSharingPreviewValue(@Nullable ContentResolver contentResolver) {
@@ -1101,7 +1125,7 @@ public class BluetoothUtils {
LocalBluetoothLeBroadcast broadcast =
localBtManager.getProfileManager().getLeAudioBroadcastProfile();
if (broadcast == null || !broadcast.isEnabled(null)) return null;
- int primaryGroupId = getPrimaryGroupIdForBroadcast(contentResolver);
+ int primaryGroupId = getPrimaryGroupIdForBroadcast(contentResolver, localBtManager);
if (primaryGroupId == BluetoothCsipSetCoordinator.GROUP_ID_INVALID) return null;
LocalBluetoothLeBroadcastAssistant assistant =
localBtManager.getProfileManager().getLeAudioBroadcastAssistantProfile();
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
index 011b2fc15807..edec2e427315 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
@@ -75,6 +75,7 @@ import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.Executor;
+import java.util.stream.IntStream;
import java.util.stream.Stream;
/**
@@ -154,8 +155,8 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice>
private boolean mIsLeAudioProfileConnectedFail = false;
private boolean mUnpairing;
@Nullable
- private final InputDevice mInputDevice;
- private final boolean mIsDeviceStylus;
+ private InputDevice mInputDevice;
+ private boolean mIsDeviceStylus;
// Group second device for Hearing Aid
private CachedBluetoothDevice mSubDevice;
@@ -313,8 +314,7 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice>
mLocalNapRoleConnected = true;
}
}
- if (Flags.enableSetPreferredTransportForLeAudioDevice()
- && profile instanceof HidProfile) {
+ if (profile instanceof HidProfile) {
updatePreferredTransport();
}
} else if (profile instanceof MapProfile
@@ -329,8 +329,7 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice>
mLocalNapRoleConnected = false;
}
- if (Flags.enableSetPreferredTransportForLeAudioDevice()
- && profile instanceof LeAudioProfile) {
+ if (profile instanceof LeAudioProfile) {
updatePreferredTransport();
}
@@ -762,11 +761,8 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice>
* {@link BluetoothDevice#BATTERY_LEVEL_UNKNOWN}
*/
public int getMinBatteryLevelWithMemberDevices() {
- return Stream.concat(Stream.of(this), mMemberDevices.stream())
- .mapToInt(cachedDevice -> cachedDevice.getBatteryLevel())
- .filter(batteryLevel -> batteryLevel > BluetoothDevice.BATTERY_LEVEL_UNKNOWN)
- .min()
- .orElse(BluetoothDevice.BATTERY_LEVEL_UNKNOWN);
+ return getMinBatteryLevels(Stream.concat(Stream.of(this), mMemberDevices.stream())
+ .mapToInt(CachedBluetoothDevice::getBatteryLevel));
}
/**
@@ -789,6 +785,13 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice>
: null;
}
+ private int getMinBatteryLevels(IntStream batteryLevels) {
+ return batteryLevels
+ .filter(battery -> battery > BluetoothDevice.BATTERY_LEVEL_UNKNOWN)
+ .min()
+ .orElse(BluetoothDevice.BATTERY_LEVEL_UNKNOWN);
+ }
+
void refresh() {
ListenableFuture<Void> future = ThreadUtils.getBackgroundExecutor().submit(() -> {
if (BluetoothUtils.isAdvancedDetailsHeader(mDevice)) {
@@ -1358,7 +1361,7 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice>
// Gets summary for the buds which are in the audio sharing.
int groupId = BluetoothUtils.getGroupId(this);
int primaryGroupId = BluetoothUtils.getPrimaryGroupIdForBroadcast(
- mContext.getContentResolver());
+ mContext.getContentResolver(), mBluetoothManager);
if ((primaryGroupId != BluetoothCsipSetCoordinator.GROUP_ID_INVALID)
? (groupId == primaryGroupId) : isActiveDevice(BluetoothProfile.LE_AUDIO)) {
// The buds are primary buds
@@ -1674,10 +1677,8 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice>
return null;
} else {
int overallBattery =
- Arrays.stream(new int[]{leftBattery, rightBattery, caseBattery})
- .filter(battery -> battery > BluetoothDevice.BATTERY_LEVEL_UNKNOWN)
- .min()
- .orElse(BluetoothDevice.BATTERY_LEVEL_UNKNOWN);
+ getMinBatteryLevels(
+ Arrays.stream(new int[]{leftBattery, rightBattery, caseBattery}));
Log.d(TAG, "Acquired battery info from metadata for untethered device "
+ mDevice.getAnonymizedAddress()
+ " left earbud battery: " + leftBattery
@@ -1711,10 +1712,75 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice>
@Nullable
private BatteryLevelsInfo getBatteryFromBluetoothService() {
- // TODO(b/397847825): Implement the logic to get battery from Bluetooth service.
- return null;
+ BatteryLevelsInfo batteryLevelsInfo;
+ if (isConnectedHearingAidDevice()) {
+ // If the device is hearing aid device, sides can be distinguished by HearingAidInfo.
+ batteryLevelsInfo = getBatteryOfHearingAidDeviceComponents();
+ if (batteryLevelsInfo != null) {
+ return batteryLevelsInfo;
+ }
+ }
+ if (isConnectedLeAudioDevice()) {
+ // If the device is LE Audio device, sides can be distinguished by LeAudioProfile.
+ batteryLevelsInfo = getBatteryOfLeAudioDeviceComponents();
+ if (batteryLevelsInfo != null) {
+ return batteryLevelsInfo;
+ }
+ }
+ int overallBattery = getMinBatteryLevelWithMemberDevices();
+ return overallBattery > BluetoothDevice.BATTERY_LEVEL_UNKNOWN
+ ? new BatteryLevelsInfo(
+ BluetoothDevice.BATTERY_LEVEL_UNKNOWN,
+ BluetoothDevice.BATTERY_LEVEL_UNKNOWN,
+ BluetoothDevice.BATTERY_LEVEL_UNKNOWN,
+ overallBattery)
+ : null;
}
+ @Nullable
+ private BatteryLevelsInfo getBatteryOfHearingAidDeviceComponents() {
+ if (getDeviceSide() == HearingAidInfo.DeviceSide.SIDE_LEFT_AND_RIGHT) {
+ return new BatteryLevelsInfo(
+ BluetoothDevice.BATTERY_LEVEL_UNKNOWN,
+ BluetoothDevice.BATTERY_LEVEL_UNKNOWN,
+ BluetoothDevice.BATTERY_LEVEL_UNKNOWN,
+ mDevice.getBatteryLevel());
+ }
+
+ int leftBattery = getHearingAidSideBattery(HearingAidInfo.DeviceSide.SIDE_LEFT);
+ int rightBattery = getHearingAidSideBattery(HearingAidInfo.DeviceSide.SIDE_RIGHT);
+ int overallBattery = getMinBatteryLevels(
+ Arrays.stream(new int[]{leftBattery, rightBattery}));
+
+ Log.d(TAG, "Acquired battery info from Bluetooth service for hearing aid device "
+ + mDevice.getAnonymizedAddress()
+ + " left battery: " + leftBattery
+ + " right battery: " + rightBattery
+ + " overall battery: " + overallBattery);
+ return overallBattery > BluetoothDevice.BATTERY_LEVEL_UNKNOWN
+ ? new BatteryLevelsInfo(
+ leftBattery,
+ rightBattery,
+ BluetoothDevice.BATTERY_LEVEL_UNKNOWN,
+ overallBattery)
+ : null;
+ }
+
+ private int getHearingAidSideBattery(int side) {
+ Optional<CachedBluetoothDevice> connectedHearingAidSide = getConnectedHearingAidSide(side);
+ return connectedHearingAidSide.isPresent()
+ ? connectedHearingAidSide
+ .map(CachedBluetoothDevice::getBatteryLevel)
+ .filter(batteryLevel -> batteryLevel > BluetoothDevice.BATTERY_LEVEL_UNKNOWN)
+ .orElse(BluetoothDevice.BATTERY_LEVEL_UNKNOWN)
+ : BluetoothDevice.BATTERY_LEVEL_UNKNOWN;
+ }
+
+ @Nullable
+ private BatteryLevelsInfo getBatteryOfLeAudioDeviceComponents() {
+ // TODO(b/397847825): Implement the logic to get battery of LE audio device components.
+ return null;
+ }
private CharSequence getTvBatterySummary(int mainBattery, int leftBattery, int rightBattery,
int lowBatteryColorRes) {
// Since there doesn't seem to be a way to use format strings to add the
@@ -1833,10 +1899,7 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice>
// Retrieve hearing aids (ASHA, HAP) individual side battery level
if (leftBattery == BluetoothDevice.BATTERY_LEVEL_UNKNOWN) {
- leftBattery = getConnectedHearingAidSide(HearingAidInfo.DeviceSide.SIDE_LEFT)
- .map(CachedBluetoothDevice::getBatteryLevel)
- .filter(batteryLevel -> batteryLevel > BluetoothDevice.BATTERY_LEVEL_UNKNOWN)
- .orElse(BluetoothDevice.BATTERY_LEVEL_UNKNOWN);
+ leftBattery = getHearingAidSideBattery(HearingAidInfo.DeviceSide.SIDE_LEFT);
}
return leftBattery;
@@ -1852,10 +1915,7 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice>
// Retrieve hearing aids (ASHA, HAP) individual side battery level
if (rightBattery == BluetoothDevice.BATTERY_LEVEL_UNKNOWN) {
- rightBattery = getConnectedHearingAidSide(HearingAidInfo.DeviceSide.SIDE_RIGHT)
- .map(CachedBluetoothDevice::getBatteryLevel)
- .filter(batteryLevel -> batteryLevel > BluetoothDevice.BATTERY_LEVEL_UNKNOWN)
- .orElse(BluetoothDevice.BATTERY_LEVEL_UNKNOWN);
+ rightBattery = getHearingAidSideBattery(HearingAidInfo.DeviceSide.SIDE_RIGHT);
}
return rightBattery;
@@ -2263,6 +2323,16 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice>
mBluetoothManager = bluetoothManager;
}
+ @VisibleForTesting
+ void setIsDeviceStylus(Boolean isDeviceStylus) {
+ mIsDeviceStylus = isDeviceStylus;
+ }
+
+ @VisibleForTesting
+ void setInputDevice(@Nullable InputDevice inputDevice) {
+ mInputDevice = inputDevice;
+ }
+
private boolean isAndroidAuto() {
try {
ParcelUuid[] uuids = mDevice.getUuids();
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CsipDeviceManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CsipDeviceManager.java
index bf86911ee683..f2c013598cdc 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CsipDeviceManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CsipDeviceManager.java
@@ -431,7 +431,10 @@ public class CsipDeviceManager {
for (BluetoothDevice device : sinksToSync) {
log("addMemberDevicesIntoMainDevice: sync audio sharing source to "
+ device.getAnonymizedAddress());
- assistant.addSource(device, metadata, /* isGroupOp= */ false);
+ if (assistant.getConnectionStatus(device)
+ == BluetoothProfile.STATE_CONNECTED) {
+ assistant.addSource(device, metadata, /* isGroupOp= */ false);
+ }
}
}
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LeAudioProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LeAudioProfile.java
index 155c7e6530aa..e46574cef917 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LeAudioProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LeAudioProfile.java
@@ -37,9 +37,11 @@ import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import com.android.settingslib.R;
+import com.android.settingslib.flags.Flags;
import java.util.ArrayList;
import java.util.List;
+import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executor;
public class LeAudioProfile implements LocalBluetoothProfile {
@@ -59,6 +61,10 @@ public class LeAudioProfile implements LocalBluetoothProfile {
// Order of this profile in device profiles list
private static final int ORDINAL = 1;
+ // Cached callbacks being registered before service is connected.
+ private ConcurrentHashMap<BluetoothLeAudio.Callback, Executor>
+ mCachedCallbackExecutorMap = new ConcurrentHashMap<>();
+
// These callbacks run on the main thread.
private final class LeAudioServiceListener implements BluetoothProfile.ServiceListener {
@@ -88,7 +94,19 @@ public class LeAudioProfile implements LocalBluetoothProfile {
// Check current list of CachedDevices to see if any are hearing aid devices.
mDeviceManager.updateHearingAidsDevices();
mProfileManager.callServiceConnectedListeners();
- mIsProfileReady = true;
+ if (!mIsProfileReady) {
+ mIsProfileReady = true;
+ if (Flags.adoptPrimaryGroupManagementApiV2()) {
+ if (DEBUG) {
+ Log.d(
+ TAG,
+ "onServiceConnected, register mCachedCallbackExecutorMap = "
+ + mCachedCallbackExecutorMap);
+ }
+ mCachedCallbackExecutorMap.forEach(
+ (callback, executor) -> registerCallback(executor, callback));
+ }
+ }
}
public void onServiceDisconnected(int profile) {
@@ -96,7 +114,12 @@ public class LeAudioProfile implements LocalBluetoothProfile {
Log.d(TAG, "Bluetooth service disconnected");
}
mProfileManager.callServiceDisconnectedListeners();
- mIsProfileReady = false;
+ if (mIsProfileReady) {
+ mIsProfileReady = false;
+ if (Flags.adoptPrimaryGroupManagementApiV2()) {
+ mCachedCallbackExecutorMap.clear();
+ }
+ }
}
}
@@ -367,6 +390,9 @@ public class LeAudioProfile implements LocalBluetoothProfile {
@NonNull BluetoothLeAudio.Callback callback) {
if (mService == null) {
Log.w(TAG, "Proxy not attached to service. Cannot register callback.");
+ if (Flags.adoptPrimaryGroupManagementApiV2()) {
+ mCachedCallbackExecutorMap.putIfAbsent(callback, executor);
+ }
return;
}
mService.registerCallback(executor, callback);
@@ -384,6 +410,9 @@ public class LeAudioProfile implements LocalBluetoothProfile {
* callback is registered
*/
public void unregisterCallback(@NonNull BluetoothLeAudio.Callback callback) {
+ if (Flags.adoptPrimaryGroupManagementApiV2()) {
+ mCachedCallbackExecutorMap.remove(callback);
+ }
if (mService == null) {
Log.w(TAG, "Proxy not attached to service. Cannot unregister callback.");
return;
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcast.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcast.java
index 01d8694256f3..b0f379605f5e 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcast.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcast.java
@@ -19,6 +19,9 @@ package com.android.settingslib.bluetooth;
import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
import static com.android.settingslib.Utils.isAudioModeOngoingCall;
+import static com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant.LocalBluetoothLeBroadcastSourceState.DECRYPTION_FAILED;
+import static com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant.LocalBluetoothLeBroadcastSourceState.PAUSED;
+import static com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant.LocalBluetoothLeBroadcastSourceState.STREAMING;
import static java.util.stream.Collectors.toList;
@@ -70,9 +73,11 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
+import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
@@ -138,6 +143,8 @@ public class LocalBluetoothLeBroadcast implements LocalBluetoothProfile {
};
private final Context mContext;
private final CachedBluetoothDeviceManager mDeviceManager;
+ private final boolean mHysteresisModeFixAvailable;
+ private final boolean mIsWorkProfile;
private BluetoothLeBroadcast mServiceBroadcast;
private BluetoothLeBroadcastAssistant mServiceBroadcastAssistant;
private BluetoothLeAudioContentMetadata mBluetoothLeAudioContentMetadata;
@@ -158,6 +165,7 @@ public class LocalBluetoothLeBroadcast implements LocalBluetoothProfile {
// Cached broadcast callbacks being register before service is connected.
private ConcurrentHashMap<BluetoothLeBroadcast.Callback, Executor>
mCachedBroadcastCallbackExecutorMap = new ConcurrentHashMap<>();
+ private Set<BluetoothDevice> mLocalSinksPendingSourceRemoval = new HashSet<>();
private final ServiceListener mServiceListener =
new ServiceListener() {
@@ -376,6 +384,7 @@ public class LocalBluetoothLeBroadcast implements LocalBluetoothProfile {
+ ", sourceId = "
+ sourceId);
}
+ mLocalSinksPendingSourceRemoval.remove(sink);
}
@Override
@@ -408,6 +417,35 @@ public class LocalBluetoothLeBroadcast implements LocalBluetoothProfile {
+ ", state = "
+ state);
}
+ if (!Flags.audioStreamMediaServiceByReceiveState()) {
+ Log.d(TAG, "Skip notifyPrivateBroadcastReceived, flag off.");
+ return;
+ }
+ if (mIsWorkProfile) {
+ Log.d(TAG, "Skip notifyPrivateBroadcastReceived for work profile.");
+ return;
+ }
+ if (state.getBroadcastId() == mBroadcastId
+ || !mLocalSinksPendingSourceRemoval.isEmpty()) {
+ Log.d(TAG,
+ "Skip notifyPrivateBroadcastReceived, onReceiveStateChanged "
+ + "triggered by personal audio sharing.");
+ return;
+ }
+ var sourceState = LocalBluetoothLeBroadcastAssistant.getLocalSourceState(state);
+ if (sourceState == STREAMING || sourceState == DECRYPTION_FAILED
+ || (mHysteresisModeFixAvailable && sourceState == PAUSED)) {
+ List<BluetoothLeAudioContentMetadata> subgroupMetadata =
+ state.getSubgroupMetadata();
+ String programInfo = subgroupMetadata.isEmpty() ? ""
+ : subgroupMetadata.getFirst().getProgramInfo();
+ notifyPrivateBroadcastReceived(
+ sink,
+ sourceId,
+ state.getBroadcastId(),
+ programInfo == null ? "" : programInfo,
+ sourceState);
+ }
}
};
@@ -439,6 +477,10 @@ public class LocalBluetoothLeBroadcast implements LocalBluetoothProfile {
BluetoothAdapter.getDefaultAdapter()
.getProfileProxy(
context, mServiceListener, BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT);
+
+ mHysteresisModeFixAvailable = BluetoothUtils.isAudioSharingHysteresisModeFixAvailable(
+ context);
+ mIsWorkProfile = isWorkProfile(mContext);
}
/**
@@ -1138,6 +1180,7 @@ public class LocalBluetoothLeBroadcast implements LocalBluetoothProfile {
int localBroadcastId = getLatestBroadcastId();
if (receiveState.getBroadcastId() != localBroadcastId) continue;
+ mLocalSinksPendingSourceRemoval.add(device);
mServiceBroadcastAssistant.removeSource(device, receiveState.getSourceId());
}
}
@@ -1151,7 +1194,7 @@ public class LocalBluetoothLeBroadcast implements LocalBluetoothProfile {
Log.d(TAG, "Skip updateFallbackActiveDeviceIfNeeded, disable flag is on");
return;
}
- if (isWorkProfile(mContext)) {
+ if (mIsWorkProfile) {
Log.d(TAG, "Skip updateFallbackActiveDeviceIfNeeded for work profile.");
return;
}
@@ -1279,7 +1322,7 @@ public class LocalBluetoothLeBroadcast implements LocalBluetoothProfile {
Log.d(TAG, "Skip notifyBroadcastStateChange, not triggered by Settings or SystemUI.");
return;
}
- if (isWorkProfile(mContext)) {
+ if (mIsWorkProfile) {
Log.d(TAG, "Skip notifyBroadcastStateChange, not triggered for work profile.");
return;
}
@@ -1290,6 +1333,26 @@ public class LocalBluetoothLeBroadcast implements LocalBluetoothProfile {
mContext.sendBroadcast(intent);
}
+ private void notifyPrivateBroadcastReceived(BluetoothDevice sink, int sourceId, int broadcastId,
+ String programInfo,
+ LocalBluetoothLeBroadcastAssistant.LocalBluetoothLeBroadcastSourceState state) {
+ String packageName = mContext.getPackageName();
+ if (!packageName.equals(SYSUI_PKG)) {
+ Log.d(TAG, "Skip notifyPrivateBroadcastReceived, not triggered by SystemUI.");
+ return;
+ }
+ var data = new PrivateBroadcastReceiveData(sink, sourceId, broadcastId, programInfo, state);
+ Intent intent = new Intent(ACTION_LE_AUDIO_PRIVATE_BROADCAST_RECEIVED);
+ intent.putExtra(EXTRA_PRIVATE_BROADCAST_RECEIVE_DATA, data);
+ intent.setPackage(SETTINGS_PKG);
+ Log.d(TAG,
+ "notifyPrivateBroadcastReceived for sink = " + sink + " with sourceId = " + sourceId
+ + " state = " + state
+ + " programInfo =" + programInfo
+ + " broadcastId = " + broadcastId);
+ mContext.sendBroadcast(intent);
+ }
+
private boolean isWorkProfile(Context context) {
UserManager userManager = context.getSystemService(UserManager.class);
return userManager != null && userManager.isManagedProfile();
diff --git a/packages/SettingsLib/src/com/android/settingslib/notification/data/repository/ZenModeRepository.kt b/packages/SettingsLib/src/com/android/settingslib/notification/data/repository/ZenModeRepository.kt
index c4e724554c04..21d518a644a9 100644
--- a/packages/SettingsLib/src/com/android/settingslib/notification/data/repository/ZenModeRepository.kt
+++ b/packages/SettingsLib/src/com/android/settingslib/notification/data/repository/ZenModeRepository.kt
@@ -27,7 +27,6 @@ import android.content.IntentFilter
import android.database.ContentObserver
import android.os.Handler
import android.provider.Settings
-import com.android.settingslib.flags.Flags
import com.android.settingslib.notification.modes.ZenMode
import com.android.settingslib.notification.modes.ZenModesBackend
import java.time.Duration
@@ -35,6 +34,7 @@ import kotlin.coroutines.CoroutineContext
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.callbackFlow
@@ -72,7 +72,7 @@ class ZenModeRepositoryImpl(
private val notificationManager: NotificationManager,
private val backend: ZenModesBackend,
private val contentResolver: ContentResolver,
- val scope: CoroutineScope,
+ val applicationScope: CoroutineScope,
val backgroundCoroutineContext: CoroutineContext,
// This is nullable just to simplify testing, since SettingsLib doesn't have a good way
// to create a fake handler.
@@ -104,7 +104,7 @@ class ZenModeRepositoryImpl(
awaitClose { context.unregisterReceiver(receiver) }
}
.flowOn(backgroundCoroutineContext)
- .shareIn(started = SharingStarted.WhileSubscribed(), scope = scope)
+ .shareIn(started = SharingStarted.WhileSubscribed(), scope = applicationScope)
}
override val consolidatedNotificationPolicy: StateFlow<NotificationManager.Policy?> by lazy {
@@ -129,14 +129,11 @@ class ZenModeRepositoryImpl(
.map { mapper(it) }
.onStart { emit(mapper(null)) }
.flowOn(backgroundCoroutineContext)
- .stateIn(scope, SharingStarted.WhileSubscribed(), null)
+ .stateIn(applicationScope, SharingStarted.WhileSubscribed(), null)
private val zenConfigChanged by lazy {
if (android.app.Flags.modesUi()) {
callbackFlow {
- // emit an initial value
- trySend(Unit)
-
val observer =
object : ContentObserver(backgroundHandler) {
override fun onChange(selfChange: Boolean) {
@@ -163,16 +160,18 @@ class ZenModeRepositoryImpl(
}
}
- override val modes: Flow<List<ZenMode>> by lazy {
- if (android.app.Flags.modesUi()) {
+ override val modes: StateFlow<List<ZenMode>> =
+ if (android.app.Flags.modesUi())
zenConfigChanged
.map { backend.modes }
.distinctUntilChanged()
.flowOn(backgroundCoroutineContext)
- } else {
- flowOf(emptyList())
- }
- }
+ .stateIn(
+ scope = applicationScope,
+ started = SharingStarted.Eagerly,
+ initialValue = backend.modes,
+ )
+ else MutableStateFlow<List<ZenMode>>(emptyList())
/**
* Gets the current list of [ZenMode] instances according to the backend.
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/devicestate/DeviceStateAutoRotateSettingManagerImplTest.kt b/packages/SettingsLib/tests/integ/src/com/android/settingslib/devicestate/DeviceStateAutoRotateSettingManagerImplTest.kt
index 78dba57028ba..a9329ba3c76c 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/devicestate/DeviceStateAutoRotateSettingManagerImplTest.kt
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/devicestate/DeviceStateAutoRotateSettingManagerImplTest.kt
@@ -181,8 +181,10 @@ class DeviceStateAutoRotateSettingManagerImplTest {
@Test
fun getAutoRotateSetting_forInvalidPosture_returnsSettingForFallbackPosture() {
- persistSettings(DEVICE_STATE_ROTATION_KEY_UNFOLDED, DEVICE_STATE_ROTATION_LOCK_UNLOCKED)
- persistSettings(DEVICE_STATE_ROTATION_KEY_FOLDED, DEVICE_STATE_ROTATION_LOCK_LOCKED)
+ persistSettings(
+ "$DEVICE_STATE_ROTATION_KEY_FOLDED:$DEVICE_STATE_ROTATION_LOCK_LOCKED:" +
+ "$DEVICE_STATE_ROTATION_KEY_UNFOLDED:$DEVICE_STATE_ROTATION_LOCK_UNLOCKED"
+ )
val autoRotateSetting = settingManager.getRotationLockSetting(DEVICE_STATE_HALF_FOLDED)
@@ -276,7 +278,6 @@ class DeviceStateAutoRotateSettingManagerImplTest {
SettableDeviceState(DEVICE_STATE_ROTATION_KEY_FOLDED, isSettable = true),
SettableDeviceState(DEVICE_STATE_ROTATION_KEY_HALF_FOLDED, isSettable = false),
SettableDeviceState(DEVICE_STATE_ROTATION_KEY_REAR_DISPLAY, isSettable = false),
- SettableDeviceState(DEVICE_STATE_ROTATION_LOCK_IGNORED, isSettable = false),
)
}
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/devicestate/DeviceStateAutoRotateSettingManagerProviderTest.kt b/packages/SettingsLib/tests/integ/src/com/android/settingslib/devicestate/DeviceStateAutoRotateSettingManagerProviderTest.kt
new file mode 100644
index 000000000000..c3ec4edfdee5
--- /dev/null
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/devicestate/DeviceStateAutoRotateSettingManagerProviderTest.kt
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2025 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.settingslib.devicestate
+
+import android.content.Context
+import android.os.Handler
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
+import android.platform.test.flag.junit.SetFlagsRule
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.window.flags.Flags
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.any
+import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.Mock
+import org.mockito.junit.MockitoJUnit
+import java.util.concurrent.Executor
+import org.mockito.Mockito.`when` as whenever
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class DeviceStateAutoRotateSettingManagerProviderTest {
+
+ @get:Rule
+ val setFlagsRule: SetFlagsRule = SetFlagsRule()
+
+ @get:Rule val rule = MockitoJUnit.rule()
+
+ private val context: Context = ApplicationProvider.getApplicationContext()
+
+ @Mock
+ private lateinit var mockExecutor: Executor
+
+ @Mock
+ private lateinit var mockSecureSettings: SecureSettings
+
+ @Mock
+ private lateinit var mockMainHandler: Handler
+
+ @Mock
+ private lateinit var mockPosturesHelper: PosturesHelper
+
+ @Before
+ fun setup() {
+ whenever(mockSecureSettings.getStringForUser(any(), anyInt())).thenReturn("")
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_DEVICE_STATE_AUTO_ROTATE_SETTING_REFACTOR)
+ fun createInstance_refactorFlagEnabled_returnsRefactoredManager() {
+ val manager =
+ DeviceStateAutoRotateSettingManagerProvider.createInstance(
+ context, mockExecutor, mockSecureSettings, mockMainHandler, mockPosturesHelper
+ )
+
+ assertThat(manager).isInstanceOf(DeviceStateAutoRotateSettingManagerImpl::class.java)
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_ENABLE_DEVICE_STATE_AUTO_ROTATE_SETTING_REFACTOR)
+ fun createInstance_refactorFlagDisabled_returnsLegacyManager() {
+ val manager =
+ DeviceStateAutoRotateSettingManagerProvider.createInstance(
+ context, mockExecutor, mockSecureSettings, mockMainHandler, mockPosturesHelper
+ )
+
+ assertThat(manager).isInstanceOf(DeviceStateRotationLockSettingsManager::class.java)
+ }
+}
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/devicestate/DeviceStateRotationLockSettingsManagerTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/devicestate/DeviceStateRotationLockSettingsManagerTest.java
index baebaf7dfef0..b23ea5f1786f 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/devicestate/DeviceStateRotationLockSettingsManagerTest.java
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/devicestate/DeviceStateRotationLockSettingsManagerTest.java
@@ -151,8 +151,8 @@ public class DeviceStateRotationLockSettingsManagerTest {
new String[]{"2:1", "1:0:1", "0:2"});
List<SettableDeviceState> settableDeviceStates =
- DeviceStateRotationLockSettingsManager.getInstance(
- mMockContext).getSettableDeviceStates();
+ new DeviceStateRotationLockSettingsManager(mMockContext,
+ mFakeSecureSettings).getSettableDeviceStates();
assertThat(settableDeviceStates).containsExactly(
new SettableDeviceState(/* deviceState= */ 2, /* isSettable= */ true),
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothEventManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothEventManagerTest.java
index eac6923473b1..2620174e5d39 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothEventManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothEventManagerTest.java
@@ -70,6 +70,9 @@ public class BluetoothEventManagerTest {
public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
private static final String DEVICE_NAME = "test_device_name";
+ private static final String DEVICE_ADDRESS_1 = "AA:BB:CC:DD:EE:11";
+ private static final String DEVICE_ADDRESS_2 = "AA:BB:CC:DD:EE:22";
+ private static final String DEVICE_ADDRESS_3 = "AA:BB:CC:DD:EE:33";
@Mock
private LocalBluetoothAdapter mLocalAdapter;
@@ -132,6 +135,9 @@ public class BluetoothEventManagerTest {
when(mA2dpProfile.isProfileReady()).thenReturn(true);
when(mHearingAidProfile.isProfileReady()).thenReturn(true);
when(mLeAudioProfile.isProfileReady()).thenReturn(true);
+ when(mDevice1.getAddress()).thenReturn(DEVICE_ADDRESS_1);
+ when(mDevice2.getAddress()).thenReturn(DEVICE_ADDRESS_2);
+ when(mDevice3.getAddress()).thenReturn(DEVICE_ADDRESS_3);
mCachedDevice1 = new CachedBluetoothDevice(mContext, mLocalProfileManager, mDevice1);
mCachedDevice2 = new CachedBluetoothDevice(mContext, mLocalProfileManager, mDevice2);
mCachedDevice3 = new CachedBluetoothDevice(mContext, mLocalProfileManager, mDevice3);
@@ -515,7 +521,6 @@ public class BluetoothEventManagerTest {
cachedDevices.add(mCachedDevice2);
int group1 = 1;
- when(mDevice3.getAddress()).thenReturn("testAddress3");
mCachedDevice1.setGroupId(group1);
mCachedDevice3.setGroupId(group1);
mCachedDevice1.addMemberDevice(mCachedDevice3);
@@ -620,18 +625,32 @@ public class BluetoothEventManagerTest {
}
@Test
- public void dispatchActiveDeviceChanged_activeFromSubDevice_mainCachedDeviceActive() {
+ public void dispatchActiveDeviceChanged_activeFromSubDevice_bothCachedDevicesActive() {
CachedBluetoothDevice subDevice = new CachedBluetoothDevice(mContext, mLocalProfileManager,
mDevice3);
mCachedDevice1.setSubDevice(subDevice);
when(mCachedDeviceManager.getCachedDevicesCopy()).thenReturn(
Collections.singletonList(mCachedDevice1));
- mCachedDevice1.onProfileStateChanged(mHearingAidProfile,
- BluetoothProfile.STATE_CONNECTED);
+ mCachedDevice1.onProfileStateChanged(mHearingAidProfile, BluetoothProfile.STATE_CONNECTED);
- assertThat(mCachedDevice1.isActiveDevice(BluetoothProfile.HEARING_AID)).isFalse();
mBluetoothEventManager.dispatchActiveDeviceChanged(subDevice, BluetoothProfile.HEARING_AID);
+
assertThat(mCachedDevice1.isActiveDevice(BluetoothProfile.HEARING_AID)).isTrue();
+ assertThat(subDevice.isActiveDevice(BluetoothProfile.HEARING_AID)).isTrue();
+ }
+
+ @Test
+ public void dispatchActiveDeviceChanged_activeFromMemberDevice_allCachedDevicesActive() {
+ mCachedDevice1.addMemberDevice(mCachedDevice2);
+ when(mCachedDeviceManager.getCachedDevicesCopy()).thenReturn(
+ Collections.singletonList(mCachedDevice1));
+ mCachedDevice1.onProfileStateChanged(mLeAudioProfile, BluetoothProfile.STATE_CONNECTED);
+
+ mBluetoothEventManager.dispatchActiveDeviceChanged(mCachedDevice2,
+ BluetoothProfile.LE_AUDIO);
+
+ assertThat(mCachedDevice1.isActiveDevice(BluetoothProfile.LE_AUDIO)).isTrue();
+ assertThat(mCachedDevice2.isActiveDevice(BluetoothProfile.LE_AUDIO)).isTrue();
}
@Test
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java
index b7814127b716..8fc4aa81b53f 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java
@@ -936,15 +936,60 @@ public class BluetoothUtilsTest {
}
@Test
+ @EnableFlags(Flags.FLAG_ADOPT_PRIMARY_GROUP_MANAGEMENT_API_V2)
+ public void getSecondaryDeviceForBroadcast_adoptAPI_noSecondary_returnNull() {
+ when(mBroadcast.isEnabled(any())).thenReturn(true);
+ when(mLeAudioProfile.getBroadcastToUnicastFallbackGroup()).thenReturn(1);
+ when(mDeviceManager.findDevice(mBluetoothDevice)).thenReturn(mCachedBluetoothDevice);
+ when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice);
+ when(mCachedBluetoothDevice.getGroupId()).thenReturn(1);
+ BluetoothLeBroadcastReceiveState state = mock(BluetoothLeBroadcastReceiveState.class);
+ when(mAssistant.getAllSources(mBluetoothDevice)).thenReturn(ImmutableList.of(state));
+ when(mAssistant.getAllConnectedDevices()).thenReturn(ImmutableList.of(mBluetoothDevice));
+
+ assertThat(
+ BluetoothUtils.getSecondaryDeviceForBroadcast(
+ mContext.getContentResolver(), mLocalBluetoothManager))
+ .isNull();
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ADOPT_PRIMARY_GROUP_MANAGEMENT_API_V2)
+ public void getSecondaryDeviceForBroadcast_adoptAPI_returnCorrectDevice() {
+ when(mBroadcast.isEnabled(any())).thenReturn(true);
+ when(mLeAudioProfile.getBroadcastToUnicastFallbackGroup()).thenReturn(1);
+ CachedBluetoothDevice cachedBluetoothDevice = mock(CachedBluetoothDevice.class);
+ BluetoothDevice bluetoothDevice = mock(BluetoothDevice.class);
+ when(cachedBluetoothDevice.getDevice()).thenReturn(bluetoothDevice);
+ when(cachedBluetoothDevice.getGroupId()).thenReturn(1);
+ when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice);
+ when(mCachedBluetoothDevice.getGroupId()).thenReturn(2);
+ when(mDeviceManager.findDevice(bluetoothDevice)).thenReturn(cachedBluetoothDevice);
+ when(mDeviceManager.findDevice(mBluetoothDevice)).thenReturn(mCachedBluetoothDevice);
+ BluetoothLeBroadcastReceiveState state = mock(BluetoothLeBroadcastReceiveState.class);
+ List<Long> bisSyncState = new ArrayList<>();
+ bisSyncState.add(1L);
+ when(state.getBisSyncState()).thenReturn(bisSyncState);
+ when(mAssistant.getAllSources(any(BluetoothDevice.class)))
+ .thenReturn(ImmutableList.of(state));
+ when(mAssistant.getAllConnectedDevices())
+ .thenReturn(ImmutableList.of(mBluetoothDevice, bluetoothDevice));
+
+ assertThat(
+ BluetoothUtils.getSecondaryDeviceForBroadcast(
+ mContext.getContentResolver(), mLocalBluetoothManager))
+ .isEqualTo(mCachedBluetoothDevice);
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_ADOPT_PRIMARY_GROUP_MANAGEMENT_API_V2)
public void getSecondaryDeviceForBroadcast_noSecondary_returnNull() {
Settings.Secure.putInt(
mContext.getContentResolver(),
BluetoothUtils.getPrimaryGroupIdUriForBroadcast(),
1);
when(mBroadcast.isEnabled(any())).thenReturn(true);
- CachedBluetoothDeviceManager deviceManager = mock(CachedBluetoothDeviceManager.class);
- when(mLocalBluetoothManager.getCachedDeviceManager()).thenReturn(deviceManager);
- when(deviceManager.findDevice(mBluetoothDevice)).thenReturn(mCachedBluetoothDevice);
+ when(mDeviceManager.findDevice(mBluetoothDevice)).thenReturn(mCachedBluetoothDevice);
when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice);
when(mCachedBluetoothDevice.getGroupId()).thenReturn(1);
BluetoothLeBroadcastReceiveState state = mock(BluetoothLeBroadcastReceiveState.class);
@@ -958,6 +1003,7 @@ public class BluetoothUtilsTest {
}
@Test
+ @DisableFlags(Flags.FLAG_ADOPT_PRIMARY_GROUP_MANAGEMENT_API_V2)
public void getSecondaryDeviceForBroadcast_returnCorrectDevice() {
Settings.Secure.putInt(
mContext.getContentResolver(),
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
index f57ee0c0930e..b4384b74ccbe 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
@@ -16,7 +16,6 @@
package com.android.settingslib.bluetooth;
import static com.android.settingslib.flags.Flags.FLAG_ENABLE_LE_AUDIO_SHARING;
-import static com.android.settingslib.flags.Flags.FLAG_ENABLE_SET_PREFERRED_TRANSPORT_FOR_LE_AUDIO_DEVICE;
import static com.android.settingslib.flags.Flags.FLAG_ENABLE_TEMPORARY_BOND_DEVICES_UI;
import static com.google.common.truth.Truth.assertThat;
@@ -42,6 +41,8 @@ import android.content.Context;
import android.graphics.drawable.BitmapDrawable;
import android.hardware.input.InputManager;
import android.media.AudioManager;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
import android.platform.test.flag.junit.SetFlagsRule;
import android.provider.Settings;
import android.text.Spannable;
@@ -138,7 +139,6 @@ public class CachedBluetoothDeviceTest {
public void setUp() {
MockitoAnnotations.initMocks(this);
mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_TV_MEDIA_OUTPUT_DIALOG);
- mSetFlagsRule.enableFlags(FLAG_ENABLE_SET_PREFERRED_TRANSPORT_FOR_LE_AUDIO_DEVICE);
mSetFlagsRule.enableFlags(FLAG_ENABLE_LE_AUDIO_SHARING);
mSetFlagsRule.enableFlags(FLAG_ENABLE_TEMPORARY_BOND_DEVICES_UI);
mContext = RuntimeEnvironment.application;
@@ -163,6 +163,7 @@ public class CachedBluetoothDeviceTest {
when(mHidProfile.getProfileId()).thenReturn(BluetoothProfile.HID_HOST);
when(mLocalBluetoothManager.getProfileManager()).thenReturn(mProfileManager);
when(mBroadcast.isEnabled(any())).thenReturn(false);
+ when(mProfileManager.getLeAudioProfile()).thenReturn(mLeAudioProfile);
when(mProfileManager.getLeAudioBroadcastProfile()).thenReturn(mBroadcast);
when(mProfileManager.getLeAudioBroadcastAssistantProfile()).thenReturn(mAssistant);
mCachedDevice = spy(new CachedBluetoothDevice(mContext, mProfileManager, mDevice));
@@ -2004,6 +2005,70 @@ public class CachedBluetoothDeviceTest {
}
@Test
+ @EnableFlags(com.android.settingslib.flags.Flags.FLAG_ADOPT_PRIMARY_GROUP_MANAGEMENT_API_V2)
+ public void getConnectionSummary_adoptAPI_isBroadcastPrimary_fallbackDevice_returnActive() {
+ when(mBroadcast.isEnabled(any())).thenReturn(true);
+ when(mCachedDevice.getDevice()).thenReturn(mDevice);
+ when(mLeAudioProfile.getBroadcastToUnicastFallbackGroup()).thenReturn(1);
+
+ List<Long> bisSyncState = new ArrayList<>();
+ bisSyncState.add(1L);
+ when(mLeBroadcastReceiveState.getBisSyncState()).thenReturn(bisSyncState);
+ List<BluetoothLeBroadcastReceiveState> sourceList = new ArrayList<>();
+ sourceList.add(mLeBroadcastReceiveState);
+ when(mAssistant.getAllSources(any())).thenReturn(sourceList);
+
+ when(mCachedDevice.getGroupId()).thenReturn(1);
+
+ assertThat(mCachedDevice.getConnectionSummary(false))
+ .isEqualTo(mContext.getString(R.string.bluetooth_active_no_battery_level));
+ }
+
+ @Test
+ @EnableFlags(com.android.settingslib.flags.Flags.FLAG_ADOPT_PRIMARY_GROUP_MANAGEMENT_API_V2)
+ public void getConnectionSummary_adoptAPI_isBroadcastPrimary_activeDevice_returnActive() {
+ when(mBroadcast.isEnabled(any())).thenReturn(true);
+ when(mCachedDevice.getDevice()).thenReturn(mDevice);
+ when(mLeAudioProfile.getBroadcastToUnicastFallbackGroup()).thenReturn(
+ BluetoothCsipSetCoordinator.GROUP_ID_INVALID);
+
+ List<Long> bisSyncState = new ArrayList<>();
+ bisSyncState.add(1L);
+ when(mLeBroadcastReceiveState.getBisSyncState()).thenReturn(bisSyncState);
+ List<BluetoothLeBroadcastReceiveState> sourceList = new ArrayList<>();
+ sourceList.add(mLeBroadcastReceiveState);
+ when(mAssistant.getAllSources(any())).thenReturn(sourceList);
+
+ when(mCachedDevice.getGroupId()).thenReturn(1);
+ when(mCachedDevice.isActiveDevice(BluetoothProfile.LE_AUDIO)).thenReturn(true);
+
+ assertThat(mCachedDevice.getConnectionSummary(false))
+ .isEqualTo(mContext.getString(R.string.bluetooth_active_no_battery_level));
+ }
+
+ @Test
+ @EnableFlags(com.android.settingslib.flags.Flags.FLAG_ADOPT_PRIMARY_GROUP_MANAGEMENT_API_V2)
+ public void getConnectionSummary_adoptAPI_isBroadcastNotPrimary_returnActiveMedia() {
+ when(mBroadcast.isEnabled(any())).thenReturn(true);
+ when(mCachedDevice.getDevice()).thenReturn(mDevice);
+ when(mLeAudioProfile.getBroadcastToUnicastFallbackGroup()).thenReturn(1);
+
+ List<Long> bisSyncState = new ArrayList<>();
+ bisSyncState.add(1L);
+ when(mLeBroadcastReceiveState.getBisSyncState()).thenReturn(bisSyncState);
+ List<BluetoothLeBroadcastReceiveState> sourceList = new ArrayList<>();
+ sourceList.add(mLeBroadcastReceiveState);
+ when(mAssistant.getAllSources(any())).thenReturn(sourceList);
+
+ when(mCachedDevice.getGroupId()).thenReturn(BluetoothCsipSetCoordinator.GROUP_ID_INVALID);
+
+ assertThat(mCachedDevice.getConnectionSummary(false))
+ .isEqualTo(
+ mContext.getString(R.string.bluetooth_active_media_only_no_battery_level));
+ }
+
+ @Test
+ @DisableFlags(com.android.settingslib.flags.Flags.FLAG_ADOPT_PRIMARY_GROUP_MANAGEMENT_API_V2)
public void getConnectionSummary_isBroadcastPrimary_fallbackDevice_returnActive() {
when(mBroadcast.isEnabled(any())).thenReturn(true);
when(mCachedDevice.getDevice()).thenReturn(mDevice);
@@ -2026,6 +2091,7 @@ public class CachedBluetoothDeviceTest {
}
@Test
+ @DisableFlags(com.android.settingslib.flags.Flags.FLAG_ADOPT_PRIMARY_GROUP_MANAGEMENT_API_V2)
public void getConnectionSummary_isBroadcastPrimary_activeDevice_returnActive() {
when(mBroadcast.isEnabled(any())).thenReturn(true);
when(mCachedDevice.getDevice()).thenReturn(mDevice);
@@ -2049,6 +2115,7 @@ public class CachedBluetoothDeviceTest {
}
@Test
+ @DisableFlags(com.android.settingslib.flags.Flags.FLAG_ADOPT_PRIMARY_GROUP_MANAGEMENT_API_V2)
public void getConnectionSummary_isBroadcastNotPrimary_returnActiveMedia() {
when(mBroadcast.isEnabled(any())).thenReturn(true);
when(mCachedDevice.getDevice()).thenReturn(mDevice);
@@ -2231,11 +2298,7 @@ public class CachedBluetoothDeviceTest {
"false".getBytes());
when(mDevice.getMetadata(BluetoothDevice.METADATA_MAIN_BATTERY)).thenReturn(
MAIN_BATTERY.getBytes());
- when(mContext.getSystemService(InputManager.class)).thenReturn(mInputManager);
- when(mInputManager.getInputDeviceIds()).thenReturn(new int[]{TEST_DEVICE_ID});
- when(mInputManager.getInputDeviceBluetoothAddress(TEST_DEVICE_ID)).thenReturn(
- DEVICE_ADDRESS);
- when(mInputManager.getInputDevice(TEST_DEVICE_ID)).thenReturn(mInputDevice);
+ mCachedDevice.setInputDevice(mInputDevice);
BatteryLevelsInfo batteryLevelsInfo = mCachedDevice.getBatteryLevelsInfo();
@@ -2253,10 +2316,9 @@ public class CachedBluetoothDeviceTest {
public void getBatteryLevelsInfo_stylusDeviceWithBattery_returnBatteryLevelsInfo() {
when(mDevice.getMetadata(BluetoothDevice.METADATA_IS_UNTETHERED_HEADSET)).thenReturn(
"false".getBytes());
- when(mDevice.getMetadata(BluetoothDevice.METADATA_DEVICE_TYPE)).thenReturn(
- BluetoothDevice.DEVICE_TYPE_STYLUS.getBytes());
when(mDevice.getMetadata(BluetoothDevice.METADATA_MAIN_BATTERY)).thenReturn(
MAIN_BATTERY.getBytes());
+ mCachedDevice.setIsDeviceStylus(true);
BatteryLevelsInfo batteryLevelsInfo = mCachedDevice.getBatteryLevelsInfo();
@@ -2270,6 +2332,31 @@ public class CachedBluetoothDeviceTest {
Integer.parseInt(MAIN_BATTERY));
}
+ @Test
+ public void getBatteryLevelsInfo_hearingAidDeviceWithBattery_returnBatteryLevelsInfo() {
+ when(mDevice.getMetadata(BluetoothDevice.METADATA_IS_UNTETHERED_HEADSET)).thenReturn(
+ "false".getBytes());
+ when(mProfileManager.getHearingAidProfile()).thenReturn(mHearingAidProfile);
+ updateProfileStatus(mHearingAidProfile, BluetoothProfile.STATE_CONNECTED);
+ mSubCachedDevice.setHearingAidInfo(getLeftAshaHearingAidInfo());
+ when(mSubCachedDevice.getBatteryLevel()).thenReturn(Integer.parseInt(TWS_BATTERY_LEFT));
+ updateSubDeviceProfileStatus(mHearingAidProfile, BluetoothProfile.STATE_CONNECTED);
+ mCachedDevice.setSubDevice(mSubCachedDevice);
+ mCachedDevice.setHearingAidInfo(getRightAshaHearingAidInfo());
+ when(mCachedDevice.getBatteryLevel()).thenReturn(Integer.parseInt(TWS_BATTERY_RIGHT));
+
+ BatteryLevelsInfo batteryLevelsInfo = mCachedDevice.getBatteryLevelsInfo();
+
+ assertThat(batteryLevelsInfo.getLeftBatteryLevel()).isEqualTo(
+ Integer.parseInt(TWS_BATTERY_LEFT));
+ assertThat(batteryLevelsInfo.getRightBatteryLevel()).isEqualTo(
+ Integer.parseInt(TWS_BATTERY_RIGHT));
+ assertThat(batteryLevelsInfo.getCaseBatteryLevel()).isEqualTo(
+ BluetoothDevice.BATTERY_LEVEL_UNKNOWN);
+ assertThat(batteryLevelsInfo.getOverallBatteryLevel()).isEqualTo(
+ Integer.parseInt(TWS_BATTERY_LEFT));
+ }
+
private void updateProfileStatus(LocalBluetoothProfile profile, int status) {
doReturn(status).when(profile).getConnectionStatus(mDevice);
mCachedDevice.onProfileStateChanged(profile, status);
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CsipDeviceManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CsipDeviceManagerTest.java
index fd14d1ff6786..4314982752c3 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CsipDeviceManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CsipDeviceManagerTest.java
@@ -377,6 +377,7 @@ public class CsipDeviceManagerTest {
BluetoothLeBroadcastReceiveState.class);
when(state.getBisSyncState()).thenReturn(ImmutableList.of(1L));
when(mAssistant.getAllSources(mDevice2)).thenReturn(ImmutableList.of(state));
+ when(mAssistant.getConnectionStatus(mDevice1)).thenReturn(BluetoothAdapter.STATE_CONNECTED);
when(mContext.getSystemService(UserManager.class)).thenReturn(mUserManager);
when(mUserManager.isManagedProfile()).thenReturn(true);
@@ -407,6 +408,7 @@ public class CsipDeviceManagerTest {
BluetoothLeBroadcastReceiveState.class);
when(state.getBisSyncState()).thenReturn(ImmutableList.of(1L));
when(mAssistant.getAllSources(mDevice2)).thenReturn(ImmutableList.of(state));
+ when(mAssistant.getConnectionStatus(mDevice1)).thenReturn(BluetoothAdapter.STATE_CONNECTED);
assertThat(mCsipDeviceManager.addMemberDevicesIntoMainDevice(GROUP1, preferredDevice))
.isTrue();
@@ -474,6 +476,8 @@ public class CsipDeviceManagerTest {
BluetoothLeBroadcastReceiveState.class);
when(state.getBisSyncState()).thenReturn(ImmutableList.of(1L));
when(mAssistant.getAllSources(mDevice1)).thenReturn(ImmutableList.of(state));
+ when(mAssistant.getConnectionStatus(mDevice2)).thenReturn(BluetoothAdapter.STATE_CONNECTED);
+ when(mAssistant.getConnectionStatus(mDevice3)).thenReturn(BluetoothAdapter.STATE_CONNECTED);
assertThat(mCsipDeviceManager.addMemberDevicesIntoMainDevice(GROUP1, preferredDevice))
.isTrue();
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/notification/data/repository/ZenModeRepositoryTest.kt b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/notification/data/repository/ZenModeRepositoryTest.kt
index b364368df473..ec7baf6bf081 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/notification/data/repository/ZenModeRepositoryTest.kt
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/notification/data/repository/ZenModeRepositoryTest.kt
@@ -75,10 +75,14 @@ class ZenModeRepositoryTest {
private val testScope: TestScope = TestScope()
+ private val initialModes = listOf(TestModeBuilder().setId("Built-in").build())
+
@Before
fun setup() {
MockitoAnnotations.initMocks(this)
+ `when`(zenModesBackend.modes).thenReturn(initialModes)
+
underTest =
ZenModeRepositoryImpl(
context,
@@ -151,8 +155,8 @@ class ZenModeRepositoryTest {
fun modesListEmitsOnSettingsChange() {
testScope.runTest {
val values = mutableListOf<List<ZenMode>>()
- val modes1 = listOf(TestModeBuilder().setId("One").build())
- `when`(zenModesBackend.modes).thenReturn(modes1)
+
+ // an initial list of modes is read when the stateflow is created
underTest.modes.onEach { values.add(it) }.launchIn(backgroundScope)
runCurrent()
@@ -172,7 +176,7 @@ class ZenModeRepositoryTest {
triggerZenModeSettingUpdate()
runCurrent()
- assertThat(values).containsExactly(modes1, modes2, modes3).inOrder()
+ assertThat(values).containsExactly(initialModes, modes2, modes3).inOrder()
}
}
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
index d0f84627f8d8..463e94bfa0d6 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
@@ -464,6 +464,7 @@ public class SecureSettingsValidators {
VALIDATORS.put(Secure.MANDATORY_BIOMETRICS_REQUIREMENTS_SATISFIED,
new InclusiveIntegerRangeValidator(0, 1));
VALIDATORS.put(Secure.ADVANCED_PROTECTION_MODE, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(Secure.AAPM_USB_DATA_PROTECTION, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.DISABLE_ADAPTIVE_AUTH_LIMIT_LOCK, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.FACE_APP_ENABLED, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.FACE_KEYGUARD_ENABLED, BOOLEAN_VALIDATOR);
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java
index ed11e12c32ff..527a1f16a84f 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java
@@ -16,6 +16,8 @@
package com.android.providers.settings;
+import static com.android.settingslib.devicestate.DeviceStateAutoRotateSettingUtils.isDeviceStateRotationLockEnabled;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
@@ -49,11 +51,11 @@ import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.LocalePicker;
import com.android.server.backup.Flags;
-import com.android.settingslib.devicestate.DeviceStateRotationLockSettingsManager;
import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Set;
@@ -347,7 +349,7 @@ public class SettingsHelper {
private boolean shouldSkipAutoRotateRestore() {
// When device state based auto rotation settings are available, let's skip the restoring
// of the standard auto rotation settings to avoid conflicting setting values.
- return DeviceStateRotationLockSettingsManager.isDeviceStateRotationLockEnabled(mContext);
+ return isDeviceStateRotationLockEnabled(mContext);
}
public String onBackupValue(String name, String value) {
@@ -650,6 +652,10 @@ public class SettingsHelper {
* e.g. current locale "en-US,zh-CN" and backup locale "ja-JP,zh-Hans-CN,en-US" are merged to
* "en-US,zh-CN,ja-JP".
*
+ * - Same language codes and scripts are dropped.
+ * e.g. current locale "en-US, zh-Hans-TW" and backup locale "en-UK, en-GB, zh-Hans-HK" are
+ * merged to "en-US, zh-Hans-TW".
+ *
* - Unsupported locales are dropped.
* e.g. current locale "en-US" and backup locale "ja-JP,zh-CN" but the supported locales
* are "en-US,zh-CN", the merged locale list is "en-US,zh-CN".
@@ -683,13 +689,23 @@ public class SettingsHelper {
filtered.add(locale);
}
+ final HashSet<String> existingLanguageAndScript = new HashSet<>();
for (int i = 0; i < restore.size(); i++) {
final Locale restoredLocaleWithExtension = copyExtensionToTargetLocale(restoredLocale,
getFilteredLocale(restore.get(i), allLocales));
+
if (restoredLocaleWithExtension != null) {
- filtered.add(restoredLocaleWithExtension);
+ String language = restoredLocaleWithExtension.getLanguage();
+ String script = restoredLocaleWithExtension.getScript();
+
+ String restoredLanguageAndScript =
+ script == null ? language : language + "-" + script;
+ if (existingLanguageAndScript.add(restoredLanguageAndScript)) {
+ filtered.add(restoredLocaleWithExtension);
+ }
}
}
+
if (filtered.size() == current.size()) {
return current; // Nothing added to current locale list.
}
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index 7179cbdf93fb..e9b6c73cb800 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -642,6 +642,7 @@ public class SettingsBackupTest {
private static final Set<String> BACKUP_DENY_LIST_SECURE_SETTINGS =
newHashSet(
+ Settings.Secure.AAPM_USB_DATA_PROTECTION,
Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE,
Settings.Secure.ACCESSIBILITY_SPEAK_PASSWORD, // Deprecated since O.
Settings.Secure.ALLOW_PRIMARY_GAIA_ACCOUNT_REMOVAL_FOR_TESTS,
diff --git a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsHelperTest.java b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsHelperTest.java
index 40654b0e2f37..48c778542d66 100644
--- a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsHelperTest.java
+++ b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsHelperTest.java
@@ -388,7 +388,11 @@ public class SettingsHelperTest {
LocaleList.forLanguageTags("zh-Hant-TW"), // current
new String[] { "fa-Arab-AF-u-nu-latn", "zh-Hant-TW" })); // supported
-
+ assertEquals(LocaleList.forLanguageTags("en-US,zh-Hans-TW"),
+ SettingsHelper.resolveLocales(
+ LocaleList.forLanguageTags("en-UK,en-GB,zh-Hans-HK"), // restore
+ LocaleList.forLanguageTags("en-US,zh-Hans-TW"), // current
+ new String[] { "en-US,zh-Hans-TW,en-UK,en-GB,zh-Hans-HK" })); // supported
}
@Test
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 55f7317f25e4..758ad797f761 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -1015,6 +1015,10 @@
<uses-permission android:name="android.permission.MANAGE_GLOBAL_SOUND_QUALITY_SERVICE" />
<uses-permission android:name="android.permission.READ_COLOR_ZONES" />
+ <!-- Permissions required for CTS test - CtsModernMediaProviderTests -->
+ <uses-permission android:name="com.android.providers.media.permission.ACCESS_OEM_METADATA" />
+ <uses-permission android:name="com.android.providers.media.permission.UPDATE_OEM_METADATA" />
+
<!-- Permission required for trade-in mode testing -->
<uses-permission android:name="android.permission.ENTER_TRADE_IN_MODE" />
diff --git a/packages/Shell/res/values-en-rCA-watch/strings.xml b/packages/Shell/res/values-en-rCA-watch/strings.xml
new file mode 100644
index 000000000000..23f83c48def7
--- /dev/null
+++ b/packages/Shell/res/values-en-rCA-watch/strings.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2021 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.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="bugreport_in_progress_title" msgid="5351751440620800623">"Bug report <xliff:g id="ID">#%1$d</xliff:g> is <xliff:g id="PERCENTAGE">%2$s</xliff:g> complete"</string>
+</resources>
diff --git a/packages/StatementService/Android.bp b/packages/StatementService/Android.bp
index 39b0302beff8..3fc9aabb7edb 100644
--- a/packages/StatementService/Android.bp
+++ b/packages/StatementService/Android.bp
@@ -32,7 +32,11 @@ android_app {
},
target_sdk_version: "29",
platform_apis: true,
+ system_ext_specific: true,
privileged: true,
+ required: [
+ "privapp_whitelist_com.android.statementservice",
+ ],
certificate: "platform",
static_libs: [
"StatementServiceParser",
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index 129949fd38b2..2b17ae48132c 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -220,6 +220,7 @@ filegroup {
"tests/src/**/systemui/statusbar/phone/StatusBarBoundsProviderTest.kt",
"tests/src/**/systemui/statusbar/phone/StatusBarMoveFromCenterAnimationControllerTest.kt",
"tests/src/**/systemui/statusbar/pipeline/airplane/ui/viewmodel/AirplaneModeViewModelImplTest.kt",
+ "tests/src/**/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryKairosAdapterTest.kt",
"tests/src/**/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt",
"tests/src/**/systemui/statusbar/pipeline/mobile/ui/view/ModernStatusBarMobileViewTest.kt",
"tests/src/**/systemui/statusbar/pipeline/wifi/ui/view/ModernStatusBarWifiViewTest.kt",
@@ -356,7 +357,9 @@ filegroup {
"tests/src/**/systemui/qs/tiles/AlarmTileTest.kt",
"tests/src/**/systemui/qs/tiles/BluetoothTileTest.kt",
"tests/src/**/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepositoryTest.kt",
+ "tests/src/**/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryKairosAdapterTest.kt",
"tests/src/**/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryTest.kt",
+ "tests/src/**/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionKairosAdapterTelephonySmokeTests.kt",
"tests/src/**/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionTelephonySmokeTests.kt",
"tests/src/**/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcherTest.kt",
"tests/src/**/systemui/statusbar/pipeline/mobile/data/model/SystemUiCarrierConfigTest.kt",
@@ -429,6 +432,7 @@ android_library {
manifest: "AndroidManifest-res.xml",
flags_packages: [
"android.app.flags-aconfig",
+ "com_android_systemui_flags",
],
}
diff --git a/packages/SystemUI/TEST_MAPPING b/packages/SystemUI/TEST_MAPPING
index 1362ffebe107..86559fd557d6 100644
--- a/packages/SystemUI/TEST_MAPPING
+++ b/packages/SystemUI/TEST_MAPPING
@@ -1,22 +1,6 @@
{
- // Curious where your @Scenario tests are running?
- //
- // @Ignore: Will not run in any configuration
- //
- // @FlakyTest: Tests that don't block pre/postsubmit but are staged to run known failures.
- // Tests will run in postsubmit on sysui-e2e-staged suite.
- //
- //
- // @PlatinumTest: Marking your test with this annotation will put your tests in presubmit.
- // Please DO NOT annotate new or old tests with @PlatinumTest annotation
- // without discussing with mdb:android-platinum
- //
- // @Postsubmit: Do not use this annotation for e2e tests. This won't have any affect.
-
- // For all other e2e tests which are not platinum, they run in sysui-silver suite,that
- // primarily runs in postsubmit with an exception to e2e test related changes.
- // If you want to see one shot place to monitor all e2e tests, look for
- // sysui-e2e-staged suite.
+ // Test mappings for SystemUI unit tests.
+ // For e2e mappings, see go/sysui-e2e-test-mapping
// v2/android-virtual-infra/test_mapping/presubmit-avd
"presubmit": [
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/Android.bp b/packages/SystemUI/accessibility/accessibilitymenu/Android.bp
index fb1f715bc68f..172f687ef6fd 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/Android.bp
+++ b/packages/SystemUI/accessibility/accessibilitymenu/Android.bp
@@ -40,6 +40,7 @@ android_app {
"com_android_systemui_flags_lib",
"SettingsLibDisplayUtils",
"SettingsLibSettingsTheme",
+ "SystemUI-shared-utils",
"com_android_a11y_menu_flags_lib",
"//frameworks/libs/systemui:view_capture",
],
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuOverlayLayout.java b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuOverlayLayout.java
index a60778658c59..db2fbd96408c 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuOverlayLayout.java
+++ b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuOverlayLayout.java
@@ -22,8 +22,6 @@ import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.View.ACCESSIBILITY_LIVE_REGION_POLITE;
import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY;
-import static com.android.app.viewcapture.ViewCaptureFactory.getViewCaptureAwareWindowManagerInstance;
-
import static java.lang.Math.max;
import android.animation.Animator;
@@ -55,11 +53,11 @@ import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.UiContext;
-import com.android.app.viewcapture.ViewCaptureAwareWindowManager;
import com.android.systemui.accessibility.accessibilitymenu.AccessibilityMenuService;
import com.android.systemui.accessibility.accessibilitymenu.R;
import com.android.systemui.accessibility.accessibilitymenu.activity.A11yMenuSettingsActivity.A11yMenuPreferenceFragment;
import com.android.systemui.accessibility.accessibilitymenu.model.A11yMenuShortcut;
+import com.android.systemui.utils.windowmanager.WindowManagerUtils;
import java.util.ArrayList;
import java.util.List;
@@ -145,9 +143,7 @@ public class A11yMenuOverlayLayout {
final Display display = mDisplayManager.getDisplay(DEFAULT_DISPLAY);
final Context uiContext = mService.createWindowContext(
display, TYPE_ACCESSIBILITY_OVERLAY, /* options= */null);
- final ViewCaptureAwareWindowManager windowManager =
- getViewCaptureAwareWindowManagerInstance(uiContext,
- com.android.systemui.Flags.enableViewCaptureTracing());
+ final WindowManager windowManager = WindowManagerUtils.getWindowManager(uiContext);
mLayout = new A11yMenuFrameLayout(uiContext);
updateLayoutPosition(uiContext);
inflateLayoutAndSetOnTouchListener(mLayout, uiContext);
@@ -162,8 +158,7 @@ public class A11yMenuOverlayLayout {
public void clearLayout() {
if (mLayout != null) {
- ViewCaptureAwareWindowManager windowManager = getViewCaptureAwareWindowManagerInstance(
- mLayout.getContext(), com.android.systemui.Flags.enableViewCaptureTracing());
+ WindowManager windowManager = WindowManagerUtils.getWindowManager(mLayout.getContext());
if (windowManager != null) {
windowManager.removeView(mLayout);
}
@@ -178,7 +173,7 @@ public class A11yMenuOverlayLayout {
return;
}
updateLayoutPosition(mLayout.getContext());
- WindowManager windowManager = mLayout.getContext().getSystemService(WindowManager.class);
+ WindowManager windowManager = WindowManagerUtils.getWindowManager(mLayout.getContext());
if (windowManager != null) {
windowManager.updateViewLayout(mLayout, mLayoutParameter);
}
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuViewPager.java b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuViewPager.java
index b899c45b0f7e..842834b5fc27 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuViewPager.java
+++ b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuViewPager.java
@@ -36,6 +36,7 @@ import com.android.systemui.accessibility.accessibilitymenu.R;
import com.android.systemui.accessibility.accessibilitymenu.activity.A11yMenuSettingsActivity.A11yMenuPreferenceFragment;
import com.android.systemui.accessibility.accessibilitymenu.model.A11yMenuShortcut;
import com.android.systemui.accessibility.accessibilitymenu.view.A11yMenuFooter.A11yMenuFooterCallBack;
+import com.android.systemui.utils.windowmanager.WindowManagerUtils;
import java.util.ArrayList;
import java.util.List;
@@ -292,8 +293,8 @@ public class A11yMenuViewPager {
// Keeps footer window height unchanged no matter the density is changed.
mA11yMenuFooter.adjustFooterToDensityScale(densityScale);
// Adjust the view pager height for system bar and display cutout insets.
- WindowManager windowManager = mA11yMenuLayout.getContext()
- .getSystemService(WindowManager.class);
+ WindowManager windowManager = WindowManagerUtils
+ .getWindowManager(mA11yMenuLayout.getContext());
WindowMetrics windowMetric = windowManager.getCurrentWindowMetrics();
Insets windowInsets = windowMetric.getWindowInsets().getInsetsIgnoringVisibility(
WindowInsets.Type.systemBars() | WindowInsets.Type.displayCutout());
diff --git a/packages/SystemUI/aconfig/accessibility.aconfig b/packages/SystemUI/aconfig/accessibility.aconfig
index c6bc1c70ad18..3cb30258fcb1 100644
--- a/packages/SystemUI/aconfig/accessibility.aconfig
+++ b/packages/SystemUI/aconfig/accessibility.aconfig
@@ -155,13 +155,3 @@ flag {
purpose: PURPOSE_BUGFIX
}
}
-
-flag {
- name: "hearing_devices_input_routing_ui_improvement"
- namespace: "accessibility"
- description: "UI improvement for hearing device input routing feature"
- bug: "397314200"
- metadata {
- purpose: PURPOSE_BUGFIX
- }
-}
diff --git a/packages/SystemUI/aconfig/desktop_users_and_accounts.aconfig b/packages/SystemUI/aconfig/desktop_users_and_accounts.aconfig
new file mode 100644
index 000000000000..c7e9c9fbee2e
--- /dev/null
+++ b/packages/SystemUI/aconfig/desktop_users_and_accounts.aconfig
@@ -0,0 +1,9 @@
+package: "com.android.systemui"
+container: "system"
+
+flag {
+ name: "user_switcher_add_sign_out_option"
+ namespace: "desktop_users_and_accounts"
+ description: "Add a sign out option to the user switcher menu if sign out is possible"
+ bug: "381478261"
+} \ No newline at end of file
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index 4693377654f8..350ec37b1d29 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -289,6 +289,15 @@ flag {
}
}
+flag {
+ name: "notification_skip_silent_updates"
+ namespace: "systemui"
+ description: "Do not notify HeadsUpManager for silent updates."
+ bug: "401068530"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
flag {
name: "scene_container"
@@ -398,13 +407,6 @@ flag {
}
flag {
- name: "light_reveal_migration"
- namespace: "systemui"
- description: "Move LightRevealScrim to recommended architecture"
- bug: "281655028"
-}
-
-flag {
name: "theme_overlay_controller_wakefulness_deprecation"
namespace: "systemui"
description: "Replacing WakefulnessLifecycle by KeyguardTransitionInteractor in "
@@ -532,6 +534,22 @@ flag {
}
flag {
+ name: "status_bar_font_updates"
+ namespace: "systemui"
+ description: "Read only flag for using a new font in the status bar"
+ bug: "393609116"
+ is_fixed_read_only: true
+}
+
+flag {
+ name: "icon_refresh_2025"
+ namespace: "systemui"
+ description: "Build time flag for 2025 icon refresh"
+ bug: "391605373"
+ is_fixed_read_only: true
+}
+
+flag {
name: "promote_notifications_automatically"
namespace: "systemui"
description: "Flag to automatically turn certain notifications into promoted notifications so "
@@ -541,6 +559,13 @@ flag {
}
flag {
+ name: "debug_live_updates_promote_all"
+ namespace: "systemui"
+ description: "Promote all notifications to Live Updates (RONs). (For testing/debugging only, do not promote/release!)"
+ bug: "404838239"
+}
+
+flag {
name: "compose_bouncer"
namespace: "systemui"
description: "Use the new compose bouncer in SystemUI"
@@ -1443,16 +1468,6 @@ flag {
}
flag {
- name: "dozeui_scheduling_alarms_background_execution"
- namespace: "systemui"
- description: "Decide whether to execute binder calls to schedule alarms in background thread"
- bug: "330492575"
- metadata {
- purpose: PURPOSE_BUGFIX
- }
-}
-
-flag {
name: "media_lockscreen_launch_animation"
namespace : "systemui"
description : "Enable the origin launch animation for UMO when opening on top of lockscreen."
@@ -1865,6 +1880,14 @@ flag {
}
flag {
+ name: "shade_header_font_update"
+ namespace: "systemui"
+ description: "Updates the fonts of the shade header"
+ bug: "393609960"
+ is_fixed_read_only: true
+}
+
+flag {
name: "keyboard_shortcut_helper_shortcut_customizer"
namespace: "systemui"
description: "An implementation of shortcut customizations through shortcut helper."
@@ -1909,13 +1932,6 @@ flag {
}
flag {
- name: "spatial_model_launcher_pushback"
- namespace: "systemui"
- description: "Implement the depth push scaling effect on Launcher when users pull down shade."
- bug: "370562309"
-}
-
-flag {
name: "spatial_model_app_pushback"
namespace: "systemui"
description: "Implement the depth push scaling effect on the current app when users pull down shade."
@@ -2021,7 +2037,14 @@ flag {
flag {
name: "permission_helper_ui_rich_ongoing"
namespace: "systemui"
- description: "[RONs] Guards inline permission helper for demoting RONs"
+ description: "[RONs] Guards inline permission helper for demoting RONs [Guts/card version]"
+ bug: "379186372"
+}
+
+flag {
+ name: "permission_helper_inline_ui_rich_ongoing"
+ namespace: "systemui"
+ description: "[RONs] Guards inline permission helper for demoting RONs [Inline version]"
bug: "379186372"
}
@@ -2136,3 +2159,37 @@ flag {
description: "Enables return animations for status bar chips"
bug: "202516970"
}
+
+flag {
+ name: "media_projection_grey_error_text"
+ namespace: "systemui"
+ description: "Set the error text color to grey when app sharing is hidden by the requesting app"
+ bug: "400877402"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
+ name: "keyguard_wm_reorder_atms_calls"
+ namespace: "systemui"
+ description: "Calls ATMS#setLockScreenShown before default display callbacks in case they're slow"
+ bug: "399693427"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
+ name: "extended_apps_shortcut_category"
+ namespace: "systemui"
+ description: "Allow users to add shortcuts to open apps that are not present in the apps category in shortcut helper by default"
+ bug: "394290928"
+}
+
+flag {
+ name: "use_aad_prox_sensor"
+ namespace: "systemui"
+ description: "Use AAD proximity sensor if flag is enabled and sensor is present"
+ bug: "402534470"
+}
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityTransitionAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityTransitionAnimator.kt
index 7ee6a6e5ebf4..440a81fc2152 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityTransitionAnimator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityTransitionAnimator.kt
@@ -751,7 +751,8 @@ constructor(
OriginTransition(createLongLivedRunner(controllerFactory, scope, forLaunch = true)),
"${cookie}_launchTransition",
)
- transitionRegister.register(launchFilter, launchRemoteTransition, includeTakeover = true)
+ // TODO(b/403529740): re-enable takeovers once we solve the Compose jank issues.
+ transitionRegister.register(launchFilter, launchRemoteTransition, includeTakeover = false)
// Cross-task close transitions should not use this animation, so we only register it for
// when the opening window is Launcher.
@@ -777,7 +778,8 @@ constructor(
),
"${cookie}_returnTransition",
)
- transitionRegister.register(returnFilter, returnRemoteTransition, includeTakeover = true)
+ // TODO(b/403529740): re-enable takeovers once we solve the Compose jank issues.
+ transitionRegister.register(returnFilter, returnRemoteTransition, includeTakeover = false)
longLivedTransitions[cookie] = Pair(launchRemoteTransition, returnRemoteTransition)
}
@@ -1086,9 +1088,8 @@ constructor(
if (!success) finishedCallback?.onAnimationFinished()
}
} else {
- // This should never happen, as either the controller or factory should always be
- // defined. This final call is for safety in case something goes wrong.
- Log.wtf(TAG, "initAndRun with neither a controller nor factory")
+ // This happens when onDisposed() has already been called due to the animation being
+ // cancelled. Only issue the callback.
finishedCallback?.onAnimationFinished()
}
}
@@ -1216,6 +1217,13 @@ constructor(
private var animation: TransitionAnimator.Animation? = null
/**
+ * Whether the opening/closing window needs to reparented to the view's window at the
+ * beginning of the animation. Since we don't always do this, we need to keep track of it in
+ * order to have the rest of the animation behave correctly.
+ */
+ var reparent = false
+
+ /**
* A timeout to cancel the transition animation if the remote animation is not started or
* cancelled within [TRANSITION_TIMEOUT] milliseconds after the intent was started.
*
@@ -1469,6 +1477,17 @@ constructor(
transitionAnimator.isExpandingFullyAbove(controller.transitionContainer, endState)
val windowState = startingWindowState ?: controller.windowAnimatorState
+ // We only reparent launch animations. In current integrations, returns are
+ // not affected by the issue solved by reparenting, and they present
+ // additional problems when the view lives in the Status Bar.
+ // TODO(b/397646693): remove this exception.
+ val isEligibleForReparenting = controller.isLaunching
+ val viewRoot = controller.transitionContainer.viewRootImpl
+ val skipReparenting = skipReparentTransaction || viewRoot == null
+ if (moveTransitionAnimationLayer() && isEligibleForReparenting && !skipReparenting) {
+ reparent = true
+ }
+
// We animate the opening window and delegate the view expansion to [this.controller].
val delegate = this.controller
val controller =
@@ -1536,16 +1555,13 @@ constructor(
)
}
- if (moveTransitionAnimationLayer() && !skipReparentTransaction) {
+ if (reparent) {
// Ensure that the launching window is rendered above the view's window,
// so it is not obstructed.
// TODO(b/397180418): re-use the start transaction once the
// RemoteAnimation wrapper is cleaned up.
SurfaceControl.Transaction().use {
- it.reparent(
- window.leash,
- controller.transitionContainer.viewRootImpl.surfaceControl,
- )
+ it.reparent(window.leash, viewRoot.surfaceControl)
it.apply()
}
}
@@ -1603,7 +1619,7 @@ constructor(
null
}
val fadeWindowBackgroundLayer =
- if (moveTransitionAnimationLayer()) {
+ if (reparent) {
false
} else {
!controller.isBelowAnimatingWindow
@@ -1727,7 +1743,7 @@ constructor(
// fade in progressively. Otherwise, it should be fully opaque and will be progressively
// revealed as the window background color layer above the window fades out.
val alpha =
- if (moveTransitionAnimationLayer() || controller.isBelowAnimatingWindow) {
+ if (reparent || controller.isBelowAnimatingWindow) {
if (controller.isLaunching) {
interpolators.contentAfterFadeInInterpolator.getInterpolation(
windowProgress
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewTransitionAnimatorController.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewTransitionAnimatorController.kt
index fdb07bdbe7f3..576ff61c4f94 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewTransitionAnimatorController.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewTransitionAnimatorController.kt
@@ -83,7 +83,7 @@ constructor(
ViewTransitionRegistry.instance
} else {
null
- }
+ },
) : ActivityTransitionAnimator.Controller {
override val isLaunching: Boolean = true
@@ -313,9 +313,17 @@ constructor(
// visibility that is saved by `setShouldBlockVisibilityChanges()` for a later restoration.
(ghostedView as? LaunchableView)?.setShouldBlockVisibilityChanges(true)
- // Create a ghost of the view that will be moving and fading out. This allows to fade out
- // the content before fading out the background.
- ghostView = GhostView.addGhost(ghostedView, transitionContainer)
+ try {
+ // Create a ghost of the view that will be moving and fading out. This allows to fade
+ // out the content before fading out the background.
+ ghostView = GhostView.addGhost(ghostedView, transitionContainer)
+ } catch (e: Exception) {
+ // It is not 100% clear what conditions cause this exception to happen in practice, and
+ // we could never reproduce it, but it does show up extremely rarely. We already handle
+ // the scenario where ghostView is null, so we just avoid crashing and log the error.
+ // See b/315858472 for an investigation of the issue.
+ Log.e(TAG, "Failed to create ghostView", e)
+ }
// [GhostView.addGhost], the result of which is our [ghostView], creates a [GhostView], and
// adds it first to a [FrameLayout] container. It then adds _that_ container to an
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/RemoteAnimationRunnerCompat.java b/packages/SystemUI/animation/src/com/android/systemui/animation/RemoteAnimationRunnerCompat.java
index ca94482b9c5a..060f0c94732d 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/RemoteAnimationRunnerCompat.java
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/RemoteAnimationRunnerCompat.java
@@ -197,6 +197,10 @@ public abstract class RemoteAnimationRunnerCompat extends IRemoteAnimationRunner
// Release surface references now. This is apparently to free GPU memory
// before GC would.
info.releaseAllSurfaces();
+ // Make sure that the transition leashes created are not leaked.
+ for (SurfaceControl leash : leashMap.values()) {
+ finishTransaction.reparent(leash, null);
+ }
// Don't release here since launcher might still be using them. Instead
// let launcher release them (eg. via RemoteAnimationTargets)
leashMap.clear();
@@ -245,6 +249,17 @@ public abstract class RemoteAnimationRunnerCompat extends IRemoteAnimationRunner
runner.onAnimationCancelled();
finishRunnable.run();
}
+
+ @Override
+ public void onTransitionConsumed(IBinder transition, boolean aborted)
+ throws RemoteException {
+ // Notify the remote runner that the transition has been canceled if the transition
+ // was merged into another transition or aborted
+ synchronized (mFinishRunnables) {
+ mFinishRunnables.remove(transition);
+ }
+ runner.onAnimationCancelled();
+ }
};
}
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/PlatformButtons.kt b/packages/SystemUI/compose/core/src/com/android/compose/PlatformButtons.kt
index df50eb8fa3e8..da07fbd9b6a6 100644
--- a/packages/SystemUI/compose/core/src/com/android/compose/PlatformButtons.kt
+++ b/packages/SystemUI/compose/core/src/com/android/compose/PlatformButtons.kt
@@ -101,10 +101,17 @@ fun PlatformIconButton(
modifier: Modifier = Modifier,
enabled: Boolean = true,
colors: IconButtonColors = iconButtonColors(),
+ shape: Shape = IconButtonDefaults.standardShape,
@DrawableRes iconResource: Int,
contentDescription: String?,
) {
- IconButton(modifier = modifier, onClick = onClick, enabled = enabled, colors = colors) {
+ IconButton(
+ modifier = modifier,
+ onClick = onClick,
+ enabled = enabled,
+ colors = colors,
+ shape = shape,
+ ) {
Icon(
painter = painterResource(id = iconResource),
contentDescription = contentDescription,
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/animation/Expandable.kt b/packages/SystemUI/compose/core/src/com/android/compose/animation/Expandable.kt
index 82e5f5bb6dc8..cd9fcefcdda3 100644
--- a/packages/SystemUI/compose/core/src/com/android/compose/animation/Expandable.kt
+++ b/packages/SystemUI/compose/core/src/com/android/compose/animation/Expandable.kt
@@ -127,6 +127,8 @@ import kotlin.math.min
*
* @sample com.android.systemui.compose.gallery.ActivityLaunchScreen
* @sample com.android.systemui.compose.gallery.DialogLaunchScreen
+ * @param defaultMinSize true if a default minimum size should be enforced even if this Expandable
+ * isn't currently clickable and false otherwise.
*/
@Composable
fun Expandable(
@@ -140,6 +142,7 @@ fun Expandable(
// TODO(b/285250939): Default this to true then remove once the Compose QS expandables have
// proven that the new implementation is robust.
useModifierBasedImplementation: Boolean = false,
+ defaultMinSize: Boolean = true,
transitionControllerFactory: ComposableControllerFactory? = null,
content: @Composable (Expandable) -> Unit,
) {
@@ -155,6 +158,7 @@ fun Expandable(
onClick,
interactionSource,
useModifierBasedImplementation,
+ defaultMinSize,
content,
)
}
@@ -182,6 +186,8 @@ fun Expandable(
*
* @sample com.android.systemui.compose.gallery.ActivityLaunchScreen
* @sample com.android.systemui.compose.gallery.DialogLaunchScreen
+ * @param defaultMinSize true if a default minimum size should be enforced even if this Expandable
+ * isn't currently clickable and false otherwise.
*/
@Composable
fun Expandable(
@@ -192,6 +198,7 @@ fun Expandable(
// TODO(b/285250939): Default this to true then remove once the Compose QS expandables have
// proven that the new implementation is robust.
useModifierBasedImplementation: Boolean = false,
+ defaultMinSize: Boolean = true,
content: @Composable (Expandable) -> Unit,
) {
val controller = controller as ExpandableControllerImpl
@@ -209,7 +216,12 @@ fun Expandable(
if (useModifierBasedImplementation) {
Box(modifier.expandable(controller, onClick, interactionSource)) {
- WrappedContent(controller.expandable, controller.contentColor, content)
+ WrappedContent(
+ controller.expandable,
+ controller.contentColor,
+ defaultMinSize = defaultMinSize,
+ content,
+ )
}
return
}
@@ -221,7 +233,7 @@ fun Expandable(
val wrappedContent =
remember(content) {
movableContentOf { expandable: Expandable ->
- WrappedContent(expandable, contentColor, content)
+ WrappedContent(expandable, contentColor, defaultMinSize = defaultMinSize, content)
}
}
@@ -306,21 +318,24 @@ fun Expandable(
private fun WrappedContent(
expandable: Expandable,
contentColor: Color,
+ defaultMinSize: Boolean,
content: @Composable (Expandable) -> Unit,
) {
val minSizeContent =
@Composable {
- // We make sure that the content itself (wrapped by the background) is at least 40.dp,
- // which is the same as the M3 buttons. This applies even if onClick is null, to make it
- // easier to write expandables that are sometimes clickable and sometimes not. There
- // shouldn't be any Expandable smaller than 40dp because if the expandable is not
- // clickable directly, then something in its content should be (and with a size >=
- // 40dp).
- val minSize = 40.dp
- Box(
- Modifier.defaultMinSize(minWidth = minSize, minHeight = minSize),
- contentAlignment = Alignment.Center,
- ) {
+ if (defaultMinSize) {
+ // We make sure that the content itself (wrapped by the background) is at
+ // least 40.dp, which is the same as the M3 buttons. This applies even if
+ // onClick is null, to make it easier to write expandables that are
+ // sometimes clickable and sometimes not.
+ val minSize = 40.dp
+ Box(
+ modifier = Modifier.defaultMinSize(minWidth = minSize, minHeight = minSize),
+ contentAlignment = Alignment.Center,
+ ) {
+ content(expandable)
+ }
+ } else {
content(expandable)
}
}
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/gesture/NestedDraggable.kt b/packages/SystemUI/compose/core/src/com/android/compose/gesture/NestedDraggable.kt
index 2ea9c487c27c..362748ec71b0 100644
--- a/packages/SystemUI/compose/core/src/com/android/compose/gesture/NestedDraggable.kt
+++ b/packages/SystemUI/compose/core/src/com/android/compose/gesture/NestedDraggable.kt
@@ -112,6 +112,16 @@ interface NestedDraggable {
interface Controller {
/**
+ * Whether this controller is ready to drag. [onDrag] will be called only if this returns
+ * `true`, and any drag event will be ignored until then.
+ *
+ * This can for instance be used to wait for the content we are dragging to to be composed
+ * before actually dragging, reducing perceivable jank at the beginning of a drag.
+ */
+ val isReadyToDrag: Boolean
+ get() = true
+
+ /**
* Whether drags that were started from nested scrolls should be automatically
* [stopped][onDragStopped] as soon as they don't consume the entire `delta` passed to
* [onDrag].
@@ -274,6 +284,9 @@ private class NestedDraggableNode(
/** The pointers currently down, in order of which they were done and mapping to their type. */
private val pointersDown = linkedMapOf<PointerId, PointerType>()
+ /** Whether the next drag event should be ignored. */
+ private var ignoreNextDrag = false
+
init {
delegate(nestedScrollModifierNode(this, nestedScrollDispatcher))
}
@@ -426,6 +439,7 @@ private class NestedDraggableNode(
velocityTracker: VelocityTracker,
) {
velocityTracker.addPointerInputChange(change)
+ if (shouldIgnoreDrag(controller)) return
scrollWithOverscroll(delta.toOffset()) { deltaFromOverscroll ->
scrollWithNestedScroll(deltaFromOverscroll) { deltaFromNestedScroll ->
@@ -434,6 +448,23 @@ private class NestedDraggableNode(
}
}
+ private fun shouldIgnoreDrag(controller: NestedDraggable.Controller): Boolean {
+ return when {
+ !controller.isReadyToDrag -> {
+ // The controller is not ready yet, so we are waiting for an expensive frame to be
+ // composed. We should ignore this drag and the next one, given that the first delta
+ // after an expensive frame will be large.
+ ignoreNextDrag = true
+ true
+ }
+ ignoreNextDrag -> {
+ ignoreNextDrag = false
+ true
+ }
+ else -> false
+ }
+ }
+
private fun onDragStopped(controller: NestedDraggable.Controller, velocity: Velocity) {
// We launch in the scope of the dispatcher so that the fling is not cancelled if this node
// is removed right after onDragStopped() is called.
@@ -617,6 +648,8 @@ private class NestedDraggableNode(
}
private fun scrollWithOverscroll(controller: NestedScrollController, offset: Offset): Offset {
+ if (shouldIgnoreDrag(controller.controller)) return offset
+
return scrollWithOverscroll(offset) { delta ->
val available = delta.toFloat()
val consumed = controller.controller.onDrag(available)
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/gesture/NestedScrollController.kt b/packages/SystemUI/compose/core/src/com/android/compose/gesture/NestedScrollController.kt
index 2530a4f240e3..54232e76a568 100644
--- a/packages/SystemUI/compose/core/src/com/android/compose/gesture/NestedScrollController.kt
+++ b/packages/SystemUI/compose/core/src/com/android/compose/gesture/NestedScrollController.kt
@@ -134,6 +134,7 @@ private class NestedScrollControllerNode(
private var bounds: NestedScrollableBound,
) : DelegatingNode(), NestedScrollConnection {
private var childrenConsumedAnyScroll = false
+ private var availableOnPreScroll = Offset.Zero
init {
delegate(nestedScrollModifierNode(this, dispatcher = null))
@@ -153,12 +154,21 @@ private class NestedScrollControllerNode(
this.bounds = bounds
}
+ override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset {
+ availableOnPreScroll = available
+ return Offset.Zero
+ }
+
override fun onPostScroll(
consumed: Offset,
available: Offset,
source: NestedScrollSource,
): Offset {
- if (hasConsumedScrollInBounds(consumed.x) || hasConsumedScrollInBounds(consumed.y)) {
+ val consumedIncludingPreScroll = availableOnPreScroll - available
+ if (
+ hasConsumedScrollInBounds(consumedIncludingPreScroll.x) ||
+ hasConsumedScrollInBounds(consumedIncludingPreScroll.y)
+ ) {
childrenConsumedAnyScroll = true
}
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/gesture/effect/OffsetOverscrollEffect.kt b/packages/SystemUI/compose/core/src/com/android/compose/gesture/effect/OffsetOverscrollEffect.kt
index 1bb8ae5019fb..07a571b94ce4 100644
--- a/packages/SystemUI/compose/core/src/com/android/compose/gesture/effect/OffsetOverscrollEffect.kt
+++ b/packages/SystemUI/compose/core/src/com/android/compose/gesture/effect/OffsetOverscrollEffect.kt
@@ -16,7 +16,6 @@
package com.android.compose.gesture.effect
-import androidx.annotation.VisibleForTesting
import androidx.compose.animation.core.AnimationSpec
import androidx.compose.foundation.OverscrollEffect
import androidx.compose.foundation.OverscrollFactory
@@ -94,7 +93,6 @@ class OffsetOverscrollEffect(animationScope: CoroutineScope, animationSpec: Anim
companion object {
private val MaxDistance = 400.dp
- @VisibleForTesting
fun computeOffset(density: Density, overscrollDistance: Float): Int {
val maxDistancePx = with(density) { MaxDistance.toPx() }
val progress = ProgressConverter.Default.convert(overscrollDistance / maxDistancePx)
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/theme/PlatformTheme.kt b/packages/SystemUI/compose/core/src/com/android/compose/theme/PlatformTheme.kt
index 84370ed4d2c7..6fb3679dfb7c 100644
--- a/packages/SystemUI/compose/core/src/com/android/compose/theme/PlatformTheme.kt
+++ b/packages/SystemUI/compose/core/src/com/android/compose/theme/PlatformTheme.kt
@@ -43,7 +43,7 @@ fun PlatformTheme(isDarkTheme: Boolean = isSystemInDarkTheme(), content: @Compos
val context = LocalContext.current
val colorScheme = remember(context, isDarkTheme) { platformColorScheme(isDarkTheme, context) }
- val androidColorScheme = remember(context) { AndroidColorScheme(context) }
+ val androidColorScheme = remember(context, isDarkTheme) { AndroidColorScheme(context) }
val typefaceNames = remember(context) { TypefaceNames.get(context) }
val typefaceTokens = remember(typefaceNames) { TypefaceTokens(typefaceNames) }
val typography =
diff --git a/packages/SystemUI/compose/core/tests/src/com/android/compose/gesture/NestedDraggableTest.kt b/packages/SystemUI/compose/core/tests/src/com/android/compose/gesture/NestedDraggableTest.kt
index b247993de4e4..b31617369cdb 100644
--- a/packages/SystemUI/compose/core/tests/src/com/android/compose/gesture/NestedDraggableTest.kt
+++ b/packages/SystemUI/compose/core/tests/src/com/android/compose/gesture/NestedDraggableTest.kt
@@ -972,6 +972,36 @@ class NestedDraggableTest(override val orientation: Orientation) : OrientationAw
}
@Test
+ fun isReadyToDrag() {
+ var isReadyToDrag by mutableStateOf(false)
+ val draggable = TestDraggable(isReadyToDrag = { isReadyToDrag })
+ val touchSlop =
+ rule.setContentWithTouchSlop {
+ Box(Modifier.fillMaxSize().nestedDraggable(draggable, orientation))
+ }
+
+ rule.onRoot().performTouchInput {
+ down(center)
+ moveBy((touchSlop + 10f).toOffset())
+ }
+
+ assertThat(draggable.onDragStartedCalled).isTrue()
+ assertThat(draggable.onDragDelta).isEqualTo(0f)
+
+ rule.onRoot().performTouchInput { moveBy(20f.toOffset()) }
+ assertThat(draggable.onDragDelta).isEqualTo(0f)
+
+ // Flag as ready to drag. We still ignore the next drag after that.
+ isReadyToDrag = true
+ rule.onRoot().performTouchInput { moveBy(30f.toOffset()) }
+ assertThat(draggable.onDragDelta).isEqualTo(0f)
+
+ // Now we drag.
+ rule.onRoot().performTouchInput { moveBy(40f.toOffset()) }
+ assertThat(draggable.onDragDelta).isEqualTo(40f)
+ }
+
+ @Test
fun consumeNestedPreScroll() {
var consumeNestedPreScroll by mutableStateOf(false)
val draggable = TestDraggable(shouldConsumeNestedPreScroll = { consumeNestedPreScroll })
@@ -1060,6 +1090,7 @@ class NestedDraggableTest(override val orientation: Orientation) : OrientationAw
},
private val shouldConsumeNestedPostScroll: (Float) -> Boolean = { true },
private val shouldConsumeNestedPreScroll: (Float) -> Boolean = { false },
+ private val isReadyToDrag: () -> Boolean = { true },
private val autoStopNestedDrags: Boolean = false,
) : NestedDraggable {
var shouldStartDrag = true
@@ -1092,6 +1123,9 @@ class NestedDraggableTest(override val orientation: Orientation) : OrientationAw
return object : NestedDraggable.Controller {
override val autoStopNestedDrags: Boolean = this@TestDraggable.autoStopNestedDrags
+ override val isReadyToDrag: Boolean
+ get() = isReadyToDrag()
+
override fun onDrag(delta: Float): Float {
onDragCalled = true
onDragDelta += delta
diff --git a/packages/SystemUI/compose/core/tests/src/com/android/compose/gesture/NestedScrollControllerTest.kt b/packages/SystemUI/compose/core/tests/src/com/android/compose/gesture/NestedScrollControllerTest.kt
index 424af3395913..377cbe238f49 100644
--- a/packages/SystemUI/compose/core/tests/src/com/android/compose/gesture/NestedScrollControllerTest.kt
+++ b/packages/SystemUI/compose/core/tests/src/com/android/compose/gesture/NestedScrollControllerTest.kt
@@ -23,9 +23,13 @@ import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
+import androidx.compose.ui.input.nestedscroll.NestedScrollSource
+import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.test.onRoot
import androidx.compose.ui.test.performTouchInput
@@ -103,4 +107,35 @@ class NestedScrollControllerTest {
rule.waitForIdle()
assertThat(state.isOuterScrollAllowed).isTrue()
}
+
+ @Test
+ fun supportsPreScrolls() {
+ val state = NestedScrollControlState()
+ rule.setContent {
+ Box(
+ Modifier.fillMaxSize()
+ .nestedScrollController(state)
+ .nestedScroll(
+ remember {
+ object : NestedScrollConnection {
+ override fun onPreScroll(
+ available: Offset,
+ source: NestedScrollSource,
+ ): Offset = available
+ }
+ }
+ )
+ .scrollable(rememberScrollableState { 0f }, Orientation.Vertical)
+ )
+ }
+
+ rule.onRoot().performTouchInput {
+ down(topLeft)
+ moveBy(Offset(0f, bottom))
+ }
+ assertThat(state.isOuterScrollAllowed).isFalse()
+
+ rule.onRoot().performTouchInput { up() }
+ assertThat(state.isOuterScrollAllowed).isTrue()
+ }
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PinInputDisplay.kt b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PinInputDisplay.kt
index 1f98cd8e07c0..90311ed93987 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PinInputDisplay.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PinInputDisplay.kt
@@ -35,7 +35,7 @@ import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
-import androidx.compose.foundation.layout.heightIn
+import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.foundation.layout.wrapContentSize
@@ -83,10 +83,7 @@ import kotlinx.coroutines.joinAll
import kotlinx.coroutines.launch
@Composable
-fun PinInputDisplay(
- viewModel: PinBouncerViewModel,
- modifier: Modifier = Modifier,
-) {
+fun PinInputDisplay(viewModel: PinBouncerViewModel, modifier: Modifier = Modifier) {
val hintedPinLength: Int? by viewModel.hintedPinLength.collectAsStateWithLifecycle()
val shapeAnimations = rememberShapeAnimations(viewModel.pinShapes)
@@ -173,7 +170,10 @@ private fun HintingPinInputDisplay(
LaunchedEffect(Unit) { playAnimation = true }
val dotColor = MaterialTheme.colorScheme.onSurfaceVariant
- Row(modifier = modifier.heightIn(min = shapeAnimations.shapeSize)) {
+ Row(
+ verticalAlignment = Alignment.CenterVertically,
+ modifier = modifier.height(shapeAnimations.shapeSize),
+ ) {
pinEntryDrawable.forEachIndexed { index, drawable ->
// Key the loop by [index] and [drawable], so that updating a shape drawable at the same
// index will play the new animation (by remembering a new [atEnd]).
@@ -316,17 +316,15 @@ private fun SimArea(viewModel: PinBouncerViewModel) {
Box(modifier = Modifier.padding(bottom = 20.dp)) {
// If isLockedEsim is null, then we do not show anything.
if (isLockedEsim == true) {
- PlatformOutlinedButton(
- onClick = { viewModel.onDisableEsimButtonClicked() },
- ) {
+ PlatformOutlinedButton(onClick = { viewModel.onDisableEsimButtonClicked() }) {
Row(
horizontalArrangement = Arrangement.spacedBy(10.dp),
- verticalAlignment = Alignment.CenterVertically
+ verticalAlignment = Alignment.CenterVertically,
) {
Image(
painter = painterResource(id = R.drawable.ic_no_sim),
contentDescription = null,
- colorFilter = ColorFilter.tint(MaterialTheme.colorScheme.onSurface)
+ colorFilter = ColorFilter.tint(MaterialTheme.colorScheme.onSurface),
)
Text(
text = stringResource(R.string.disable_carrier_button_text),
@@ -339,15 +337,13 @@ private fun SimArea(viewModel: PinBouncerViewModel) {
Image(
painter = painterResource(id = R.drawable.ic_lockscreen_sim),
contentDescription = null,
- colorFilter = ColorFilter.tint(colorResource(id = R.color.background_protected))
+ colorFilter = ColorFilter.tint(colorResource(id = R.color.background_protected)),
)
}
}
}
-private class PinInputRow(
- val shapeAnimations: ShapeAnimations,
-) {
+private class PinInputRow(val shapeAnimations: ShapeAnimations) {
private val entries = mutableStateListOf<PinInputEntry>()
@Composable
@@ -359,10 +355,11 @@ private class PinInputRow(
contentAlignment = Alignment.Center,
) {
Row(
- modifier
- .heightIn(min = shapeAnimations.shapeSize)
- // Pins overflowing horizontally should still be shown as scrolling.
- .wrapContentSize(unbounded = true)
+ verticalAlignment = Alignment.CenterVertically,
+ modifier =
+ Modifier.height(shapeAnimations.shapeSize)
+ // Pins overflowing horizontally should still be shown as scrolling.
+ .wrapContentSize(unbounded = true),
) {
entries.forEach { entry -> key(entry.digit) { entry.Content() } }
}
@@ -439,10 +436,7 @@ private class PinInputRow(
}
}
-private class PinInputEntry(
- val digit: Digit,
- val shapeAnimations: ShapeAnimations,
-) {
+private class PinInputEntry(val digit: Digit, val shapeAnimations: ShapeAnimations) {
private val shape = shapeAnimations.getShapeToDot(digit.sequenceNumber)
// horizontal space occupied, used to shift contents as individual digits are animated in/out
private val entryWidth =
@@ -474,7 +468,7 @@ private class PinInputEntry(
suspend fun animateRemoval() = coroutineScope {
awaitAll(
async { entryWidth.animateTo(0.dp, shapeAnimations.inputShiftAnimationSpec) },
- async { shapeSize.animateTo(0.dp, shapeAnimations.deleteShapeSizeAnimationSpec) }
+ async { shapeSize.animateTo(0.dp, shapeAnimations.deleteShapeSizeAnimationSpec) },
)
}
@@ -505,7 +499,7 @@ private class PinInputEntry(
layout(animatedEntryWidth.roundToPx(), shapeHeight.roundToPx()) {
placeable.place(
((animatedEntryWidth - animatedShapeSize) / 2f).roundToPx(),
- ((shapeHeight - animatedShapeSize) / 2f).roundToPx()
+ ((shapeHeight - animatedShapeSize) / 2f).roundToPx(),
)
}
},
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt
index 3150e94908cd..2b8fe39c4870 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt
@@ -47,6 +47,7 @@ import com.android.compose.animation.scene.observableTransitionState
import com.android.compose.animation.scene.rememberMutableSceneTransitionLayoutState
import com.android.compose.animation.scene.transitions
import com.android.compose.modifiers.thenIf
+import com.android.systemui.Flags
import com.android.systemui.communal.shared.model.CommunalBackgroundType
import com.android.systemui.communal.shared.model.CommunalScenes
import com.android.systemui.communal.shared.model.CommunalTransitionKeys
@@ -104,7 +105,9 @@ val sceneTransitionsV2 = transitions {
fade(Communal.Elements.Grid)
fade(Communal.Elements.IndicationArea)
fade(Communal.Elements.LockIcon)
- fade(Communal.Elements.StatusBar)
+ if (!Flags.glanceableHubV2()) {
+ fade(Communal.Elements.StatusBar)
+ }
}
timestampRange(startMillis = 167, endMillis = 334) { fade(Communal.Elements.Scrim) }
}
@@ -131,7 +134,9 @@ val sceneTransitions = transitions {
fade(Communal.Elements.Grid)
fade(Communal.Elements.IndicationArea)
fade(Communal.Elements.LockIcon)
- fade(Communal.Elements.StatusBar)
+ if (!Flags.glanceableHubV2()) {
+ fade(Communal.Elements.StatusBar)
+ }
}
timestampRange(startMillis = 167, endMillis = 334) { fade(Communal.Elements.Scrim) }
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContent.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContent.kt
index 2d03e2bcdd19..0181928317e1 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContent.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContent.kt
@@ -29,6 +29,7 @@ import androidx.compose.ui.unit.IntRect
import androidx.compose.ui.unit.dp
import androidx.compose.ui.zIndex
import com.android.compose.animation.scene.ContentScope
+import com.android.systemui.Flags
import com.android.systemui.communal.domain.interactor.CommunalSettingsInteractor
import com.android.systemui.communal.smartspace.SmartspaceInteractionHandler
import com.android.systemui.communal.ui.compose.section.AmbientStatusBarSection
@@ -70,8 +71,10 @@ constructor(
content = {
Box(modifier = Modifier.fillMaxSize()) {
with(communalPopupSection) { Popup() }
- with(ambientStatusBarSection) {
- AmbientStatusBar(modifier = Modifier.fillMaxWidth().zIndex(1f))
+ if (!Flags.glanceableHubV2()) {
+ with(ambientStatusBarSection) {
+ AmbientStatusBar(modifier = Modifier.fillMaxWidth().zIndex(1f))
+ }
}
CommunalHub(
viewModel = viewModel,
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/LockSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/LockSection.kt
index 0db2bb51c971..5fac6863e931 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/LockSection.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/LockSection.kt
@@ -33,6 +33,7 @@ import com.android.compose.animation.scene.ElementKey
import com.android.systemui.biometrics.AuthController
import com.android.systemui.customization.R as customR
import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.flags.FeatureFlagsClassic
import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.ui.binder.DeviceEntryIconViewBinder
@@ -49,12 +50,14 @@ import com.android.systemui.res.R
import com.android.systemui.statusbar.VibratorHelper
import dagger.Lazy
import javax.inject.Inject
+import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
class LockSection
@Inject
constructor(
@Application private val applicationScope: CoroutineScope,
+ @Main private val mainDispatcher: CoroutineDispatcher,
private val windowManager: WindowManager,
private val authController: AuthController,
private val featureFlags: FeatureFlagsClassic,
@@ -80,6 +83,7 @@ constructor(
id = R.id.device_entry_icon_view
DeviceEntryIconViewBinder.bind(
applicationScope,
+ mainDispatcher,
this,
deviceEntryIconViewModel.get(),
deviceEntryForegroundViewModel.get(),
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationScrimFlingBehavior.kt b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationScrimFlingBehavior.kt
new file mode 100644
index 000000000000..bc38ef0abb25
--- /dev/null
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationScrimFlingBehavior.kt
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2025 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.notifications.ui.composable
+
+import androidx.compose.animation.core.AnimationState
+import androidx.compose.animation.core.DecayAnimationSpec
+import androidx.compose.animation.core.animateDecay
+import androidx.compose.foundation.gestures.FlingBehavior
+import androidx.compose.foundation.gestures.ScrollScope
+import androidx.compose.ui.MotionDurationScale
+import com.android.systemui.scene.session.ui.composable.rememberSession
+import kotlinx.coroutines.CancellationException
+import kotlinx.coroutines.withContext
+import kotlin.math.abs
+
+/**
+ * Fork of [androidx.compose.foundation.gestures.DefaultFlingBehavior] to allow us to use it with
+ * [rememberSession].
+ */
+internal class NotificationScrimFlingBehavior(
+ private var flingDecay: DecayAnimationSpec<Float>,
+ private val motionDurationScale: MotionDurationScale = NotificationScrimMotionDurationScale
+) : FlingBehavior {
+ override suspend fun ScrollScope.performFling(initialVelocity: Float): Float {
+ // come up with the better threshold, but we need it since spline curve gives us NaNs
+ return withContext(motionDurationScale) {
+ if (abs(initialVelocity) > 1f) {
+ var velocityLeft = initialVelocity
+ var lastValue = 0f
+ val animationState =
+ AnimationState(
+ initialValue = 0f,
+ initialVelocity = initialVelocity,
+ )
+ try {
+ animationState.animateDecay(flingDecay) {
+ val delta = value - lastValue
+ val consumed = scrollBy(delta)
+ lastValue = value
+ velocityLeft = this.velocity
+ // avoid rounding errors and stop if anything is unconsumed
+ if (abs(delta - consumed) > 0.5f) this.cancelAnimation()
+ }
+ } catch (exception: CancellationException) {
+ velocityLeft = animationState.velocity
+ }
+ velocityLeft
+ } else {
+ initialVelocity
+ }
+ }
+ }
+}
+
+internal val NotificationScrimMotionDurationScale =
+ object : MotionDurationScale {
+ override val scaleFactor: Float
+ get() = 1f
+ } \ No newline at end of file
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt
index 09b8d178cc8e..79b346439d5d 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt
@@ -20,12 +20,13 @@ package com.android.systemui.notifications.ui.composable
import android.util.Log
import androidx.compose.animation.core.Animatable
import androidx.compose.animation.core.AnimationVector1D
+import androidx.compose.animation.core.DecayAnimationSpec
import androidx.compose.animation.core.tween
+import androidx.compose.animation.splineBasedDecay
import androidx.compose.foundation.ScrollState
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.gestures.Orientation
-import androidx.compose.foundation.gestures.ScrollableDefaults
import androidx.compose.foundation.gestures.animateScrollBy
import androidx.compose.foundation.gestures.rememberScrollableState
import androidx.compose.foundation.gestures.scrollBy
@@ -59,7 +60,6 @@ import androidx.compose.runtime.mutableFloatStateOf
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
-import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.runtime.snapshotFlow
import androidx.compose.ui.Alignment
@@ -97,6 +97,7 @@ import com.android.systemui.common.ui.compose.windowinsets.LocalScreenCornerRadi
import com.android.systemui.res.R
import com.android.systemui.scene.session.ui.composable.SaveableSession
import com.android.systemui.scene.session.ui.composable.rememberSession
+import com.android.systemui.scene.session.ui.composable.sessionCoroutineScope
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.shade.ui.composable.ShadeHeader
import com.android.systemui.statusbar.notification.stack.shared.model.AccessibilityScrollEvent
@@ -298,7 +299,7 @@ fun ContentScope.NotificationScrollingStack(
onEmptySpaceClick: (() -> Unit)? = null,
modifier: Modifier = Modifier,
) {
- val coroutineScope = rememberCoroutineScope()
+ val coroutineScope = shadeSession.sessionCoroutineScope()
val density = LocalDensity.current
val screenCornerRadius = LocalScreenCornerRadius.current
val scrimCornerRadius = dimensionResource(R.dimen.notification_scrim_corner_radius)
@@ -308,8 +309,6 @@ fun ContentScope.NotificationScrollingStack(
ScrollState(initial = 0)
}
val syntheticScroll = viewModel.syntheticScroll.collectAsStateWithLifecycle(0f)
- val isCurrentGestureOverscroll =
- viewModel.isCurrentGestureOverscroll.collectAsStateWithLifecycle(false)
val expansionFraction by viewModel.expandFraction.collectAsStateWithLifecycle(0f)
val shadeToQsFraction by viewModel.shadeToQsFraction.collectAsStateWithLifecycle(0f)
@@ -454,15 +453,15 @@ fun ContentScope.NotificationScrollingStack(
}
}
- val flingBehavior = ScrollableDefaults.flingBehavior()
val scrimNestedScrollConnection =
shadeSession.rememberSession(
scrimOffset,
- maxScrimTop,
minScrimTop,
- isCurrentGestureOverscroll,
- flingBehavior,
+ viewModel.isCurrentGestureOverscroll,
+ density,
) {
+ val flingSpec: DecayAnimationSpec<Float> = splineBasedDecay(density)
+ val flingBehavior = NotificationScrimFlingBehavior(flingSpec)
NotificationScrimNestedScrollConnection(
scrimOffset = { scrimOffset.value },
snapScrimOffset = { value -> coroutineScope.launch { scrimOffset.snapTo(value) } },
@@ -473,7 +472,7 @@ fun ContentScope.NotificationScrollingStack(
maxScrimOffset = 0f,
contentHeight = { stackHeight.intValue.toFloat() },
minVisibleScrimHeight = minVisibleScrimHeight,
- isCurrentGestureOverscroll = { isCurrentGestureOverscroll.value },
+ isCurrentGestureOverscroll = { viewModel.isCurrentGestureOverscroll },
flingBehavior = flingBehavior,
)
}
@@ -574,11 +573,11 @@ fun ContentScope.NotificationScrollingStack(
) {
Column(
modifier =
- Modifier.thenIf(supportNestedScrolling) {
+ Modifier.disableSwipesWhenScrolling(NestedScrollableBound.BottomRight)
+ .thenIf(supportNestedScrolling) {
Modifier.nestedScroll(scrimNestedScrollConnection)
}
.stackVerticalOverscroll(coroutineScope) { scrollState.canScrollForward }
- .disableSwipesWhenScrolling(NestedScrollableBound.BottomRight)
.verticalScroll(scrollState)
.padding(top = stackTopPadding, bottom = stackBottomPadding)
.fillMaxWidth()
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/qs/footer/ui/compose/FooterActions.kt b/packages/SystemUI/compose/features/src/com/android/systemui/qs/footer/ui/compose/FooterActions.kt
index 7782705d4c61..336f9e1ad6e3 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/qs/footer/ui/compose/FooterActions.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/qs/footer/ui/compose/FooterActions.kt
@@ -38,6 +38,7 @@ import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.CornerSize
import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
import androidx.compose.material3.Icon
import androidx.compose.material3.LocalContentColor
import androidx.compose.material3.MaterialTheme
@@ -81,6 +82,7 @@ import com.android.systemui.common.shared.model.Icon
import com.android.systemui.common.ui.compose.Icon
import com.android.systemui.compose.modifiers.sysuiResTag
import com.android.systemui.qs.flags.QSComposeFragment
+import com.android.systemui.qs.flags.QsInCompose
import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsButtonViewModel
import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsForegroundServicesButtonViewModel
import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsSecurityButtonViewModel
@@ -388,6 +390,7 @@ private fun NewChangesDot(modifier: Modifier = Modifier) {
}
/** A larger button with an icon, some text and an optional dot (to indicate new changes). */
+@OptIn(ExperimentalMaterial3ExpressiveApi::class)
@Composable
private fun TextButton(
icon: Icon,
@@ -422,10 +425,13 @@ private fun TextButton(
Text(
text,
Modifier.weight(1f),
- style = MaterialTheme.typography.bodyMedium,
- // TODO(b/242040009): Remove this letter spacing. We should only use the M3 text
- // styles without modifying them.
- letterSpacing = 0.01.em,
+ style =
+ if (QsInCompose.isEnabled) {
+ MaterialTheme.typography.labelLarge
+ } else {
+ MaterialTheme.typography.bodyMedium
+ },
+ letterSpacing = if (QsInCompose.isEnabled) 0.em else 0.01.em,
color = colorAttr(R.attr.onShadeInactiveVariant),
maxLines = 1,
overflow = TextOverflow.Ellipsis,
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/session/ui/composable/Session.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/session/ui/composable/Session.kt
index 4eaacf31e23d..1750b11a998b 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/session/ui/composable/Session.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/session/ui/composable/Session.kt
@@ -17,10 +17,14 @@
package com.android.systemui.scene.session.ui.composable
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.DisposableEffectResult
+import androidx.compose.runtime.DisposableEffectScope
+import androidx.compose.runtime.RememberObserver
import androidx.compose.runtime.SideEffect
import androidx.compose.runtime.currentCompositeKeyHash
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.rememberCompositionContext
import androidx.compose.runtime.saveable.Saver
import androidx.compose.runtime.saveable.SaverScope
import androidx.compose.runtime.saveable.mapSaver
@@ -28,6 +32,10 @@ import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import com.android.systemui.scene.session.shared.SessionStorage
import com.android.systemui.util.kotlin.mapValuesNotNullTo
+import kotlin.coroutines.CoroutineContext
+import kotlin.coroutines.EmptyCoroutineContext
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Job
/**
* An explicit storage for remembering composable state outside of the lifetime of a composition.
@@ -89,6 +97,55 @@ fun <T> Session.rememberSession(vararg inputs: Any?, key: String? = null, init:
rememberSession(key, *inputs, init = init)
/**
+ * A side effect of composition that must be reversed or cleaned up if the [Session] ends.
+ *
+ * @see androidx.compose.runtime.DisposableEffect
+ */
+@Composable
+fun Session.SessionDisposableEffect(
+ vararg inputs: Any?,
+ key: String? = null,
+ effect: DisposableEffectScope.() -> DisposableEffectResult,
+) {
+ rememberSession(inputs, key) {
+ object : RememberObserver {
+
+ var onDispose: DisposableEffectResult? = null
+
+ override fun onAbandoned() {
+ // no-op
+ }
+
+ override fun onForgotten() {
+ onDispose?.dispose()
+ onDispose = null
+ }
+
+ override fun onRemembered() {
+ onDispose = DisposableEffectScope().effect()
+ }
+ }
+ }
+}
+
+/**
+ * Return a [CoroutineScope] bound to this [Session] using the optional [CoroutineContext] provided
+ * by [getContext]. [getContext] will only be called once and the same [CoroutineScope] instance
+ * will be returned for the duration of the [Session].
+ *
+ * @see androidx.compose.runtime.rememberCoroutineScope
+ */
+@Composable
+fun Session.sessionCoroutineScope(
+ getContext: () -> CoroutineContext = { EmptyCoroutineContext }
+): CoroutineScope {
+ val effectContext: CoroutineContext = rememberCompositionContext().effectCoroutineContext
+ val job = rememberSession { Job() }
+ SessionDisposableEffect { onDispose { job.cancel() } }
+ return rememberSession { CoroutineScope(effectContext + job + getContext()) }
+}
+
+/**
* An explicit storage for remembering composable state outside of the lifetime of a composition.
*
* Specifically, this allows easy conversion of standard [rememberSession] invocations to ones that
@@ -147,15 +204,10 @@ interface SaveableSession : Session {
* location in the composition tree.
*/
@Composable
-fun rememberSaveableSession(
- vararg inputs: Any?,
- key: String? = null,
-): SaveableSession =
+fun rememberSaveableSession(vararg inputs: Any?, key: String? = null): SaveableSession =
rememberSaveable(*inputs, SaveableSessionImpl.SessionSaver, key) { SaveableSessionImpl() }
-private class SessionImpl(
- private val storage: SessionStorage = SessionStorage(),
-) : Session {
+private class SessionImpl(private val storage: SessionStorage = SessionStorage()) : Session {
@Composable
override fun <T> rememberSession(key: String?, vararg inputs: Any?, init: () -> T): T {
val storage = storage.storage
@@ -169,16 +221,31 @@ private class SessionImpl(
}
if (finalKey !in storage) {
val value = init()
- SideEffect { storage[finalKey] = SessionStorage.StorageEntry(inputs, value) }
+ SideEffect {
+ storage[finalKey] = SessionStorage.StorageEntry(inputs, value)
+ if (value is RememberObserver) {
+ value.onRemembered()
+ }
+ }
return value
}
val entry = storage[finalKey]!!
if (!inputs.contentEquals(entry.keys)) {
val value = init()
- SideEffect { entry.stored = value }
+ SideEffect {
+ val oldValue = entry.stored
+ if (oldValue is RememberObserver) {
+ oldValue.onForgotten()
+ }
+ entry.stored = value
+ if (value is RememberObserver) {
+ value.onRemembered()
+ }
+ }
return value
}
- @Suppress("UNCHECKED_CAST") return entry.stored as T
+ @Suppress("UNCHECKED_CAST")
+ return entry.stored as T
}
}
@@ -228,7 +295,8 @@ private class SaveableSessionImpl(
}
return value
}
- @Suppress("UNCHECKED_CAST") return entry.stored as T
+ @Suppress("UNCHECKED_CAST")
+ return entry.stored as T
}
}
}
@@ -263,7 +331,7 @@ private class SaveableSessionImpl(
v?.let { StorageEntry.Unrestored(v) }
}
)
- }
+ },
)
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/OverlayShade.kt b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/OverlayShade.kt
index 068218a0053a..b30e12f073ad 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/OverlayShade.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/OverlayShade.kt
@@ -55,6 +55,7 @@ import com.android.compose.animation.scene.LowestZIndexContentPicker
import com.android.compose.windowsizeclass.LocalWindowSizeClass
import com.android.mechanics.behavior.VerticalExpandContainerSpec
import com.android.mechanics.behavior.verticalExpandContainerBackground
+import com.android.systemui.Flags
import com.android.systemui.res.R
import com.android.systemui.shade.ui.composable.OverlayShade.rememberShadeExpansionMotion
@@ -189,9 +190,17 @@ object OverlayShade {
}
object Colors {
- val ScrimBackground = Color(0f, 0f, 0f, alpha = 0.2f)
+ val ScrimBackground = Color(0f, 0f, 0f, alpha = 0.3f)
val PanelBackground: Color
- @Composable @ReadOnlyComposable get() = MaterialTheme.colorScheme.surfaceContainer
+ @Composable
+ @ReadOnlyComposable
+ get() {
+ return if (Flags.notificationShadeBlur()) {
+ MaterialTheme.colorScheme.primaryContainer.copy(alpha = 0.4f)
+ } else {
+ MaterialTheme.colorScheme.surfaceContainer
+ }
+ }
}
object Dimensions {
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt
index 86c8fc34a63c..03debb6fa7ca 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt
@@ -43,6 +43,7 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
+import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
@@ -67,12 +68,15 @@ import com.android.compose.animation.scene.content.state.TransitionState
import com.android.compose.modifiers.thenIf
import com.android.compose.theme.colorAttr
import com.android.settingslib.Utils
+import com.android.systemui.Flags
import com.android.systemui.battery.BatteryMeterView
import com.android.systemui.battery.BatteryMeterViewController
import com.android.systemui.common.ui.compose.windowinsets.CutoutLocation
import com.android.systemui.common.ui.compose.windowinsets.LocalDisplayCutout
import com.android.systemui.common.ui.compose.windowinsets.LocalScreenCornerRadius
import com.android.systemui.compose.modifiers.sysuiResTag
+import com.android.systemui.kairos.ExperimentalKairosApi
+import com.android.systemui.kairos.buildSpec
import com.android.systemui.privacy.OngoingPrivacyChip
import com.android.systemui.res.R
import com.android.systemui.scene.shared.model.Scenes
@@ -86,8 +90,12 @@ import com.android.systemui.shade.ui.viewmodel.ShadeHeaderViewModel.HeaderChipHi
import com.android.systemui.statusbar.phone.StatusBarLocation
import com.android.systemui.statusbar.phone.StatusIconContainer
import com.android.systemui.statusbar.pipeline.mobile.ui.view.ModernShadeCarrierGroupMobileView
+import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.MobileIconsViewModelKairosComposeWrapper
import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.ShadeCarrierGroupMobileIconViewModel
+import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.ShadeCarrierGroupMobileIconViewModelKairos
+import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.composeWrapper
import com.android.systemui.statusbar.policy.Clock
+import com.android.systemui.util.composable.kairos.ActivatedKairosSpec
object ShadeHeader {
object Elements {
@@ -285,6 +293,25 @@ fun ContentScope.OverlayShadeHeader(
viewModel: ShadeHeaderViewModel,
modifier: Modifier = Modifier,
) {
+ OverlayShadeHeaderPartialStateless(
+ viewModel,
+ viewModel.showClock,
+ modifier,
+ )
+}
+
+/**
+ * Ideally, we should have a stateless function for overlay shade header, which facilitates testing.
+ * However, it is cumbersome to implement such a stateless function, especially when some of the
+ * overlay shade header's children accept a view model as the param. Therefore, this function only
+ * break up the clock visibility. It is where "PartialStateless" comes from.
+ */
+@Composable
+fun ContentScope.OverlayShadeHeaderPartialStateless(
+ viewModel: ShadeHeaderViewModel,
+ showClock: Boolean,
+ modifier: Modifier = Modifier,
+) {
val horizontalPadding =
max(LocalScreenCornerRadius.current / 2f, Shade.Dimensions.HorizontalPadding)
@@ -300,7 +327,7 @@ fun ContentScope.OverlayShadeHeader(
modifier = Modifier.padding(horizontal = horizontalPadding),
) {
val chipHighlight = viewModel.notificationsChipHighlight
- if (viewModel.showClock) {
+ if (showClock) {
Clock(
onClick = viewModel::onClockClicked,
modifier = Modifier.padding(horizontal = 4.dp),
@@ -520,8 +547,14 @@ private fun BatteryIcon(
)
}
+@OptIn(ExperimentalKairosApi::class)
@Composable
private fun ShadeCarrierGroup(viewModel: ShadeHeaderViewModel, modifier: Modifier = Modifier) {
+ if (Flags.statusBarMobileIconKairos()) {
+ ShadeCarrierGroupKairos(viewModel, modifier)
+ return
+ }
+
Row(modifier = modifier, horizontalArrangement = Arrangement.spacedBy(5.dp)) {
for (subId in viewModel.mobileSubIds) {
AndroidView(
@@ -543,6 +576,49 @@ private fun ShadeCarrierGroup(viewModel: ShadeHeaderViewModel, modifier: Modifie
}
}
+@ExperimentalKairosApi
+@Composable
+private fun ShadeCarrierGroupKairos(
+ viewModel: ShadeHeaderViewModel,
+ modifier: Modifier = Modifier,
+) {
+ Row(modifier = modifier) {
+ ActivatedKairosSpec(
+ buildSpec = viewModel.mobileIconsViewModelKairos.get().composeWrapper(),
+ kairosNetwork = viewModel.kairosNetwork,
+ ) { iconsViewModel: MobileIconsViewModelKairosComposeWrapper ->
+ for ((subId, icon) in iconsViewModel.icons) {
+ Spacer(modifier = Modifier.width(5.dp))
+ val scope = rememberCoroutineScope()
+ AndroidView(
+ factory = { context ->
+ ModernShadeCarrierGroupMobileView.constructAndBind(
+ context = context,
+ logger = iconsViewModel.logger,
+ slot = "mobile_carrier_shade_group",
+ viewModel =
+ buildSpec {
+ ShadeCarrierGroupMobileIconViewModelKairos(
+ icon,
+ icon.iconInteractor,
+ )
+ },
+ scope = scope,
+ subscriptionId = subId,
+ location = StatusBarLocation.SHADE_CARRIER_GROUP,
+ kairosNetwork = viewModel.kairosNetwork,
+ )
+ .first
+ .also {
+ it.setOnClickListener { viewModel.onShadeCarrierGroupClicked() }
+ }
+ }
+ )
+ }
+ }
+ }
+}
+
@Composable
private fun ContentScope.StatusIcons(
viewModel: ShadeHeaderViewModel,
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/ColumnVolumeSliders.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/ColumnVolumeSliders.kt
index d9e8f02f005b..52b1e3aeb00c 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/ColumnVolumeSliders.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/ColumnVolumeSliders.kt
@@ -30,9 +30,11 @@ import androidx.compose.animation.scaleOut
import androidx.compose.animation.shrinkVertically
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.RowScope
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.IconButtonDefaults
@@ -42,7 +44,6 @@ import androidx.compose.runtime.State
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
-import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.semantics.Role
@@ -154,9 +155,7 @@ fun ColumnVolumeSliders(
totalCount = viewModels.size,
),
)
- .thenIf(!Flags.volumeRedesign()) {
- Modifier.padding(top = 16.dp)
- },
+ .padding(top = if (Flags.volumeRedesign()) 4.dp else 16.dp),
state = sliderState,
onValueChange = { newValue: Float ->
sliderViewModel.onValueChanged(sliderState, newValue)
@@ -223,7 +222,7 @@ private fun ExpandButtonLegacy(
}
@Composable
-private fun ExpandButton(
+private fun RowScope.ExpandButton(
isExpanded: Boolean,
isExpandable: Boolean,
onExpandedChanged: (Boolean) -> Unit,
@@ -243,16 +242,17 @@ private fun ExpandButton(
) {
PlatformIconButton(
modifier =
- Modifier.size(width = 48.dp, height = 40.dp).semantics {
+ Modifier.size(40.dp).semantics {
role = Role.Switch
stateDescription = expandButtonStateDescription
},
onClick = { onExpandedChanged(!isExpanded) },
colors =
IconButtonDefaults.iconButtonColors(
- containerColor = Color.Transparent,
- contentColor = MaterialTheme.colorScheme.onSurfaceVariant,
+ containerColor = MaterialTheme.colorScheme.surfaceContainerHighest,
+ contentColor = MaterialTheme.colorScheme.onSurface,
),
+ shape = RoundedCornerShape(12.dp),
iconResource =
if (isExpanded) {
R.drawable.ic_arrow_down_24dp
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/VolumeSlider.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/VolumeSlider.kt
index 4b3ebc2bd53d..a65afad9cb7a 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/VolumeSlider.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/VolumeSlider.kt
@@ -30,14 +30,16 @@ import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.RowScope
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
-import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.material3.ExperimentalMaterial3Api
+import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
import androidx.compose.material3.Icon as MaterialIcon
import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.SliderDefaults
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
@@ -60,28 +62,31 @@ import androidx.compose.ui.semantics.disabled
import androidx.compose.ui.semantics.progressBarRangeInfo
import androidx.compose.ui.semantics.setProgress
import androidx.compose.ui.semantics.stateDescription
+import androidx.compose.ui.unit.DpSize
import androidx.compose.ui.unit.dp
import com.android.compose.PlatformSlider
import com.android.compose.PlatformSliderColors
import com.android.systemui.Flags
-import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.common.shared.model.Icon as IconModel
import com.android.systemui.common.ui.compose.Icon
import com.android.systemui.compose.modifiers.sysuiResTag
import com.android.systemui.haptics.slider.SliderHapticFeedbackFilter
import com.android.systemui.haptics.slider.compose.ui.SliderHapticsViewModel
import com.android.systemui.lifecycle.rememberViewModel
import com.android.systemui.res.R
+import com.android.systemui.volume.dialog.sliders.ui.compose.SliderTrack
import com.android.systemui.volume.haptics.ui.VolumeHapticsConfigsProvider
import com.android.systemui.volume.panel.component.volume.slider.ui.viewmodel.SliderState
-import com.android.systemui.volume.ui.slider.AccessibilityParams
-import com.android.systemui.volume.ui.slider.Haptics
-import com.android.systemui.volume.ui.slider.Slider
+import com.android.systemui.volume.ui.compose.slider.AccessibilityParams
+import com.android.systemui.volume.ui.compose.slider.Haptics
+import com.android.systemui.volume.ui.compose.slider.Slider
+import com.android.systemui.volume.ui.compose.slider.SliderIcon
import kotlin.math.round
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.map
-@OptIn(ExperimentalMaterial3Api::class)
+@OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterial3ExpressiveApi::class)
@Composable
fun VolumeSlider(
state: SliderState,
@@ -91,7 +96,7 @@ fun VolumeSlider(
modifier: Modifier = Modifier,
hapticsViewModelFactory: SliderHapticsViewModel.Factory?,
onValueChangeFinished: (() -> Unit)? = null,
- button: (@Composable () -> Unit)? = null,
+ button: (@Composable RowScope.() -> Unit)? = null,
) {
if (!Flags.volumeRedesign()) {
LegacyVolumeSlider(
@@ -106,52 +111,88 @@ fun VolumeSlider(
return
}
- Column(modifier = modifier.animateContentSize(), verticalArrangement = Arrangement.Top) {
+ Column(modifier = modifier.animateContentSize()) {
+ Text(
+ text = state.label,
+ style = MaterialTheme.typography.titleMedium,
+ color = MaterialTheme.colorScheme.onSurface,
+ modifier = Modifier.fillMaxWidth().clearAndSetSemantics {},
+ )
Row(
- horizontalArrangement = Arrangement.spacedBy(12.dp),
- modifier = Modifier.fillMaxWidth().height(40.dp),
+ horizontalArrangement = Arrangement.spacedBy(8.dp),
+ modifier = Modifier.fillMaxWidth().padding(vertical = 4.dp),
verticalAlignment = Alignment.CenterVertically,
) {
- state.icon?.let {
- Icon(
- icon = it,
- tint = MaterialTheme.colorScheme.onSurface,
- modifier = Modifier.size(24.dp),
+ val materialSliderColors =
+ SliderDefaults.colors(
+ activeTickColor = MaterialTheme.colorScheme.surfaceContainerHigh,
+ inactiveTrackColor = MaterialTheme.colorScheme.surfaceContainerHigh,
+ disabledActiveTickColor = MaterialTheme.colorScheme.surfaceContainerHigh,
+ disabledInactiveTrackColor = MaterialTheme.colorScheme.surfaceContainerHigh,
)
- }
- Text(
- text = state.label,
- style = MaterialTheme.typography.titleMedium,
- color = MaterialTheme.colorScheme.onSurface,
- modifier = Modifier.weight(1f),
+ Slider(
+ value = state.value,
+ valueRange = state.valueRange,
+ onValueChanged = onValueChange,
+ onValueChangeFinished = { onValueChangeFinished?.invoke() },
+ colors = materialSliderColors,
+ isEnabled = state.isEnabled,
+ stepDistance = state.step,
+ accessibilityParams =
+ AccessibilityParams(
+ contentDescription = state.a11yContentDescription,
+ stateDescription = state.a11yStateDescription,
+ ),
+ track = { sliderState ->
+ SliderTrack(
+ sliderState = sliderState,
+ colors = materialSliderColors,
+ isEnabled = state.isEnabled,
+ activeTrackStartIcon =
+ state.icon?.let { icon ->
+ { iconsState ->
+ SliderIcon(
+ icon = {
+ Icon(icon = icon, modifier = Modifier.size(24.dp))
+ },
+ isVisible = iconsState.isActiveTrackStartIconVisible,
+ )
+ }
+ },
+ inactiveTrackStartIcon =
+ state.icon?.let { icon ->
+ { iconsState ->
+ SliderIcon(
+ icon = {
+ Icon(icon = icon, modifier = Modifier.size(24.dp))
+ },
+ isVisible = !iconsState.isActiveTrackStartIconVisible,
+ )
+ }
+ },
+ )
+ },
+ thumb = { sliderState, interactionSource ->
+ SliderDefaults.Thumb(
+ sliderState = sliderState,
+ interactionSource = interactionSource,
+ enabled = state.isEnabled,
+ colors = materialSliderColors,
+ thumbSize = DpSize(4.dp, 52.dp),
+ )
+ },
+ haptics =
+ hapticsViewModelFactory?.let {
+ Haptics.Enabled(
+ hapticsViewModelFactory = it,
+ hapticFilter = state.hapticFilter,
+ orientation = Orientation.Horizontal,
+ )
+ } ?: Haptics.Disabled,
+ modifier = Modifier.weight(1f).sysuiResTag(state.label),
)
- button?.invoke()
+ button?.invoke(this)
}
-
- Slider(
- value = state.value,
- valueRange = state.valueRange,
- onValueChanged = onValueChange,
- onValueChangeFinished = { onValueChangeFinished?.invoke() },
- isEnabled = state.isEnabled,
- stepDistance = state.a11yStep,
- accessibilityParams =
- AccessibilityParams(
- label = state.label,
- disabledMessage = state.disabledMessage,
- currentStateDescription = state.a11yStateDescription,
- ),
- haptics =
- hapticsViewModelFactory?.let {
- Haptics.Enabled(
- hapticsViewModelFactory = it,
- hapticFilter = state.hapticFilter,
- orientation = Orientation.Horizontal,
- )
- } ?: Haptics.Disabled,
- modifier =
- Modifier.height(40.dp).padding(top = 4.dp, bottom = 12.dp).sysuiResTag(state.label),
- )
state.disabledMessage?.let { disabledMessage ->
AnimatedVisibility(visible = !state.isEnabled) {
Row(
@@ -169,7 +210,7 @@ fun VolumeSlider(
text = disabledMessage,
color = MaterialTheme.colorScheme.onSurfaceVariant,
style = MaterialTheme.typography.labelSmall,
- modifier = Modifier.basicMarquee(),
+ modifier = Modifier.basicMarquee().clearAndSetSemantics {},
)
}
}
@@ -229,7 +270,7 @@ private fun LegacyVolumeSlider(
}
val newValue =
- (value + targetDirection * state.a11yStep).coerceIn(
+ (value + targetDirection * state.step).coerceIn(
state.valueRange.start,
state.valueRange.endInclusive,
)
@@ -250,7 +291,11 @@ private fun LegacyVolumeSlider(
enabled = state.isEnabled,
icon = {
state.icon?.let {
- SliderIcon(icon = it, onIconTapped = onIconTapped, isTappable = state.isMutable)
+ LegacySliderIcon(
+ icon = it,
+ onIconTapped = onIconTapped,
+ isTappable = state.isMutable,
+ )
}
},
colors = sliderColors,
@@ -286,8 +331,8 @@ private fun valueState(state: SliderState): State<Float> {
}
@Composable
-private fun SliderIcon(
- icon: Icon,
+private fun LegacySliderIcon(
+ icon: IconModel,
onIconTapped: () -> Unit,
isTappable: Boolean,
modifier: Modifier = Modifier,
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateContent.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateContent.kt
index b04d89d8160f..0b0df06c2015 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateContent.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateContent.kt
@@ -20,6 +20,7 @@ import androidx.compose.animation.core.Animatable
import androidx.compose.animation.core.AnimationVector1D
import androidx.compose.animation.core.SpringSpec
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
+import androidx.compose.runtime.withFrameNanos
import com.android.compose.animation.scene.content.state.TransitionState
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
@@ -47,6 +48,11 @@ internal fun CoroutineScope.animateContent(
oneOffAnimation.animatable = it
}
+ if (layoutState.deferTransitionProgress) {
+ // Defer the animation by one frame so that the transition progress is changed only when
+ // the expensive first composition frame is done.
+ withFrameNanos {}
+ }
animatable.animateTo(targetProgress, animationSpec, initialVelocity)
}
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt
index 024ca22069ae..36eafa400090 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt
@@ -290,6 +290,15 @@ private class DragControllerImpl(
val isDrivingTransition: Boolean
get() = layoutState.transitionState == swipeAnimation.contentTransition
+ override val isReadyToDrag: Boolean
+ get() {
+ return !layoutState.deferTransitionProgress ||
+ with(draggableHandler.layoutImpl.elementStateScope) {
+ swipeAnimation.fromContent.targetSize() != null &&
+ swipeAnimation.toContent.targetSize() != null
+ }
+ }
+
init {
check(!isDrivingTransition) { "Multiple controllers with the same SwipeTransition" }
}
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt
index 4da83c3a6fc9..a8b676d4ee45 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt
@@ -462,7 +462,9 @@ internal class SceneTransitionLayoutImpl(
// swipes.
.swipeToScene(horizontalDraggableHandler)
.swipeToScene(verticalDraggableHandler)
- .then(LayoutElement(layoutImpl = this))
+ .then(
+ LayoutElement(layoutImpl = this, transitionState = this.state.transitionState)
+ )
) {
LookaheadScope {
if (_lookaheadScope == null) {
@@ -623,23 +625,28 @@ internal class SceneTransitionLayoutImpl(
@VisibleForTesting internal fun overlaysOrNullForTest(): Map<OverlayKey, Overlay>? = _overlays
}
-private data class LayoutElement(private val layoutImpl: SceneTransitionLayoutImpl) :
- ModifierNodeElement<LayoutNode>() {
- override fun create(): LayoutNode = LayoutNode(layoutImpl)
+private data class LayoutElement(
+ private val layoutImpl: SceneTransitionLayoutImpl,
+ private val transitionState: TransitionState,
+) : ModifierNodeElement<LayoutNode>() {
+ override fun create(): LayoutNode = LayoutNode(layoutImpl, transitionState)
override fun update(node: LayoutNode) {
node.layoutImpl = layoutImpl
+ node.transitionState = transitionState
}
}
-private class LayoutNode(var layoutImpl: SceneTransitionLayoutImpl) :
- Modifier.Node(), ApproachLayoutModifierNode, LayoutAwareModifierNode {
+private class LayoutNode(
+ var layoutImpl: SceneTransitionLayoutImpl,
+ var transitionState: TransitionState,
+) : Modifier.Node(), ApproachLayoutModifierNode, LayoutAwareModifierNode {
override fun onRemeasured(size: IntSize) {
layoutImpl.lastSize = size
}
override fun isMeasurementApproachInProgress(lookaheadSize: IntSize): Boolean {
- return layoutImpl.state.isTransitioning()
+ return transitionState is TransitionState.Transition.ChangeScene
}
@ExperimentalComposeUiApi
@@ -652,8 +659,7 @@ private class LayoutNode(var layoutImpl: SceneTransitionLayoutImpl) :
val width: Int
val height: Int
- val transition =
- layoutImpl.state.currentTransition as? TransitionState.Transition.ChangeScene
+ val transition = transitionState as? TransitionState.Transition.ChangeScene
if (transition == null) {
width = placeable.width
height = placeable.height
@@ -662,6 +668,9 @@ private class LayoutNode(var layoutImpl: SceneTransitionLayoutImpl) :
val fromSize = layoutImpl.scene(transition.fromScene).targetSize
val toSize = layoutImpl.scene(transition.toScene).targetSize
+ check(fromSize != Element.SizeUnspecified) { "fromSize is unspecified " }
+ check(toSize != Element.SizeUnspecified) { "toSize is unspecified" }
+
// Optimization: make sure we don't read state.progress if fromSize ==
// toSize to avoid running this code every frame when the layout size does
// not change.
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt
index 56e8c458ad67..4e28dd569f21 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt
@@ -234,6 +234,10 @@ sealed interface MutableSceneTransitionLayoutState : SceneTransitionLayoutState
* `from` overlay by `to` overlay.
* @param stateLinks the [StateLink] connecting this [SceneTransitionLayoutState] to other
* [SceneTransitionLayoutState]s.
+ * @param deferTransitionProgress whether we should wait for the first composition to be done before
+ * changing the progress of a transition. This can help reduce perceivable jank at the start of a
+ * transition in case the first composition of a content takes a lot of time and we are going to
+ * miss that first frame.
*/
fun MutableSceneTransitionLayoutState(
initialScene: SceneKey,
@@ -246,6 +250,9 @@ fun MutableSceneTransitionLayoutState(
canReplaceOverlay: (from: OverlayKey, to: OverlayKey) -> Boolean = { _, _ -> true },
onTransitionStart: (TransitionState.Transition) -> Unit = {},
onTransitionEnd: (TransitionState.Transition) -> Unit = {},
+
+ // TODO(b/400688335): Turn on by default and remove this flag before flexiglass is released.
+ deferTransitionProgress: Boolean = false,
): MutableSceneTransitionLayoutState {
return MutableSceneTransitionLayoutStateImpl(
initialScene,
@@ -258,6 +265,7 @@ fun MutableSceneTransitionLayoutState(
canReplaceOverlay,
onTransitionStart,
onTransitionEnd,
+ deferTransitionProgress,
)
}
@@ -272,6 +280,9 @@ fun rememberMutableSceneTransitionLayoutState(
canReplaceOverlay: (from: OverlayKey, to: OverlayKey) -> Boolean = { _, _ -> true },
onTransitionStart: (TransitionState.Transition) -> Unit = {},
onTransitionEnd: (TransitionState.Transition) -> Unit = {},
+
+ // TODO(b/400688335): Turn on by default and remove this flag before flexiglass is released.
+ deferTransitionProgress: Boolean = false,
): MutableSceneTransitionLayoutState {
val motionScheme = MaterialTheme.motionScheme
val layoutState = remember {
@@ -286,6 +297,7 @@ fun rememberMutableSceneTransitionLayoutState(
canReplaceOverlay = canReplaceOverlay,
onTransitionStart = onTransitionStart,
onTransitionEnd = onTransitionEnd,
+ deferTransitionProgress = deferTransitionProgress,
)
}
@@ -298,6 +310,7 @@ fun rememberMutableSceneTransitionLayoutState(
layoutState.canReplaceOverlay = canReplaceOverlay
layoutState.onTransitionStart = onTransitionStart
layoutState.onTransitionEnd = onTransitionEnd
+ layoutState.deferTransitionProgress = deferTransitionProgress
}
return layoutState
}
@@ -317,6 +330,8 @@ internal class MutableSceneTransitionLayoutStateImpl(
},
internal var onTransitionStart: (TransitionState.Transition) -> Unit = {},
internal var onTransitionEnd: (TransitionState.Transition) -> Unit = {},
+ // TODO(b/400688335): Turn on by default and remove this flag before flexiglass is released.
+ internal var deferTransitionProgress: Boolean = false,
) : MutableSceneTransitionLayoutState {
private val creationThread: Thread = Thread.currentThread()
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/UserActionDistanceScopeImpl.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/UserActionDistanceScopeImpl.kt
index 2d2a81542f84..5d4232d8a8b7 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/UserActionDistanceScopeImpl.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/UserActionDistanceScopeImpl.kt
@@ -38,7 +38,7 @@ internal class ElementStateScopeImpl(private val layoutImpl: SceneTransitionLayo
}
override fun ContentKey.targetSize(): IntSize? {
- return layoutImpl.content(this).targetSize.takeIf { it != IntSize.Zero }
+ return layoutImpl.content(this).targetSize.takeIf { it != Element.SizeUnspecified }
}
}
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/content/Content.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/content/Content.kt
index 90bf92ae1dd0..7492f3737edc 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/content/Content.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/content/Content.kt
@@ -93,9 +93,10 @@ internal sealed class Content(
val containerState = ContainerState()
// Important: All fields in this class should be backed by State given that contents are updated
- // directly during composition, outside of a SideEffect.
+ // directly during composition, outside of a SideEffect, or are observed during composition,
+ // layout or drawing.
var content by mutableStateOf(content)
- var targetSize by mutableStateOf(IntSize.Zero)
+ var targetSize by mutableStateOf(Element.SizeUnspecified)
var userActions by mutableStateOf(actions)
var zIndex by mutableFloatStateOf(zIndex)
@@ -212,9 +213,17 @@ private class ContentNode(
return if (isElevationPossible) delegate(ContainerNode(content.containerState)) else null
}
+ override fun onDetach() {
+ this.content.targetSize = Element.SizeUnspecified
+ }
+
fun update(content: Content, isElevationPossible: Boolean, isInvisible: Boolean) {
- if (content != this.content || isElevationPossible != this.isElevationPossible) {
+ if (content != this.content) {
+ this.content.targetSize = Element.SizeUnspecified
this.content = content
+ }
+
+ if (content != this.content || isElevationPossible != this.isElevationPossible) {
this.isElevationPossible = isElevationPossible
containerDelegate?.let { undelegate(it) }
diff --git a/packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_gesture_dragFullyClose.json b/packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_gesture_dragFullyClose.json
index 5dbb01338090..770f85969ed1 100644
--- a/packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_gesture_dragFullyClose.json
+++ b/packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_gesture_dragFullyClose.json
@@ -76,7 +76,7 @@
},
{
"x": 5.2,
- "y": 5.6
+ "y": 5.2
},
{
"x": 5.2,
@@ -326,11 +326,11 @@
},
{
"width": 150,
- "height": 293.2
+ "height": 292.8
},
{
"width": 150,
- "height": 293.2
+ "height": 292.8
},
{
"width": 150,
diff --git a/packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_gesture_dragHalfClose.json b/packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_gesture_dragHalfClose.json
index 1543d186ea03..6b7de56c1da6 100644
--- a/packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_gesture_dragHalfClose.json
+++ b/packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_gesture_dragHalfClose.json
@@ -81,8 +81,8 @@
"y": 5.2
},
{
- "x": 5.6,
- "y": 6.8
+ "x": 5.2,
+ "y": 5.2
},
{
"x": 5.2,
@@ -344,7 +344,7 @@
},
{
"width": 150,
- "height": 294
+ "height": 292.4
},
{
"width": 150,
diff --git a/packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_gesture_flingClose.json b/packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_gesture_flingClose.json
index 115483cf4013..015df8fd02fb 100644
--- a/packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_gesture_flingClose.json
+++ b/packages/SystemUI/compose/scene/tests/goldens/edge_verticalReveal_gesture_flingClose.json
@@ -84,8 +84,8 @@
"y": 5.2
},
{
- "x": 5.6,
- "y": 6.4
+ "x": 5.2,
+ "y": 5.2
},
{
"x": 5.2,
@@ -259,7 +259,7 @@
},
{
"width": 150,
- "height": 290
+ "height": 288.8
},
{
"width": 150,
@@ -279,34 +279,34 @@
},
{
"width": 150,
- "height": 223.6
+ "height": 224
},
{
"width": 150,
- "height": 182.8
+ "height": 183.2
},
{
"width": 150,
- "height": 141.2
+ "height": 141.6
},
{
"width": 150,
- "height": 104
+ "height": 104.4
},
{
- "width": 147.6,
- "height": 74
+ "width": 148,
+ "height": 74.4
},
{
"width": 139.6,
- "height": 50.8
+ "height": 51.2
},
{
"width": 133.6,
- "height": 32
+ "height": 32.4
},
{
- "width": 129.2,
+ "width": 129.6,
"height": 15.6
},
{
@@ -409,19 +409,19 @@
1,
1,
1,
- 0.99479187,
- 0.8575029,
- 0.65572864,
- 0.4691311,
- 0.3215357,
- 0.21380007,
- 0.13896108,
- 0.0887118,
- 0.05580789,
- 0.03467691,
- 0.021318138,
- 0.0129826665,
- 0.007839739,
+ 0.99532944,
+ 0.8594856,
+ 0.65783304,
+ 0.47089338,
+ 0.32286334,
+ 0.21474117,
+ 0.139602,
+ 0.089136004,
+ 0.056082606,
+ 0.03485179,
+ 0.02142787,
+ 0.013050735,
+ 0.007881463,
0,
0,
0,
diff --git a/packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_gesture_dragFullyClose.json b/packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_gesture_dragFullyClose.json
index f44d4cd7c14e..5ac06217d161 100644
--- a/packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_gesture_dragFullyClose.json
+++ b/packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_gesture_dragFullyClose.json
@@ -72,7 +72,7 @@
},
{
"x": 5.2,
- "y": 5.6
+ "y": 5.2
},
{
"x": 5.2,
@@ -306,11 +306,11 @@
},
{
"width": 150,
- "height": 293.2
+ "height": 292.8
},
{
"width": 150,
- "height": 293.2
+ "height": 292.8
},
{
"width": 150,
diff --git a/packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_gesture_dragHalfClose.json b/packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_gesture_dragHalfClose.json
index 9b68c71a7a34..1cae67b3eba7 100644
--- a/packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_gesture_dragHalfClose.json
+++ b/packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_gesture_dragHalfClose.json
@@ -79,8 +79,8 @@
"y": 5.2
},
{
- "x": 5.6,
- "y": 6.8
+ "x": 5.2,
+ "y": 5.2
},
{
"x": 5.2,
@@ -334,7 +334,7 @@
},
{
"width": 150,
- "height": 294
+ "height": 292.4
},
{
"width": 150,
diff --git a/packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_gesture_flingClose.json b/packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_gesture_flingClose.json
index 86805bd6ff29..ca87bc28c45d 100644
--- a/packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_gesture_flingClose.json
+++ b/packages/SystemUI/compose/scene/tests/goldens/floating_verticalReveal_gesture_flingClose.json
@@ -83,8 +83,8 @@
"y": 5.2
},
{
- "x": 5.6,
- "y": 6.4
+ "x": 5.2,
+ "y": 5.2
},
{
"x": 5.2,
@@ -254,7 +254,7 @@
},
{
"width": 150,
- "height": 290
+ "height": 288.8
},
{
"width": 150,
@@ -274,27 +274,27 @@
},
{
"width": 150,
- "height": 223.6
+ "height": 224
},
{
"width": 150,
- "height": 182.8
+ "height": 183.2
},
{
"width": 150,
- "height": 141.2
+ "height": 141.6
},
{
"width": 150,
- "height": 104
+ "height": 104.4
},
{
"width": 150,
- "height": 72
+ "height": 72.4
},
{
"width": 150,
- "height": 46
+ "height": 46.4
},
{
"width": 150,
@@ -400,19 +400,19 @@
1,
1,
1,
- 0.99479187,
- 0.8575029,
- 0.65572864,
- 0.4691311,
- 0.3215357,
- 0.21380007,
- 0.13896108,
- 0.0887118,
- 0.05580789,
- 0.03467691,
- 0.021318138,
- 0.0129826665,
- 0.007839739,
+ 0.99532944,
+ 0.8594856,
+ 0.65783304,
+ 0.47089338,
+ 0.32286334,
+ 0.21474117,
+ 0.139602,
+ 0.089136004,
+ 0.056082606,
+ 0.03485179,
+ 0.02142787,
+ 0.013050735,
+ 0.007881463,
0,
0,
0,
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/testing/ElementStateAccessTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/testing/ElementStateAccessTest.kt
index e4a87ba3a26b..7d9a32b3948a 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/testing/ElementStateAccessTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/testing/ElementStateAccessTest.kt
@@ -58,17 +58,24 @@ class ElementStateAccessTest {
assertThat(semanticNode.lastScaleForTesting).isEqualTo(Scale(1f, 1f))
}
+ at(16) {
+ val semanticNode = onElement(TestElements.Foo).fetchSemanticsNode()
+ assertThat(semanticNode.lastAlphaForTesting).isEqualTo(0.75f)
+ assertThat(semanticNode.lastScaleForTesting).isEqualTo(Scale(0.75f, 0.75f))
+ }
+
at(32) {
val semanticNode = onElement(TestElements.Foo).fetchSemanticsNode()
assertThat(semanticNode.lastAlphaForTesting).isEqualTo(0.5f)
assertThat(semanticNode.lastScaleForTesting).isEqualTo(Scale(0.5f, 0.5f))
}
- at(64) {
+ at(48) {
val semanticNode = onElement(TestElements.Foo).fetchSemanticsNode()
- assertThat(semanticNode.lastAlphaForTesting).isEqualTo(0f)
- assertThat(semanticNode.lastScaleForTesting).isEqualTo(Scale(0f, 0f))
+ assertThat(semanticNode.lastAlphaForTesting).isEqualTo(0.25f)
+ assertThat(semanticNode.lastScaleForTesting).isEqualTo(Scale(0.25f, 0.25f))
}
+
after { onElement(TestElements.Foo).assertDoesNotExist() }
}
}
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ComposedDigitalLayerController.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ComposedDigitalLayerController.kt
index 5f71b19fbc3f..884666854ab4 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ComposedDigitalLayerController.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ComposedDigitalLayerController.kt
@@ -111,10 +111,6 @@ class ComposedDigitalLayerController(private val clockCtx: ClockContext) :
override fun onZenDataChanged(data: ZenData) {}
- override fun onFontAxesChanged(axes: ClockAxisStyle) {
- view.updateAxes(axes)
- }
-
override var isReactiveTouchInteractionEnabled
get() = view.isReactiveTouchInteractionEnabled
set(value) {
@@ -152,6 +148,13 @@ class ComposedDigitalLayerController(private val clockCtx: ClockContext) :
override fun onFidgetTap(x: Float, y: Float) {
view.animateFidget(x, y)
}
+
+ private var hasFontAxes = false
+
+ override fun onFontAxesChanged(style: ClockAxisStyle) {
+ view.updateAxes(style, isAnimated = hasFontAxes)
+ hasFontAxes = true
+ }
}
override val faceEvents =
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt
index 3cfa78d17fe7..450cece8709a 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt
@@ -231,8 +231,6 @@ class DefaultClockController(
override fun onAlarmDataChanged(data: AlarmData) {}
override fun onZenDataChanged(data: ZenData) {}
-
- override fun onFontAxesChanged(axes: ClockAxisStyle) {}
}
open inner class DefaultClockAnimations(
@@ -285,6 +283,8 @@ class DefaultClockController(
override fun onPositionUpdated(distance: Float, fraction: Float) {}
override fun onFidgetTap(x: Float, y: Float) {}
+
+ override fun onFontAxesChanged(style: ClockAxisStyle) {}
}
inner class LargeClockAnimations(
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockController.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockController.kt
index 96c3ac75587e..84f45fcc0532 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockController.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockController.kt
@@ -99,12 +99,6 @@ class FlexClockController(private val clockCtx: ClockContext) : ClockController
smallClock.events.onZenDataChanged(data)
largeClock.events.onZenDataChanged(data)
}
-
- override fun onFontAxesChanged(axes: ClockAxisStyle) {
- val fontAxes = ClockAxisStyle(getDefaultAxes(clockCtx.settings).merge(axes))
- smallClock.events.onFontAxesChanged(fontAxes)
- largeClock.events.onFontAxesChanged(fontAxes)
- }
}
override fun initialize(
@@ -113,10 +107,10 @@ class FlexClockController(private val clockCtx: ClockContext) : ClockController
foldFraction: Float,
clockListener: ClockEventListener?,
) {
- events.onFontAxesChanged(clockCtx.settings.axes)
smallClock.run {
layerController.onViewBoundsChanged = { clockListener?.onBoundsChanged(it) }
events.onThemeChanged(theme.copy(isDarkTheme = isDarkTheme))
+ animations.onFontAxesChanged(clockCtx.settings.axes)
animations.doze(dozeFraction)
animations.fold(foldFraction)
events.onTimeTick()
@@ -125,6 +119,7 @@ class FlexClockController(private val clockCtx: ClockContext) : ClockController
largeClock.run {
layerController.onViewBoundsChanged = { clockListener?.onBoundsChanged(it) }
events.onThemeChanged(theme.copy(isDarkTheme = isDarkTheme))
+ animations.onFontAxesChanged(clockCtx.settings.axes)
animations.doze(dozeFraction)
animations.fold(foldFraction)
events.onTimeTick()
@@ -144,23 +139,25 @@ class FlexClockController(private val clockCtx: ClockContext) : ClockController
listOf(
GSFAxes.WEIGHT.toClockAxis(
type = AxisType.Float,
- currentValue = 475f,
+ currentValue = 400f,
name = "Weight",
description = "Glyph Weight",
),
GSFAxes.WIDTH.toClockAxis(
type = AxisType.Float,
- currentValue = 85f,
+ currentValue = 80f,
name = "Width",
description = "Glyph Width",
),
GSFAxes.ROUND.toClockAxis(
type = AxisType.Boolean,
+ currentValue = 100f,
name = "Round",
description = "Glyph Roundness",
),
GSFAxes.SLANT.toClockAxis(
type = AxisType.Boolean,
+ currentValue = 0f,
name = "Slant",
description = "Glyph Slant",
),
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockFaceController.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockFaceController.kt
index 171a68f72e20..ec7803d0c6d6 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockFaceController.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockFaceController.kt
@@ -31,10 +31,12 @@ import com.android.systemui.plugins.clocks.ClockFaceConfig
import com.android.systemui.plugins.clocks.ClockFaceController
import com.android.systemui.plugins.clocks.ClockFaceEvents
import com.android.systemui.plugins.clocks.ClockFaceLayout
+import com.android.systemui.plugins.clocks.ClockFontAxis.Companion.merge
import com.android.systemui.plugins.clocks.DefaultClockFaceLayout
import com.android.systemui.plugins.clocks.ThemeConfig
import com.android.systemui.plugins.clocks.WeatherData
import com.android.systemui.plugins.clocks.ZenData
+import com.android.systemui.shared.clocks.FlexClockController.Companion.getDefaultAxes
import com.android.systemui.shared.clocks.FontUtils.get
import com.android.systemui.shared.clocks.FontUtils.set
import com.android.systemui.shared.clocks.ViewUtils.computeLayoutDiff
@@ -131,15 +133,6 @@ class FlexClockFaceController(clockCtx: ClockContext, private val isLargeClock:
layerController.faceEvents.onThemeChanged(theme)
}
- override fun onFontAxesChanged(settings: ClockAxisStyle) {
- var axes = ClockAxisStyle(settings)
- if (!isLargeClock && axes[GSFAxes.WIDTH] > SMALL_CLOCK_MAX_WDTH) {
- axes[GSFAxes.WIDTH] = SMALL_CLOCK_MAX_WDTH
- }
-
- layerController.events.onFontAxesChanged(axes)
- }
-
/**
* targetRegion passed to all customized clock applies counter translationY of Keyguard and
* keyguard_large_clock_top_margin from default clock
@@ -232,6 +225,15 @@ class FlexClockFaceController(clockCtx: ClockContext, private val isLargeClock:
override fun onFidgetTap(x: Float, y: Float) {
layerController.animations.onFidgetTap(x, y)
}
+
+ override fun onFontAxesChanged(style: ClockAxisStyle) {
+ var axes = ClockAxisStyle(getDefaultAxes(clockCtx.settings).merge(style))
+ if (!isLargeClock && axes[GSFAxes.WIDTH] > SMALL_CLOCK_MAX_WDTH) {
+ axes[GSFAxes.WIDTH] = SMALL_CLOCK_MAX_WDTH
+ }
+
+ layerController.animations.onFontAxesChanged(axes)
+ }
}
companion object {
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/SimpleDigitalHandLayerController.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/SimpleDigitalHandLayerController.kt
index 7be9a936cbd3..eef910c3bd27 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/SimpleDigitalHandLayerController.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/SimpleDigitalHandLayerController.kt
@@ -171,10 +171,6 @@ open class SimpleDigitalHandLayerController(
override fun onAlarmDataChanged(data: AlarmData) {}
override fun onZenDataChanged(data: ZenData) {}
-
- override fun onFontAxesChanged(axes: ClockAxisStyle) {
- view.updateAxes(axes)
- }
}
override val animations =
@@ -195,6 +191,13 @@ open class SimpleDigitalHandLayerController(
view.dozeFraction = fraction
}
+ private var hasFontAxes = false
+
+ override fun onFontAxesChanged(style: ClockAxisStyle) {
+ view.updateAxes(style, isAnimated = hasFontAxes)
+ hasFontAxes = true
+ }
+
override fun fold(fraction: Float) {
applyLayout()
refreshTime()
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/FlexClockView.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/FlexClockView.kt
index 4531aed0e83d..4a5532b6e462 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/FlexClockView.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/FlexClockView.kt
@@ -272,8 +272,8 @@ class FlexClockView(clockCtx: ClockContext) : ViewGroup(clockCtx.context) {
invalidate()
}
- fun updateAxes(axes: ClockAxisStyle) {
- childViews.forEach { view -> view.updateAxes(axes) }
+ fun updateAxes(axes: ClockAxisStyle, isAnimated: Boolean) {
+ childViews.forEach { view -> view.updateAxes(axes, isAnimated) }
requestLayout()
}
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/SimpleDigitalClockTextView.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/SimpleDigitalClockTextView.kt
index 377a24c2899b..05c9818f0c57 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/SimpleDigitalClockTextView.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/SimpleDigitalClockTextView.kt
@@ -200,11 +200,11 @@ open class SimpleDigitalClockTextView(
invalidate()
}
- fun updateAxes(lsAxes: ClockAxisStyle) {
+ fun updateAxes(lsAxes: ClockAxisStyle, isAnimated: Boolean) {
lsFontVariation = lsAxes.toFVar()
aodFontVariation = lsAxes.copyWith(fixedAodAxes).toFVar()
fidgetFontVariation = buildFidgetVariation(lsAxes).toFVar()
- logger.updateAxes(lsFontVariation, aodFontVariation)
+ logger.updateAxes(lsFontVariation, aodFontVariation, isAnimated)
lockScreenPaint.typeface = typefaceCache.getTypefaceForVariant(lsFontVariation)
typeface = lockScreenPaint.typeface
@@ -212,7 +212,15 @@ open class SimpleDigitalClockTextView(
textBounds = lockScreenPaint.getTextBounds(text)
targetTextBounds = textBounds
- textAnimator.setTextStyle(TextAnimator.Style(fVar = lsFontVariation))
+ textAnimator.setTextStyle(
+ TextAnimator.Style(fVar = lsFontVariation),
+ TextAnimator.Animation(
+ animate = isAnimated && isAnimationEnabled,
+ duration = AXIS_CHANGE_ANIMATION_DURATION,
+ interpolator = aodDozingInterpolator,
+ ),
+ )
+
measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED)
recomputeMaxSingleDigitSizes()
requestLayout()
@@ -651,6 +659,7 @@ open class SimpleDigitalClockTextView(
.compose()
val CHARGE_ANIMATION_DURATION = 500L
+ val AXIS_CHANGE_ANIMATION_DURATION = 400L
val FIDGET_ANIMATION_DURATION = 250L
val FIDGET_INTERPOLATOR = PathInterpolator(0.26873f, 0f, 0.45042f, 1f)
val FIDGET_DISTS =
diff --git a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityContainerTest.java b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityContainerTest.java
index 2845f6a2983a..e75f60736435 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityContainerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityContainerTest.java
@@ -507,8 +507,8 @@ public class KeyguardSecurityContainerTest extends SysuiTestCase {
0 /* flags */);
users.add(new UserRecord(info, null, false /* isGuest */, false /* isCurrent */,
false /* isAddUser */, false /* isRestricted */, true /* isSwitchToEnabled */,
- false /* isAddSupervisedUser */, null /* enforcedAdmin */,
- false /* isManageUsers */));
+ false /* isAddSupervisedUser */, false /* isSignOut */,
+ null /* enforcedAdmin */, false /* isManageUsers */));
}
return users;
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/MagnificationModeSwitchTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/MagnificationModeSwitchTest.java
index aa95abb3528f..0b0088926aae 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/MagnificationModeSwitchTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/MagnificationModeSwitchTest.java
@@ -73,14 +73,10 @@ import android.widget.ImageView;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
-import com.android.app.viewcapture.ViewCapture;
-import com.android.app.viewcapture.ViewCaptureAwareWindowManager;
import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.res.R;
-import kotlin.Lazy;
-
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@@ -108,8 +104,6 @@ public class MagnificationModeSwitchTest extends SysuiTestCase {
private SfVsyncFrameCallbackProvider mSfVsyncFrameProvider;
@Mock
private MagnificationModeSwitch.ClickListener mClickListener;
- @Mock
- private Lazy<ViewCapture> mLazyViewCapture;
private TestableWindowManager mWindowManager;
private ViewPropertyAnimator mViewPropertyAnimator;
private MagnificationModeSwitch mMagnificationModeSwitch;
@@ -123,7 +117,6 @@ public class MagnificationModeSwitchTest extends SysuiTestCase {
mContext = Mockito.spy(getContext());
final WindowManager wm = mContext.getSystemService(WindowManager.class);
mWindowManager = spy(new TestableWindowManager(wm));
- mContext.addMockSystemService(Context.WINDOW_SERVICE, mWindowManager);
mContext.addMockSystemService(Context.ACCESSIBILITY_SERVICE, mAccessibilityManager);
mSpyImageView = Mockito.spy(new ImageView(mContext));
mViewPropertyAnimator = Mockito.spy(mSpyImageView.animate());
@@ -139,10 +132,8 @@ public class MagnificationModeSwitchTest extends SysuiTestCase {
return null;
}).when(mSfVsyncFrameProvider).postFrameCallback(
any(Choreographer.FrameCallback.class));
- ViewCaptureAwareWindowManager vwm = new ViewCaptureAwareWindowManager(mWindowManager,
- mLazyViewCapture, false);
- mMagnificationModeSwitch = new MagnificationModeSwitch(mContext, mSpyImageView,
- mSfVsyncFrameProvider, mClickListener, vwm);
+ mMagnificationModeSwitch = new MagnificationModeSwitch(mContext, mWindowManager,
+ mSpyImageView, mSfVsyncFrameProvider, mClickListener);
assertNotNull(mTouchListener);
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/MagnificationSettingsControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/MagnificationSettingsControllerTest.java
index 3cd3fefb8ef0..02ec5aac120c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/MagnificationSettingsControllerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/MagnificationSettingsControllerTest.java
@@ -23,11 +23,11 @@ import static org.mockito.Mockito.verify;
import android.content.pm.ActivityInfo;
import android.testing.TestableLooper;
+import android.view.WindowManager;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
-import com.android.app.viewcapture.ViewCaptureAwareWindowManager;
import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.accessibility.WindowMagnificationSettings.MagnificationSize;
@@ -58,15 +58,15 @@ public class MagnificationSettingsControllerTest extends SysuiTestCase {
@Mock
private SecureSettings mSecureSettings;
@Mock
- private ViewCaptureAwareWindowManager mViewCaptureAwareWindowManager;
+ private WindowManager mWindowManager;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mMagnificationSettingsController = new MagnificationSettingsController(
mContext, mSfVsyncFrameProvider,
- mMagnificationSettingControllerCallback, mSecureSettings,
- mWindowMagnificationSettings, mViewCaptureAwareWindowManager);
+ mMagnificationSettingControllerCallback, mSecureSettings, mWindowManager,
+ mWindowMagnificationSettings);
}
@After
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/MirrorWindowControlTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/MirrorWindowControlTest.java
index 12c866f0adb2..463bfe5ae73f 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/MirrorWindowControlTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/MirrorWindowControlTest.java
@@ -27,14 +27,15 @@ import static org.mockito.Mockito.verify;
import android.content.Context;
import android.graphics.Point;
+import android.os.Looper;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
+import android.view.WindowManager;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
-import com.android.app.viewcapture.ViewCaptureAwareWindowManager;
import com.android.systemui.SysuiTestCase;
import org.junit.Before;
@@ -48,7 +49,7 @@ import org.mockito.MockitoAnnotations;
@RunWith(AndroidJUnit4.class)
public class MirrorWindowControlTest extends SysuiTestCase {
- @Mock ViewCaptureAwareWindowManager mWindowManager;
+ @Mock WindowManager mWindowManager;
View mView;
int mViewWidth;
int mViewHeight;
@@ -69,8 +70,12 @@ public class MirrorWindowControlTest extends SysuiTestCase {
return null;
}).when(mWindowManager).addView(any(View.class), any(LayoutParams.class));
+ if (Looper.myLooper() == null) {
+ Looper.prepare();
+ }
+
mStubMirrorWindowControl = new StubMirrorWindowControl(getContext(), mView, mViewWidth,
- mViewHeight);
+ mViewHeight, mWindowManager);
}
@Test
@@ -122,8 +127,9 @@ public class MirrorWindowControlTest extends SysuiTestCase {
boolean mInvokeOnCreateView = false;
- StubMirrorWindowControl(Context context, View view, int width, int height) {
- super(context);
+ StubMirrorWindowControl(Context context, View view, int width, int height,
+ WindowManager windowManager) {
+ super(context, windowManager);
mView = view;
mWidth = width;
mHeight = height;
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/ModeSwitchesControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/ModeSwitchesControllerTest.java
index e1e515eb31f5..b1c837a99f3d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/ModeSwitchesControllerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/ModeSwitchesControllerTest.java
@@ -24,11 +24,11 @@ import android.provider.Settings;
import android.testing.TestableLooper;
import android.view.Display;
import android.view.View;
+import android.view.WindowManager;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
-import com.android.app.viewcapture.ViewCaptureAwareWindowManager;
import com.android.systemui.SysuiTestCase;
import org.junit.After;
@@ -51,8 +51,6 @@ public class ModeSwitchesControllerTest extends SysuiTestCase {
private View mSpyView;
@Mock
private MagnificationModeSwitch.ClickListener mListener;
- @Mock
- private ViewCaptureAwareWindowManager mViewCaptureAwareWindowManager;
@Before
@@ -61,8 +59,9 @@ public class ModeSwitchesControllerTest extends SysuiTestCase {
mSupplier = new FakeSwitchSupplier(mContext.getSystemService(DisplayManager.class));
mModeSwitchesController = new ModeSwitchesController(mSupplier);
mModeSwitchesController.setClickListenerDelegate(mListener);
- mModeSwitch = Mockito.spy(new MagnificationModeSwitch(mContext, mModeSwitchesController,
- mViewCaptureAwareWindowManager));
+ WindowManager wm = mContext.getSystemService(WindowManager.class);
+ mModeSwitch = Mockito.spy(new MagnificationModeSwitch(mContext, wm,
+ mModeSwitchesController));
mSpyView = Mockito.spy(new View(mContext));
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/DragToInteractAnimationControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/DragToInteractAnimationControllerTest.java
index 6edf94939010..95ebd8190e9c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/DragToInteractAnimationControllerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/DragToInteractAnimationControllerTest.java
@@ -72,7 +72,7 @@ public class DragToInteractAnimationControllerTest extends SysuiTestCase {
stubWindowManager);
final MenuView stubMenuView = spy(new MenuView(mContext, stubMenuViewModel,
stubMenuViewAppearance, mockSecureSettings));
- mInteractView = spy(new DragToInteractView(mContext));
+ mInteractView = spy(new DragToInteractView(mContext, stubWindowManager));
mDismissView = spy(new DismissView(mContext));
if (Flags.floatingMenuDragToEdit()) {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuListViewTouchHandlerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuListViewTouchHandlerTest.java
index fff6def52803..572d140b850d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuListViewTouchHandlerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuListViewTouchHandlerTest.java
@@ -101,7 +101,7 @@ public class MenuListViewTouchHandlerTest extends SysuiTestCase {
mStubMenuView.setTranslationY(0);
mMenuAnimationController = spy(new MenuAnimationController(
mStubMenuView, stubMenuViewAppearance));
- mInteractView = spy(new DragToInteractView(mContext));
+ mInteractView = spy(new DragToInteractView(mContext, windowManager));
mDismissView = spy(new DismissView(mContext));
if (Flags.floatingMenuDragToEdit()) {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerControllerTest.java
index 4f043109a534..b5fa52570d6a 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerControllerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerControllerTest.java
@@ -39,15 +39,11 @@ import android.view.accessibility.AccessibilityManager;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
-import com.android.app.viewcapture.ViewCapture;
-import com.android.app.viewcapture.ViewCaptureAwareWindowManager;
import com.android.settingslib.bluetooth.HearingAidDeviceManager;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.navigationbar.NavigationModeController;
import com.android.systemui.util.settings.SecureSettings;
-import kotlin.Lazy;
-
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -78,16 +74,11 @@ public class MenuViewLayerControllerTest extends SysuiTestCase {
@Mock
private WindowMetrics mWindowMetrics;
- @Mock
- private Lazy<ViewCapture> mLazyViewCapture;
-
private MenuViewLayerController mMenuViewLayerController;
@Before
public void setUp() throws Exception {
final WindowManager wm = mContext.getSystemService(WindowManager.class);
- final ViewCaptureAwareWindowManager viewCaptureAwareWm = new ViewCaptureAwareWindowManager(
- mWindowManager, mLazyViewCapture, /* isViewCaptureEnabled= */ false);
doAnswer(invocation -> wm.getMaximumWindowMetrics()).when(
mWindowManager).getMaximumWindowMetrics();
mContext.addMockSystemService(Context.WINDOW_SERVICE, mWindowManager);
@@ -95,8 +86,8 @@ public class MenuViewLayerControllerTest extends SysuiTestCase {
when(mWindowMetrics.getBounds()).thenReturn(new Rect(0, 0, 1080, 2340));
when(mWindowMetrics.getWindowInsets()).thenReturn(stubDisplayInsets());
mMenuViewLayerController = new MenuViewLayerController(mContext, mWindowManager,
- viewCaptureAwareWm, mAccessibilityManager, mSecureSettings,
- mock(NavigationModeController.class), mHearingAidDeviceManager);
+ mAccessibilityManager, mSecureSettings, mock(NavigationModeController.class),
+ mHearingAidDeviceManager);
}
@Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegateTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegateTest.java
index d118ace08b85..530aba4993a8 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegateTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegateTest.java
@@ -176,6 +176,8 @@ public class HearingDevicesDialogDelegateTest extends SysuiTestCase {
.thenReturn(SETTINGS_PACKAGE_NAME);
when(mDevice.getBondState()).thenReturn(BOND_BONDED);
when(mDevice.isConnected()).thenReturn(true);
+ when(mDevice.getAddress()).thenReturn(DEVICE_ADDRESS);
+ when(mDevice.getAnonymizedAddress()).thenReturn(DEVICE_ADDRESS);
when(mCachedDevice.getDevice()).thenReturn(mDevice);
when(mCachedDevice.getAddress()).thenReturn(DEVICE_ADDRESS);
when(mCachedDevice.getName()).thenReturn(DEVICE_NAME);
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
index 446891a7873e..c4fafc192260 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
@@ -41,7 +41,6 @@ import android.widget.ScrollView
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.app.viewcapture.ViewCapture
import com.android.internal.jank.InteractionJankMonitor
import com.android.internal.widget.LockPatternUtils
import com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PATTERN
@@ -116,7 +115,6 @@ open class AuthContainerViewTest : SysuiTestCase() {
@Mock private lateinit var packageManager: PackageManager
@Mock private lateinit var activityTaskManager: ActivityTaskManager
@Mock private lateinit var accessibilityManager: AccessibilityManager
- @Mock private lateinit var lazyViewCapture: Lazy<ViewCapture>
private lateinit var displayRepository: FakeDisplayRepository
private lateinit var displayStateInteractor: DisplayStateInteractor
@@ -689,7 +687,6 @@ open class AuthContainerViewTest : SysuiTestCase() {
{ credentialViewModel },
fakeExecutor,
vibrator,
- lazyViewCapture,
msdlPlayer,
) {
override fun postOnAnimation(runnable: Runnable) {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/AuthControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/AuthControllerTest.java
index a1a2aa70d869..f4185ee48510 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/AuthControllerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/AuthControllerTest.java
@@ -83,7 +83,6 @@ import android.view.WindowManager;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
-import com.android.app.viewcapture.ViewCapture;
import com.android.internal.R;
import com.android.internal.jank.InteractionJankMonitor;
import com.android.internal.widget.LockPatternUtils;
@@ -100,11 +99,10 @@ import com.android.systemui.util.concurrency.Execution;
import com.android.systemui.util.concurrency.FakeExecution;
import com.android.systemui.util.concurrency.FakeExecutor;
import com.android.systemui.util.time.FakeSystemClock;
+import com.android.systemui.utils.windowmanager.WindowManagerProvider;
import com.google.android.msdl.domain.MSDLPlayer;
-import dagger.Lazy;
-
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -175,8 +173,6 @@ public class AuthControllerTest extends SysuiTestCase {
private PromptViewModel mPromptViewModel;
@Mock
private UdfpsUtils mUdfpsUtils;
- @Mock
- private Lazy<ViewCapture> mLazyViewCapture;
@Captor
private ArgumentCaptor<IFingerprintAuthenticatorsRegisteredCallback> mFpAuthenticatorsRegisteredCaptor;
@@ -198,6 +194,8 @@ public class AuthControllerTest extends SysuiTestCase {
private KeyguardManager mKeyguardManager;
@Mock
private MSDLPlayer mMSDLPlayer;
+ @Mock
+ private WindowManagerProvider mWindowManagerProvider;
private TestableContext mContextSpy;
private Execution mExecution;
@@ -1198,7 +1196,8 @@ public class AuthControllerTest extends SysuiTestCase {
when(mDisplayManager.getDisplay(displayId)).thenReturn(mockDisplay);
}
doReturn(mockDisplayContext).when(mContextSpy).createDisplayContext(mockDisplay);
- when(mockDisplayContext.getSystemService(WindowManager.class)).thenReturn(mockDisplayWM);
+ when(mWindowManagerProvider.getWindowManager(mockDisplayContext))
+ .thenReturn(mockDisplayWM);
return mockDisplayWM;
}
@@ -1214,7 +1213,7 @@ public class AuthControllerTest extends SysuiTestCase {
() -> mLogContextInteractor, () -> mPromptSelectionInteractor,
() -> mCredentialViewModel, () -> mPromptViewModel, mInteractionJankMonitor,
mHandler, mBackgroundExecutor, mUdfpsUtils, mVibratorHelper, mKeyguardManager,
- mLazyViewCapture, mMSDLPlayer);
+ mMSDLPlayer, mWindowManagerProvider);
}
@Override
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
index a2f5a30a20ff..675c9deaff23 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
@@ -45,12 +45,12 @@ import android.view.LayoutInflater;
import android.view.Surface;
import android.view.View;
import android.view.ViewRootImpl;
+import android.view.WindowManager;
import android.view.accessibility.AccessibilityManager;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
-import com.android.app.viewcapture.ViewCaptureAwareWindowManager;
import com.android.internal.logging.InstanceIdSequence;
import com.android.internal.util.LatencyTracker;
import com.android.keyguard.KeyguardUpdateMonitor;
@@ -129,7 +129,7 @@ public class UdfpsControllerTest extends SysuiTestCase {
@Mock
private FingerprintManager mFingerprintManager;
@Mock
- private ViewCaptureAwareWindowManager mWindowManager;
+ private WindowManager mWindowManager;
@Mock
private StatusBarStateController mStatusBarStateController;
@Mock
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractorTest.kt
index c1feca29906a..91ec1cbce8a4 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractorTest.kt
@@ -77,6 +77,7 @@ class PrimaryBouncerInteractorTest : SysuiTestCase() {
private lateinit var resources: TestableResources
private lateinit var trustRepository: FakeTrustRepository
private lateinit var testScope: TestScope
+ private val TEST_REASON = "reason"
@Before
fun setUp() {
@@ -118,7 +119,7 @@ class PrimaryBouncerInteractorTest : SysuiTestCase() {
mainHandler.setMode(FakeHandler.Mode.QUEUEING)
// WHEN bouncer show is requested
- underTest.show(true)
+ underTest.show(true, TEST_REASON)
// WHEN all queued messages are dispatched
mainHandler.dispatchQueuedMessages()
@@ -134,7 +135,7 @@ class PrimaryBouncerInteractorTest : SysuiTestCase() {
@Test
fun testShow_isScrimmed() {
- underTest.show(true)
+ underTest.show(true, TEST_REASON)
verify(repository).setKeyguardAuthenticatedBiometrics(null)
verify(repository).setPrimaryStartingToHide(false)
verify(repository).setPrimaryScrimmed(true)
@@ -162,7 +163,7 @@ class PrimaryBouncerInteractorTest : SysuiTestCase() {
@Test
fun testShowReturnsFalseWhenDelegateIsNotSet() {
whenever(bouncerView.delegate).thenReturn(null)
- assertThat(underTest.show(true)).isEqualTo(false)
+ assertThat(underTest.show(true, TEST_REASON)).isEqualTo(false)
}
@Test
@@ -171,7 +172,7 @@ class PrimaryBouncerInteractorTest : SysuiTestCase() {
whenever(keyguardSecurityModel.getSecurityMode(anyInt()))
.thenReturn(KeyguardSecurityModel.SecurityMode.SimPuk)
- underTest.show(true)
+ underTest.show(true, TEST_REASON)
verify(repository).setPrimaryShow(false)
verify(repository).setPrimaryShow(true)
}
@@ -352,7 +353,7 @@ class PrimaryBouncerInteractorTest : SysuiTestCase() {
whenever(faceAuthInteractor.canFaceAuthRun()).thenReturn(true)
// WHEN bouncer show is requested
- underTest.show(true)
+ underTest.show(true, TEST_REASON)
// THEN primary show & primary showing soon aren't updated immediately
verify(repository, never()).setPrimaryShow(true)
@@ -375,7 +376,7 @@ class PrimaryBouncerInteractorTest : SysuiTestCase() {
whenever(faceAuthInteractor.canFaceAuthRun()).thenReturn(false)
// WHEN bouncer show is requested
- underTest.show(true)
+ underTest.show(true, TEST_REASON)
// THEN primary show & primary showing soon are updated immediately
verify(repository).setPrimaryShow(true)
@@ -394,7 +395,7 @@ class PrimaryBouncerInteractorTest : SysuiTestCase() {
runCurrent()
// WHEN bouncer show is requested
- underTest.show(true)
+ underTest.show(true, TEST_REASON)
// THEN primary show & primary showing soon were scheduled to update
verify(repository, never()).setPrimaryShow(true)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/DeviceInactiveConditionTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/DeviceInactiveConditionTest.kt
index c98d6c531d54..6710575e32d2 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/DeviceInactiveConditionTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/DeviceInactiveConditionTest.kt
@@ -56,6 +56,7 @@ class DeviceInactiveConditionTest : SysuiTestCase() {
Kosmos.Fixture {
DeviceInactiveCondition(
applicationCoroutineScope,
+ applicationCoroutineScope,
keyguardStateController,
wakefulnessLifecycle,
keyguardUpdateMonitor,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalAutoOpenInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalAutoOpenInteractorTest.kt
index f4a1c90a5471..856a62e3f5a7 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalAutoOpenInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalAutoOpenInteractorTest.kt
@@ -25,9 +25,10 @@ import com.android.systemui.common.data.repository.fake
import com.android.systemui.communal.data.model.FEATURE_AUTO_OPEN
import com.android.systemui.communal.data.model.FEATURE_MANUAL_OPEN
import com.android.systemui.communal.data.model.SuppressionReason
+import com.android.systemui.communal.posturing.data.model.PositionState
import com.android.systemui.communal.posturing.data.repository.fake
import com.android.systemui.communal.posturing.data.repository.posturingRepository
-import com.android.systemui.communal.posturing.shared.model.PosturedState
+import com.android.systemui.communal.posturing.domain.interactor.advanceTimeBySlidingWindowAndRun
import com.android.systemui.dock.DockManager
import com.android.systemui.dock.fakeDockManager
import com.android.systemui.kosmos.Kosmos
@@ -55,9 +56,11 @@ class CommunalAutoOpenInteractorTest : SysuiTestCase() {
fun setUp() {
runBlocking { kosmos.fakeUserRepository.asMainUser() }
with(kosmos.fakeSettings) {
- putBoolForUser(Settings.Secure.SCREENSAVER_ACTIVATE_ON_SLEEP, false, MAIN_USER_ID)
- putBoolForUser(Settings.Secure.SCREENSAVER_ACTIVATE_ON_DOCK, false, MAIN_USER_ID)
- putBoolForUser(Settings.Secure.SCREENSAVER_ACTIVATE_ON_POSTURED, false, MAIN_USER_ID)
+ putIntForUser(
+ Settings.Secure.WHEN_TO_START_GLANCEABLE_HUB,
+ Settings.Secure.GLANCEABLE_HUB_START_NEVER,
+ MAIN_USER_ID,
+ )
}
}
@@ -67,9 +70,9 @@ class CommunalAutoOpenInteractorTest : SysuiTestCase() {
val shouldAutoOpen by collectLastValue(underTest.shouldAutoOpen)
val suppressionReason by collectLastValue(underTest.suppressionReason)
- fakeSettings.putBoolForUser(
- Settings.Secure.SCREENSAVER_ACTIVATE_ON_SLEEP,
- true,
+ fakeSettings.putIntForUser(
+ Settings.Secure.WHEN_TO_START_GLANCEABLE_HUB,
+ Settings.Secure.GLANCEABLE_HUB_START_CHARGING,
MAIN_USER_ID,
)
@@ -91,9 +94,9 @@ class CommunalAutoOpenInteractorTest : SysuiTestCase() {
val shouldAutoOpen by collectLastValue(underTest.shouldAutoOpen)
val suppressionReason by collectLastValue(underTest.suppressionReason)
- fakeSettings.putBoolForUser(
- Settings.Secure.SCREENSAVER_ACTIVATE_ON_DOCK,
- true,
+ fakeSettings.putIntForUser(
+ Settings.Secure.WHEN_TO_START_GLANCEABLE_HUB,
+ Settings.Secure.GLANCEABLE_HUB_START_DOCKED,
MAIN_USER_ID,
)
@@ -118,14 +121,19 @@ class CommunalAutoOpenInteractorTest : SysuiTestCase() {
val shouldAutoOpen by collectLastValue(underTest.shouldAutoOpen)
val suppressionReason by collectLastValue(underTest.suppressionReason)
- fakeSettings.putBoolForUser(
- Settings.Secure.SCREENSAVER_ACTIVATE_ON_POSTURED,
- true,
+ fakeSettings.putIntForUser(
+ Settings.Secure.WHEN_TO_START_GLANCEABLE_HUB,
+ Settings.Secure.GLANCEABLE_HUB_START_CHARGING_UPRIGHT,
MAIN_USER_ID,
)
batteryRepository.fake.setDevicePluggedIn(true)
- posturingRepository.fake.setPosturedState(PosturedState.NotPostured)
+ posturingRepository.fake.emitPositionState(
+ PositionState(
+ stationary = PositionState.StationaryState.Stationary(confidence = 1f),
+ orientation = PositionState.OrientationState.NotPostured(confidence = 1f),
+ )
+ )
assertThat(shouldAutoOpen).isFalse()
assertThat(suppressionReason)
@@ -133,7 +141,13 @@ class CommunalAutoOpenInteractorTest : SysuiTestCase() {
SuppressionReason.ReasonWhenToAutoShow(FEATURE_AUTO_OPEN or FEATURE_MANUAL_OPEN)
)
- posturingRepository.fake.setPosturedState(PosturedState.Postured(1f))
+ posturingRepository.fake.emitPositionState(
+ PositionState(
+ stationary = PositionState.StationaryState.Stationary(confidence = 1f),
+ orientation = PositionState.OrientationState.Postured(confidence = 1f),
+ )
+ )
+ advanceTimeBySlidingWindowAndRun()
assertThat(shouldAutoOpen).isTrue()
assertThat(suppressionReason).isNull()
}
@@ -144,24 +158,14 @@ class CommunalAutoOpenInteractorTest : SysuiTestCase() {
val shouldAutoOpen by collectLastValue(underTest.shouldAutoOpen)
val suppressionReason by collectLastValue(underTest.suppressionReason)
- fakeSettings.putBoolForUser(
- Settings.Secure.SCREENSAVER_ACTIVATE_ON_SLEEP,
- false,
- MAIN_USER_ID,
- )
- fakeSettings.putBoolForUser(
- Settings.Secure.SCREENSAVER_ACTIVATE_ON_DOCK,
- false,
- MAIN_USER_ID,
- )
- fakeSettings.putBoolForUser(
- Settings.Secure.SCREENSAVER_ACTIVATE_ON_POSTURED,
- false,
+ fakeSettings.putIntForUser(
+ Settings.Secure.WHEN_TO_START_GLANCEABLE_HUB,
+ Settings.Secure.GLANCEABLE_HUB_START_NEVER,
MAIN_USER_ID,
)
batteryRepository.fake.setDevicePluggedIn(true)
- posturingRepository.fake.setPosturedState(PosturedState.Postured(1f))
+ posturingRepository.fake.emitPositionState(PositionState())
fakeDockManager.setIsDocked(true)
assertThat(shouldAutoOpen).isFalse()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalSettingsInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalSettingsInteractorTest.kt
index 310bf6486413..d6f7145bd770 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalSettingsInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalSettingsInteractorTest.kt
@@ -21,10 +21,12 @@ import android.app.admin.devicePolicyManager
import android.content.Intent
import android.content.pm.UserInfo
import android.os.UserManager
+import android.provider.Settings
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.broadcast.broadcastDispatcher
+import com.android.systemui.communal.shared.model.WhenToStartHub
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.collectLastValue
import com.android.systemui.kosmos.runTest
@@ -32,6 +34,7 @@ import com.android.systemui.kosmos.useUnconfinedTestDispatcher
import com.android.systemui.settings.fakeUserTracker
import com.android.systemui.testKosmos
import com.android.systemui.user.data.repository.fakeUserRepository
+import com.android.systemui.util.settings.fakeSettings
import org.junit.Assert.assertEquals
import org.junit.Assert.assertNotNull
import org.junit.Assert.assertNull
@@ -82,6 +85,19 @@ class CommunalSettingsInteractorTest : SysuiTestCase() {
assertEquals(USER_INFO_WORK.id, disallowedUser!!.id)
}
+ @Test
+ fun whenToStartHub_matchesRepository() =
+ kosmos.runTest {
+ fakeSettings.putIntForUser(
+ Settings.Secure.WHEN_TO_START_GLANCEABLE_HUB,
+ Settings.Secure.GLANCEABLE_HUB_START_CHARGING,
+ MAIN_USER_INFO.id,
+ )
+
+ val startCondition by collectLastValue(underTest.whenToStartHub)
+ assertEquals(startCondition, WhenToStartHub.WHILE_CHARGING)
+ }
+
private fun setKeyguardFeaturesDisabled(user: UserInfo, disabledFlags: Int) {
whenever(
kosmos.devicePolicyManager.getKeyguardDisabledFeatures(
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/posturing/domain/interactor/PosturingInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/posturing/domain/interactor/PosturingInteractorTest.kt
index 0df8834618d5..b4708d97c4c3 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/posturing/domain/interactor/PosturingInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/posturing/domain/interactor/PosturingInteractorTest.kt
@@ -16,22 +16,39 @@
package com.android.systemui.communal.posturing.domain.interactor
+import android.hardware.Sensor
+import android.hardware.TriggerEventListener
+import android.platform.test.annotations.EnableFlags
+import android.service.dreams.Flags.FLAG_ALLOW_DREAM_WHEN_POSTURED
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
+import com.android.systemui.communal.posturing.data.model.PositionState
import com.android.systemui.communal.posturing.data.repository.fake
import com.android.systemui.communal.posturing.data.repository.posturingRepository
import com.android.systemui.communal.posturing.shared.model.PosturedState
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.advanceTimeBy
import com.android.systemui.kosmos.collectLastValue
+import com.android.systemui.kosmos.runCurrent
import com.android.systemui.kosmos.runTest
import com.android.systemui.kosmos.useUnconfinedTestDispatcher
import com.android.systemui.testKosmos
+import com.android.systemui.util.sensors.asyncSensorManager
import com.google.common.truth.Truth.assertThat
+import kotlin.time.Duration.Companion.hours
+import kotlin.time.Duration.Companion.milliseconds
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.kotlin.any
+import org.mockito.kotlin.doAnswer
+import org.mockito.kotlin.doReturn
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.stub
@SmallTest
@RunWith(AndroidJUnit4::class)
+@EnableFlags(FLAG_ALLOW_DREAM_WHEN_POSTURED)
class PosturingInteractorTest : SysuiTestCase() {
private val kosmos = testKosmos().useUnconfinedTestDispatcher()
@@ -44,8 +61,166 @@ class PosturingInteractorTest : SysuiTestCase() {
val postured by collectLastValue(underTest.postured)
assertThat(postured).isFalse()
- posturingRepository.fake.setPosturedState(PosturedState.Postured(1f))
+ posturingRepository.fake.emitPositionState(
+ PositionState(
+ stationary = PositionState.StationaryState.Stationary(confidence = 1f),
+ orientation = PositionState.OrientationState.Postured(confidence = 1f),
+ )
+ )
+
+ advanceTimeBySlidingWindowAndRun()
+ assertThat(postured).isTrue()
+ }
+
+ @Test
+ fun testLowConfidenceOrientation() =
+ kosmos.runTest {
+ val postured by collectLastValue(underTest.postured)
+ assertThat(postured).isFalse()
+
+ posturingRepository.fake.emitPositionState(
+ PositionState(
+ stationary = PositionState.StationaryState.Stationary(confidence = 1f),
+ orientation = PositionState.OrientationState.Postured(confidence = 0.2f),
+ )
+ )
+
+ advanceTimeBySlidingWindowAndRun()
+ assertThat(postured).isFalse()
+ }
+
+ @Test
+ fun testLowConfidenceStationary() =
+ kosmos.runTest {
+ val postured by collectLastValue(underTest.postured)
+ assertThat(postured).isFalse()
+
+ posturingRepository.fake.emitPositionState(
+ PositionState(
+ stationary = PositionState.StationaryState.Stationary(confidence = 1f),
+ orientation = PositionState.OrientationState.Postured(confidence = 0.2f),
+ )
+ )
+
+ advanceTimeBySlidingWindowAndRun()
+ assertThat(postured).isFalse()
+ }
+
+ @Test
+ fun testSlidingWindow() =
+ kosmos.runTest {
+ val postured by collectLastValue(underTest.postured)
+ assertThat(postured).isFalse()
+
+ posturingRepository.fake.emitPositionState(
+ PositionState(
+ stationary = PositionState.StationaryState.Stationary(confidence = 1f),
+ orientation = PositionState.OrientationState.Postured(confidence = 0.2f),
+ )
+ )
+
+ advanceTimeBy(PosturingInteractor.SLIDING_WINDOW_DURATION / 2)
+ runCurrent()
+ assertThat(postured).isFalse()
+
+ posturingRepository.fake.emitPositionState(
+ PositionState(
+ stationary = PositionState.StationaryState.Stationary(confidence = 1f),
+ orientation = PositionState.OrientationState.Postured(confidence = 1f),
+ )
+ )
+ assertThat(postured).isFalse()
+ advanceTimeBy(PosturingInteractor.SLIDING_WINDOW_DURATION / 2)
+ runCurrent()
+
+ // The 0.2 confidence will have fallen out of the sliding window, and we should now flip
+ // to true.
+ assertThat(postured).isTrue()
+
+ advanceTimeBy(9999.hours)
+ // We should remain postured if no other updates are received.
+ assertThat(postured).isTrue()
+ }
+
+ @Test
+ fun testLiftGesture_afterSlidingWindow() =
+ kosmos.runTest {
+ val triggerSensor = stubSensorManager()
+ val sensor = asyncSensorManager.getDefaultSensor(Sensor.TYPE_PICK_UP_GESTURE)!!
+
+ val postured by collectLastValue(underTest.postured)
+ assertThat(postured).isFalse()
+
+ posturingRepository.fake.emitPositionState(
+ PositionState(
+ stationary = PositionState.StationaryState.Stationary(confidence = 1f),
+ orientation = PositionState.OrientationState.Postured(confidence = 1f),
+ )
+ )
+
+ advanceTimeBySlidingWindowAndRun()
+ assertThat(postured).isTrue()
+
+ // If we detect a lift gesture, we should transition back to not postured.
+ triggerSensor(sensor)
+ assertThat(postured).isFalse()
+
+ advanceTimeBy(9999.hours)
+ assertThat(postured).isFalse()
+ }
+
+ @Test
+ fun testLiftGesture_overridesSlidingWindow() =
+ kosmos.runTest {
+ val triggerSensor = stubSensorManager()
+ val sensor = asyncSensorManager.getDefaultSensor(Sensor.TYPE_PICK_UP_GESTURE)!!
+
+ val postured by collectLastValue(underTest.postured)
+ assertThat(postured).isFalse()
+
+ // Add multiple stationary + postured events to the sliding window.
+ repeat(100) {
+ advanceTimeBy(1.milliseconds)
+ posturingRepository.fake.emitPositionState(
+ PositionState(
+ stationary = PositionState.StationaryState.Stationary(confidence = 1f),
+ orientation = PositionState.OrientationState.Postured(confidence = 1f),
+ )
+ )
+ }
+
+ assertThat(postured).isTrue()
+
+ // If we detect a lift gesture, we should transition back to not postured immediately.
+ triggerSensor(sensor)
+ assertThat(postured).isFalse()
+ }
+
+ @Test
+ fun testSignificantMotion_afterSlidingWindow() =
+ kosmos.runTest {
+ val triggerSensor = stubSensorManager()
+ val sensor = asyncSensorManager.getDefaultSensor(Sensor.TYPE_SIGNIFICANT_MOTION)!!
+
+ val postured by collectLastValue(underTest.postured)
+ assertThat(postured).isFalse()
+
+ posturingRepository.fake.emitPositionState(
+ PositionState(
+ stationary = PositionState.StationaryState.Stationary(confidence = 1f),
+ orientation = PositionState.OrientationState.Postured(confidence = 1f),
+ )
+ )
+
+ advanceTimeBySlidingWindowAndRun()
assertThat(postured).isTrue()
+
+ // If we detect motion, we should transition back to not postured.
+ triggerSensor(sensor)
+ assertThat(postured).isFalse()
+
+ advanceTimeBy(9999.hours)
+ assertThat(postured).isFalse()
}
@Test
@@ -55,12 +230,51 @@ class PosturingInteractorTest : SysuiTestCase() {
assertThat(postured).isFalse()
underTest.setValueForDebug(PosturedState.NotPostured)
- posturingRepository.fake.setPosturedState(PosturedState.Postured(1f))
+ posturingRepository.fake.emitPositionState(
+ PositionState(
+ stationary = PositionState.StationaryState.Stationary(confidence = 1f),
+ orientation = PositionState.OrientationState.Postured(confidence = 1f),
+ )
+ )
// Repository value is overridden by debug value
assertThat(postured).isFalse()
underTest.setValueForDebug(PosturedState.Unknown)
+
+ advanceTimeBySlidingWindowAndRun()
assertThat(postured).isTrue()
}
+
+ private fun Kosmos.stubSensorManager(): (sensor: Sensor) -> Unit {
+ val callbacks = mutableMapOf<Sensor, List<TriggerEventListener>>()
+ val pickupSensor = mock<Sensor>()
+ val motionSensor = mock<Sensor>()
+
+ asyncSensorManager.stub {
+ on { getDefaultSensor(Sensor.TYPE_PICK_UP_GESTURE) } doReturn pickupSensor
+ on { getDefaultSensor(Sensor.TYPE_SIGNIFICANT_MOTION) } doReturn motionSensor
+ on { requestTriggerSensor(any(), any()) } doAnswer
+ {
+ val callback = it.arguments[0] as TriggerEventListener
+ val sensor = it.arguments[1] as Sensor
+ callbacks[sensor] = callbacks.getOrElse(sensor) { emptyList() } + callback
+ true
+ }
+ on { cancelTriggerSensor(any(), any()) } doAnswer
+ {
+ val callback = it.arguments[0] as TriggerEventListener
+ val sensor = it.arguments[1] as Sensor
+ callbacks[sensor] = callbacks.getOrElse(sensor) { emptyList() } - callback
+ true
+ }
+ }
+
+ return { sensor: Sensor ->
+ val list = callbacks.getOrElse(sensor) { emptyList() }
+ // Simulate a trigger sensor which unregisters callbacks after triggering.
+ callbacks[sensor] = emptyList()
+ list.forEach { it.onTrigger(mock()) }
+ }
+ }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/display/data/repository/DisplayRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/display/data/repository/DisplayRepositoryTest.kt
index 859137507bbf..358635e2400c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/display/data/repository/DisplayRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/display/data/repository/DisplayRepositoryTest.kt
@@ -80,17 +80,18 @@ class DisplayRepositoryTest : SysuiTestCase() {
testScope.backgroundScope,
UnconfinedTestDispatcher(),
)
- DisplayRepositoryImpl(
+ val displaysWithDecorRepository =
+ DisplaysWithDecorationsRepositoryImpl(
commandQueue,
windowManager,
testScope.backgroundScope,
displayRepositoryFromLib,
)
- .also {
- verify(displayManager, never()).registerDisplayListener(any(), any())
- // It needs to be called, just once, for the initial value.
- verify(displayManager).getDisplays()
- }
+ DisplayRepositoryImpl(displayRepositoryFromLib, displaysWithDecorRepository).also {
+ verify(displayManager, never()).registerDisplayListener(any(), any())
+ // It needs to be called, just once, for the initial value.
+ verify(displayManager).getDisplays()
+ }
}
@Before
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/display/data/repository/DisplayScopeRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/display/data/repository/DisplayScopeRepositoryInstanceProviderTest.kt
index da7a723f220e..8afaea32273d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/display/data/repository/DisplayScopeRepositoryImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/display/data/repository/DisplayScopeRepositoryInstanceProviderTest.kt
@@ -16,7 +16,6 @@
package com.android.systemui.display.data.repository
-import android.platform.test.annotations.EnableFlags
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
@@ -24,77 +23,41 @@ import com.android.systemui.kosmos.applicationCoroutineScope
import com.android.systemui.kosmos.testDispatcher
import com.android.systemui.kosmos.testScope
import com.android.systemui.kosmos.useUnconfinedTestDispatcher
-import com.android.systemui.statusbar.core.StatusBarConnectedDisplays
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.isActive
import kotlinx.coroutines.test.runTest
-import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
-@EnableFlags(StatusBarConnectedDisplays.FLAG_NAME)
@RunWith(AndroidJUnit4::class)
@SmallTest
-class DisplayScopeRepositoryImplTest : SysuiTestCase() {
+class DisplayScopeRepositoryInstanceProviderTest : SysuiTestCase() {
private val kosmos = testKosmos().useUnconfinedTestDispatcher()
private val testScope = kosmos.testScope
- private val fakeDisplayRepository = kosmos.displayRepository
- private val repo =
- DisplayScopeRepositoryImpl(
+ private val underTest =
+ DisplayScopeRepositoryInstanceProvider(
kosmos.applicationCoroutineScope,
kosmos.testDispatcher,
- fakeDisplayRepository,
)
- @Before
- fun setUp() {
- repo.start()
- }
-
- @Test
- fun scopeForDisplay_multipleCallsForSameDisplayId_returnsSameInstance() {
- val scopeForDisplay = repo.scopeForDisplay(displayId = 1)
-
- assertThat(repo.scopeForDisplay(displayId = 1)).isSameInstanceAs(scopeForDisplay)
- }
-
- @Test
- fun scopeForDisplay_differentDisplayId_returnsNewInstance() {
- val scopeForDisplay1 = repo.scopeForDisplay(displayId = 1)
- val scopeForDisplay2 = repo.scopeForDisplay(displayId = 2)
-
- assertThat(scopeForDisplay1).isNotSameInstanceAs(scopeForDisplay2)
- }
-
@Test
- fun scopeForDisplay_activeByDefault() =
+ fun createInstance_activeByDefault() =
testScope.runTest {
- val scopeForDisplay = repo.scopeForDisplay(displayId = 1)
+ val scopeForDisplay = underTest.createInstance(displayId = 1)
assertThat(scopeForDisplay.isActive).isTrue()
}
@Test
- fun scopeForDisplay_afterDisplayRemoved_scopeIsCancelled() =
+ fun destroyInstance_afterDisplayRemoved_scopeIsCancelled() =
testScope.runTest {
- val scopeForDisplay = repo.scopeForDisplay(displayId = 1)
+ val scopeForDisplay = underTest.createInstance(displayId = 1)
- fakeDisplayRepository.removeDisplay(displayId = 1)
+ underTest.destroyInstance(scopeForDisplay)
assertThat(scopeForDisplay.isActive).isFalse()
}
-
- @Test
- fun scopeForDisplay_afterDisplayRemoved_returnsNewInstance() =
- testScope.runTest {
- val initialScope = repo.scopeForDisplay(displayId = 1)
-
- fakeDisplayRepository.removeDisplay(displayId = 1)
-
- val newScope = repo.scopeForDisplay(displayId = 1)
- assertThat(newScope).isNotSameInstanceAs(initialScope)
- }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/display/data/repository/PerDisplayInstanceRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/display/data/repository/PerDisplayInstanceRepositoryImplTest.kt
index 6c7783ae44e1..bf49d92dd2bd 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/display/data/repository/PerDisplayInstanceRepositoryImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/display/data/repository/PerDisplayInstanceRepositoryImplTest.kt
@@ -19,6 +19,7 @@ package com.android.systemui.display.data.repository
import android.view.Display
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import com.android.app.displaylib.PerDisplayInstanceRepositoryImpl
import com.android.systemui.SysuiTestCase
import com.android.systemui.dump.dumpManager
import com.android.systemui.kosmos.testScope
@@ -43,9 +44,10 @@ class PerDisplayInstanceRepositoryImplTest : SysuiTestCase() {
private val fakeDisplayRepository = kosmos.displayRepository
private val fakePerDisplayInstanceProviderWithTeardown =
kosmos.fakePerDisplayInstanceProviderWithTeardown
+ private val lifecycleManager = kosmos.fakeDisplayInstanceLifecycleManager
private val underTest: PerDisplayInstanceRepositoryImpl<TestPerDisplayInstance> =
- kosmos.fakePerDisplayInstanceRepository
+ kosmos.createPerDisplayInstanceRepository(overrideLifecycleManager = null)
@Before
fun addDisplays() = runBlocking {
@@ -108,6 +110,43 @@ class PerDisplayInstanceRepositoryImplTest : SysuiTestCase() {
verify(kosmos.dumpManager).registerNormalDumpable(anyString(), any())
}
+ @Test
+ fun perDisplay_afterCustomLifecycleManagerRemovesDisplay_destroyInstanceInvoked() =
+ testScope.runTest {
+ val underTest =
+ kosmos.createPerDisplayInstanceRepository(
+ overrideLifecycleManager = lifecycleManager
+ )
+ // Let's start with both
+ lifecycleManager.displayIds.value = setOf(DEFAULT_DISPLAY_ID, NON_DEFAULT_DISPLAY_ID)
+
+ val instance = underTest[NON_DEFAULT_DISPLAY_ID]
+
+ lifecycleManager.displayIds.value = setOf(DEFAULT_DISPLAY_ID)
+
+ // Now that the lifecycle manager says so, let's make sure it was destroyed
+ assertThat(fakePerDisplayInstanceProviderWithTeardown.destroyed)
+ .containsExactly(instance)
+ }
+
+ @Test
+ fun perDisplay_lifecycleManagerDoesNotContainIt_displayRepositoryDoes_returnsNull() =
+ testScope.runTest {
+ val underTest =
+ kosmos.createPerDisplayInstanceRepository(
+ overrideLifecycleManager = lifecycleManager
+ )
+ // only default display, so getting for the non-default one should fail, despite the
+ // repository having both displays already
+ lifecycleManager.displayIds.value = setOf(DEFAULT_DISPLAY_ID)
+
+ assertThat(underTest[NON_DEFAULT_DISPLAY_ID]).isNull()
+
+ lifecycleManager.displayIds.value = setOf(DEFAULT_DISPLAY_ID, NON_DEFAULT_DISPLAY_ID)
+
+ assertThat(underTest[NON_DEFAULT_DISPLAY_ID]).isNotNull()
+ }
+
private fun createDisplay(displayId: Int): Display =
display(type = Display.TYPE_INTERNAL, id = displayId)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayServiceTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayServiceTest.kt
index b42eddd12e4e..fd99313a17b7 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayServiceTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayServiceTest.kt
@@ -36,7 +36,6 @@ import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.LifecycleRegistry
import androidx.test.filters.SmallTest
-import com.android.app.viewcapture.ViewCaptureAwareWindowManager
import com.android.app.viewcapture.ViewCaptureFactory
import com.android.compose.animation.scene.ObservableTransitionState
import com.android.internal.logging.UiEventLogger
@@ -143,7 +142,6 @@ class DreamOverlayServiceTest(flags: FlagsParameterization?) : SysuiTestCase() {
mock<ScrimManager> { on { currentController }.thenReturn(mScrimController) }
private val mSystemDialogsCloser = mock<SystemDialogsCloser>()
private val mDreamOverlayCallbackController = mock<DreamOverlayCallbackController>()
- private val mLazyViewCapture = lazy { viewCaptureSpy }
private val mViewCaptor = argumentCaptor<View>()
private val mTouchHandlersCaptor = argumentCaptor<Set<TouchHandler>>()
@@ -156,7 +154,6 @@ class DreamOverlayServiceTest(flags: FlagsParameterization?) : SysuiTestCase() {
private val gestureInteractor = spy(kosmos.gestureInteractor)
private lateinit var mCommunalInteractor: CommunalInteractor
- private lateinit var mViewCaptureAwareWindowManager: ViewCaptureAwareWindowManager
private lateinit var environmentComponents: EnvironmentComponents
private lateinit var mService: DreamOverlayService
@@ -244,18 +241,12 @@ class DreamOverlayServiceTest(flags: FlagsParameterization?) : SysuiTestCase() {
mComplicationComponentFactory,
mAmbientTouchComponentFactory,
)
- mViewCaptureAwareWindowManager =
- ViewCaptureAwareWindowManager(
- mWindowManager,
- mLazyViewCapture,
- isViewCaptureEnabled = false,
- )
mService =
DreamOverlayService(
mContext,
mLifecycleOwner,
mMainExecutor,
- mViewCaptureAwareWindowManager,
+ mWindowManager,
mComplicationComponentFactory,
mDreamComplicationComponentFactory,
mDreamOverlayComponentFactory,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutCustomizationModeRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutCustomizationModeRepositoryTest.kt
new file mode 100644
index 000000000000..e955ca85c8e2
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutCustomizationModeRepositoryTest.kt
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2025 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.keyboard.shortcut.data.repository
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.keyboard.shortcut.shortcutHelperCustomizationModeRepository
+import com.android.systemui.keyboard.shortcut.shortcutHelperTestHelper
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.kosmos.useUnconfinedTestDispatcher
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class ShortcutCustomizationModeRepositoryTest : SysuiTestCase() {
+
+ private val kosmos = testKosmos().useUnconfinedTestDispatcher()
+ private val repo = kosmos.shortcutHelperCustomizationModeRepository
+ private val testScope = kosmos.testScope
+ private val helper = kosmos.shortcutHelperTestHelper
+
+ @Test
+ fun customizationMode_disabledByDefault() {
+ testScope.runTest {
+ val customizationMode by collectLastValue(repo.isCustomizationModeEnabled)
+
+ assertThat(customizationMode).isFalse()
+ }
+ }
+
+ @Test
+ fun customizationMode_enabledOnRequest_whenShortcutHelperIsOpen() {
+ testScope.runTest {
+ val customizationMode by collectLastValue(repo.isCustomizationModeEnabled)
+ helper.showFromActivity()
+ repo.toggleCustomizationMode(isCustomizing = true)
+ assertThat(customizationMode).isTrue()
+ }
+ }
+
+ @Test
+ fun customizationMode_disabledOnRequest_whenShortcutHelperIsOpen() {
+ testScope.runTest {
+ val customizationMode by collectLastValue(repo.isCustomizationModeEnabled)
+ helper.showFromActivity()
+ repo.toggleCustomizationMode(isCustomizing = true)
+ repo.toggleCustomizationMode(isCustomizing = false)
+ assertThat(customizationMode).isFalse()
+ }
+ }
+
+ @Test
+ fun customizationMode_disabledWhenShortcutHelperIsDismissed() {
+ testScope.runTest {
+ val customizationMode by collectLastValue(repo.isCustomizationModeEnabled)
+ helper.showFromActivity()
+ repo.toggleCustomizationMode(isCustomizing = true)
+ helper.hideFromActivity()
+ assertThat(customizationMode).isFalse()
+ }
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutHelperViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutHelperViewModelTest.kt
index 766744885077..d4292bf5b66c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutHelperViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutHelperViewModelTest.kt
@@ -56,7 +56,6 @@ import com.android.systemui.keyboard.shortcut.shortcutHelperViewModel
import com.android.systemui.keyboard.shortcut.ui.model.IconSource
import com.android.systemui.keyboard.shortcut.ui.model.ShortcutCategoryUi
import com.android.systemui.keyboard.shortcut.ui.model.ShortcutsUiState
-import com.android.systemui.kosmos.testDispatcher
import com.android.systemui.kosmos.testScope
import com.android.systemui.kosmos.useUnconfinedTestDispatcher
import com.android.systemui.model.sysUiState
@@ -66,7 +65,8 @@ import com.android.systemui.settings.userTracker
import com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_SHORTCUT_HELPER_SHOWING
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.test.UnconfinedTestDispatcher
+import kotlin.test.assertFalse
+import kotlin.test.assertTrue
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
@@ -89,7 +89,6 @@ class ShortcutHelperViewModelTest : SysuiTestCase() {
private val kosmos =
testKosmos().useUnconfinedTestDispatcher().also {
- it.testDispatcher = UnconfinedTestDispatcher()
it.shortcutHelperSystemShortcutsSource = fakeSystemSource
it.shortcutHelperMultiTaskingShortcutsSource = fakeMultiTaskingSource
it.shortcutHelperAppCategoriesShortcutsSource = FakeKeyboardShortcutGroupsSource()
@@ -445,6 +444,51 @@ class ShortcutHelperViewModelTest : SysuiTestCase() {
assertThat((uiState as? ShortcutsUiState.Active)?.searchQuery).isEqualTo("")
}
+ @Test
+ fun shortcutsUiState_customizationModeDisabledByDefault() {
+ testScope.runTest {
+ testHelper.showFromActivity()
+ val uiState by collectLastValue(viewModel.shortcutsUiState)
+
+ assertFalse((uiState as ShortcutsUiState.Active).isCustomizationModeEnabled)
+ }
+ }
+
+ @Test
+ fun shortcutsUiState_customizationModeEnabledOnRequest() {
+ testScope.runTest {
+ testHelper.showFromActivity()
+ val uiState by collectLastValue(viewModel.shortcutsUiState)
+ viewModel.toggleCustomizationMode(true)
+
+ assertTrue((uiState as ShortcutsUiState.Active).isCustomizationModeEnabled)
+ }
+ }
+
+ @Test
+ fun shortcutsUiState_customizationModeDisabledOnRequest() {
+ testScope.runTest {
+ testHelper.showFromActivity()
+ val uiState by collectLastValue(viewModel.shortcutsUiState)
+ viewModel.toggleCustomizationMode(true)
+ viewModel.toggleCustomizationMode(false)
+
+ assertFalse((uiState as ShortcutsUiState.Active).isCustomizationModeEnabled)
+ }
+ }
+
+ @Test
+ fun shortcutsUiState_customizationModeDisabledWhenShortcutHelperIsReopened() {
+ testScope.runTest {
+ testHelper.showFromActivity()
+ val uiState by collectLastValue(viewModel.shortcutsUiState)
+ viewModel.toggleCustomizationMode(true)
+ closeAndReopenShortcutHelper()
+
+ assertFalse((uiState as ShortcutsUiState.Active).isCustomizationModeEnabled)
+ }
+ }
+
private fun openHelperAndSearchForFooString() {
testHelper.showFromActivity()
viewModel.onSearchQueryChanged("foo")
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractorTest.kt
index 63229dbb47a4..8fdbf9b45d2e 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractorTest.kt
@@ -229,7 +229,6 @@ class FromAlternateBouncerTransitionInteractorTest(flags: FlagsParameterization)
}
@Test
- @EnableFlags(FLAG_GLANCEABLE_HUB_V2)
fun transitionToDreaming() =
kosmos.runTest {
fakePowerRepository.updateWakefulness(
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt
index 0718d0d32812..83fd4c258082 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt
@@ -264,6 +264,29 @@ class KeyguardInteractorTest : SysuiTestCase() {
}
@Test
+ fun dismissAlpha_doesNotEmitWhenNotDismissible() =
+ testScope.runTest {
+ val dismissAlpha by collectValues(underTest.dismissAlpha)
+ assertThat(dismissAlpha[0]).isEqualTo(1f)
+ assertThat(dismissAlpha.size).isEqualTo(1)
+
+ keyguardTransitionRepository.sendTransitionSteps(from = AOD, to = LOCKSCREEN, testScope)
+
+ // User begins to swipe up when not dimissible, which would show bouncer
+ repository.setStatusBarState(StatusBarState.KEYGUARD)
+ repository.setKeyguardDismissible(false)
+ shadeRepository.setLegacyShadeExpansion(0.98f)
+
+ assertThat(dismissAlpha[0]).isEqualTo(1f)
+ assertThat(dismissAlpha.size).isEqualTo(1)
+
+ // Shade reset should not affect dismiss alpha when not dismissible
+ shadeRepository.setLegacyShadeExpansion(0f)
+ assertThat(dismissAlpha[0]).isEqualTo(1f)
+ assertThat(dismissAlpha.size).isEqualTo(1)
+ }
+
+ @Test
fun dismissAlpha_onGlanceableHub_doesNotEmitWhenShadeResets() =
testScope.runTest {
val dismissAlpha by collectValues(underTest.dismissAlpha)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardKeyEventInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardKeyEventInteractorTest.kt
index 6704d63395ad..582666561be2 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardKeyEventInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardKeyEventInteractorTest.kt
@@ -267,12 +267,20 @@ class KeyguardKeyEventInteractorTest : SysuiTestCase() {
// action down: does NOT collapse the shade
val actionDownMenuKeyEvent = KeyEvent(KeyEvent.ACTION_DOWN, keycode)
assertThat(underTest.dispatchKeyEvent(actionDownMenuKeyEvent)).isFalse()
- verify(statusBarKeyguardViewManager, never()).showPrimaryBouncer(any())
+ verify(statusBarKeyguardViewManager, never())
+ .showPrimaryBouncer(
+ any(),
+ eq("KeyguardKeyEventInteractor#collapseShadeLockedOrShowPrimaryBouncer"),
+ )
// action up: collapses the shade
val actionUpMenuKeyEvent = KeyEvent(KeyEvent.ACTION_UP, keycode)
assertThat(underTest.dispatchKeyEvent(actionUpMenuKeyEvent)).isTrue()
- verify(statusBarKeyguardViewManager).showPrimaryBouncer(eq(true))
+ verify(statusBarKeyguardViewManager)
+ .showPrimaryBouncer(
+ eq(true),
+ eq("KeyguardKeyEventInteractor#collapseShadeLockedOrShowPrimaryBouncer"),
+ )
}
private fun verifyActionsDoNothing(keycode: Int) {
@@ -280,12 +288,20 @@ class KeyguardKeyEventInteractorTest : SysuiTestCase() {
val actionDownMenuKeyEvent = KeyEvent(KeyEvent.ACTION_DOWN, keycode)
assertThat(underTest.dispatchKeyEvent(actionDownMenuKeyEvent)).isFalse()
verify(shadeController, never()).animateCollapseShadeForced()
- verify(statusBarKeyguardViewManager, never()).showPrimaryBouncer(any())
+ verify(statusBarKeyguardViewManager, never())
+ .showPrimaryBouncer(
+ any(),
+ eq("KeyguardKeyEventInteractor#collapseShadeLockedOrShowPrimaryBouncer"),
+ )
// action up: doesNothing
val actionUpMenuKeyEvent = KeyEvent(KeyEvent.ACTION_UP, keycode)
assertThat(underTest.dispatchKeyEvent(actionUpMenuKeyEvent)).isFalse()
verify(shadeController, never()).animateCollapseShadeForced()
- verify(statusBarKeyguardViewManager, never()).showPrimaryBouncer(any())
+ verify(statusBarKeyguardViewManager, never())
+ .showPrimaryBouncer(
+ any(),
+ eq("KeyguardKeyEventInteractor#collapseShadeLockedOrShowPrimaryBouncer"),
+ )
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorTest.kt
index f0eedee48e57..4f351143c793 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorTest.kt
@@ -340,6 +340,22 @@ class WindowManagerLockscreenVisibilityInteractorTest : SysuiTestCase() {
@Test
@EnableSceneContainer
+ fun surfaceBehindVisibility_whileSceneContainerNotVisible_alwaysTrue() =
+ testScope.runTest {
+ val isSurfaceBehindVisible by collectLastValue(underTest.value.surfaceBehindVisibility)
+ val currentScene by collectLastValue(kosmos.sceneInteractor.currentScene)
+ assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
+ assertThat(isSurfaceBehindVisible).isFalse()
+
+ kosmos.sceneInteractor.setVisible(false, "test")
+ runCurrent()
+
+ assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
+ assertThat(isSurfaceBehindVisible).isTrue()
+ }
+
+ @Test
+ @EnableSceneContainer
fun surfaceBehindVisibility_idleWhileLocked_alwaysFalse() =
testScope.runTest {
val isSurfaceBehindVisible by collectLastValue(underTest.value.surfaceBehindVisibility)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModelTest.kt
index e1323c166f6b..9aee4c97f214 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModelTest.kt
@@ -35,6 +35,7 @@ import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.statusbar.phone.statusBarKeyguardViewManager
import com.android.systemui.testKosmos
import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.eq
import com.google.common.collect.Range
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.test.runTest
@@ -56,7 +57,8 @@ class AlternateBouncerViewModelTest : SysuiTestCase() {
fun onTapped() =
testScope.runTest {
underTest.onTapped()
- verify(statusBarKeyguardViewManager).showPrimaryBouncer(any())
+ verify(statusBarKeyguardViewManager)
+ .showPrimaryBouncer(any(), eq("AlternateBouncerViewModel#onTapped"))
}
@Test
@@ -154,7 +156,7 @@ class AlternateBouncerViewModelTest : SysuiTestCase() {
private fun stepToAlternateBouncer(
value: Float,
- state: TransitionState = TransitionState.RUNNING
+ state: TransitionState = TransitionState.RUNNING,
): TransitionStep {
return step(
from = KeyguardState.LOCKSCREEN,
@@ -166,7 +168,7 @@ class AlternateBouncerViewModelTest : SysuiTestCase() {
private fun stepFromAlternateBouncer(
value: Float,
- state: TransitionState = TransitionState.RUNNING
+ state: TransitionState = TransitionState.RUNNING,
): TransitionStep {
return step(
from = KeyguardState.ALTERNATE_BOUNCER,
@@ -180,14 +182,14 @@ class AlternateBouncerViewModelTest : SysuiTestCase() {
from: KeyguardState,
to: KeyguardState,
value: Float,
- transitionState: TransitionState
+ transitionState: TransitionState,
): TransitionStep {
return TransitionStep(
from = from,
to = to,
value = value,
transitionState = transitionState,
- ownerName = "AlternateBouncerViewModelTest"
+ ownerName = "AlternateBouncerViewModelTest",
)
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/data/repository/MediaFilterRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/data/repository/MediaFilterRepositoryTest.kt
index 8a11be0ab8fa..b1427f21345c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/data/repository/MediaFilterRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/data/repository/MediaFilterRepositoryTest.kt
@@ -219,17 +219,13 @@ class MediaFilterRepositoryTest : SysuiTestCase() {
underTest.addSelectedUserMediaEntry(playingData1)
underTest.addMediaDataLoadingState(MediaDataLoadingModel.Loaded(playingInstanceId1))
underTest.addSelectedUserMediaEntry(playingData2)
- underTest.addMediaDataLoadingState(
- MediaDataLoadingModel.Loaded(playingInstanceId2, false)
- )
+ underTest.addMediaDataLoadingState(MediaDataLoadingModel.Loaded(playingInstanceId2))
assertThat(currentMedia?.size).isEqualTo(2)
assertThat(currentMedia)
.containsExactly(
MediaCommonModel.MediaControl(MediaDataLoadingModel.Loaded(playingInstanceId1)),
- MediaCommonModel.MediaControl(
- MediaDataLoadingModel.Loaded(playingInstanceId2, false)
- ),
+ MediaCommonModel.MediaControl(MediaDataLoadingModel.Loaded(playingInstanceId2)),
)
.inOrder()
@@ -238,9 +234,7 @@ class MediaFilterRepositoryTest : SysuiTestCase() {
assertThat(currentMedia?.size).isEqualTo(2)
assertThat(currentMedia)
.containsExactly(
- MediaCommonModel.MediaControl(
- MediaDataLoadingModel.Loaded(playingInstanceId2, false)
- ),
+ MediaCommonModel.MediaControl(MediaDataLoadingModel.Loaded(playingInstanceId2)),
MediaCommonModel.MediaControl(MediaDataLoadingModel.Loaded(playingInstanceId1)),
)
.inOrder()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/ui/util/MediaDiffUtilTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/ui/util/MediaDiffUtilTest.kt
index faa62c2febc1..45128e23c900 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/ui/util/MediaDiffUtilTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/ui/util/MediaDiffUtilTest.kt
@@ -21,7 +21,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.internal.logging.InstanceId
import com.android.systemui.SysuiTestCase
-import com.android.systemui.media.controls.ui.viewmodel.MediaCommonViewModel
+import com.android.systemui.media.controls.ui.viewmodel.MediaControlViewModel
import com.android.systemui.media.controls.ui.viewmodel.mediaControlViewModel
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
@@ -37,8 +37,8 @@ class MediaDiffUtilTest : SysuiTestCase() {
@Test
fun newMediaControlAdded() {
- val mediaControl = createMediaControl(InstanceId.fakeInstanceId(123), true)
- val oldList = listOf<MediaCommonViewModel>()
+ val mediaControl = createMediaControl(InstanceId.fakeInstanceId(123))
+ val oldList = listOf<MediaControlViewModel>()
val newList = listOf(mediaControl)
val mediaLoadedCallback = MediaViewModelCallback(oldList, newList)
val mediaLoadedListUpdateCallback =
@@ -56,18 +56,18 @@ class MediaDiffUtilTest : SysuiTestCase() {
@Test
fun updateMediaControl_contentChanged() {
- val mediaControl = createMediaControl(InstanceId.fakeInstanceId(123), true)
+ val mediaControl = createMediaControl(InstanceId.fakeInstanceId(123))
val oldList = listOf(mediaControl)
- val newList = listOf(mediaControl.copy(immediatelyUpdateUi = false))
+ val newList = listOf(mediaControl.copy())
val mediaLoadedCallback = MediaViewModelCallback(oldList, newList)
val mediaLoadedListUpdateCallback =
MediaViewModelListUpdateCallback(
oldList,
newList,
- { commonViewModel, _ -> fail("Unexpected to add $commonViewModel") },
- { commonViewModel, _ -> assertThat(commonViewModel).isNotEqualTo(mediaControl) },
+ { controlViewModel, _ -> fail("Unexpected to add $controlViewModel") },
+ { controlViewModel, _ -> assertThat(controlViewModel).isNotEqualTo(mediaControl) },
{ fail("Unexpected to remove $it") },
- { commonViewModel, _, _ -> fail("Unexpected to move $commonViewModel ") },
+ { controlViewModel, _, _ -> fail("Unexpected to move $controlViewModel ") },
)
DiffUtil.calculateDiff(mediaLoadedCallback).dispatchUpdatesTo(mediaLoadedListUpdateCallback)
@@ -75,8 +75,8 @@ class MediaDiffUtilTest : SysuiTestCase() {
@Test
fun mediaControlMoved() {
- val mediaControl1 = createMediaControl(InstanceId.fakeInstanceId(123), true)
- val mediaControl2 = createMediaControl(InstanceId.fakeInstanceId(456), false)
+ val mediaControl1 = createMediaControl(InstanceId.fakeInstanceId(123))
+ val mediaControl2 = createMediaControl(InstanceId.fakeInstanceId(456))
val oldList = listOf(mediaControl1, mediaControl2)
val newList = listOf(mediaControl2, mediaControl1)
val mediaLoadedCallback = MediaViewModelCallback(oldList, newList)
@@ -84,10 +84,10 @@ class MediaDiffUtilTest : SysuiTestCase() {
MediaViewModelListUpdateCallback(
oldList,
newList,
- { commonViewModel, _ -> fail("Unexpected to add $commonViewModel") },
- { commonViewModel, _ -> fail("Unexpected to update $commonViewModel") },
+ { controlViewModel, _ -> fail("Unexpected to add $controlViewModel") },
+ { controlViewModel, _ -> fail("Unexpected to update $controlViewModel") },
{ fail("Unexpected to remove $it") },
- { commonViewModel, _, _ -> assertThat(commonViewModel).isEqualTo(mediaControl1) },
+ { controlViewModel, _, _ -> assertThat(controlViewModel).isEqualTo(mediaControl1) },
)
DiffUtil.calculateDiff(mediaLoadedCallback).dispatchUpdatesTo(mediaLoadedListUpdateCallback)
@@ -95,34 +95,24 @@ class MediaDiffUtilTest : SysuiTestCase() {
@Test
fun mediaControlRemoved() {
- val mediaControl = createMediaControl(InstanceId.fakeInstanceId(123), true)
+ val mediaControl = createMediaControl(InstanceId.fakeInstanceId(123))
val oldList = listOf(mediaControl)
- val newList = listOf<MediaCommonViewModel>()
+ val newList = listOf<MediaControlViewModel>()
val mediaLoadedCallback = MediaViewModelCallback(oldList, newList)
val mediaLoadedListUpdateCallback =
MediaViewModelListUpdateCallback(
oldList,
newList,
- { commonViewModel, _ -> fail("Unexpected to add $commonViewModel") },
- { commonViewModel, _ -> fail("Unexpected to update $commonViewModel") },
- { commonViewModel -> assertThat(commonViewModel).isEqualTo(mediaControl) },
- { commonViewModel, _, _ -> fail("Unexpected to move $commonViewModel ") },
+ { controlViewModel, _ -> fail("Unexpected to add $controlViewModel") },
+ { controlViewModel, _ -> fail("Unexpected to update $controlViewModel") },
+ { controlViewModel -> assertThat(controlViewModel).isEqualTo(mediaControl) },
+ { controlViewModel, _, _ -> fail("Unexpected to move $controlViewModel ") },
)
DiffUtil.calculateDiff(mediaLoadedCallback).dispatchUpdatesTo(mediaLoadedListUpdateCallback)
}
- private fun createMediaControl(
- instanceId: InstanceId,
- immediatelyUpdateUi: Boolean,
- ): MediaCommonViewModel.MediaControl {
- return MediaCommonViewModel.MediaControl(
- instanceId = instanceId,
- immediatelyUpdateUi = immediatelyUpdateUi,
- controlViewModel = kosmos.mediaControlViewModel,
- onAdded = {},
- onRemoved = {},
- onUpdated = {},
- )
+ private fun createMediaControl(instanceId: InstanceId): MediaControlViewModel {
+ return kosmos.mediaControlViewModel.copy(instanceId = instanceId)
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/ui/viewmodel/MediaCarouselViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/ui/viewmodel/MediaCarouselViewModelTest.kt
index e56b114dc847..fab5a3cdf8fd 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/ui/viewmodel/MediaCarouselViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/ui/viewmodel/MediaCarouselViewModelTest.kt
@@ -86,23 +86,23 @@ class MediaCarouselViewModelTest : SysuiTestCase() {
loadMediaControl(KEY_2, instanceId2, isPlaying = true)
loadMediaControl(KEY, instanceId1, isPlaying = false)
- var mediaControl2 = sortedMedia?.get(0) as MediaCommonViewModel.MediaControl
- var mediaControl1 = sortedMedia?.get(1) as MediaCommonViewModel.MediaControl
+ var mediaControl2 = sortedMedia?.get(0) as MediaControlViewModel
+ var mediaControl1 = sortedMedia?.get(1) as MediaControlViewModel
assertThat(mediaControl2.instanceId).isEqualTo(instanceId2)
assertThat(mediaControl1.instanceId).isEqualTo(instanceId1)
loadMediaControl(KEY, instanceId1, isPlaying = true)
loadMediaControl(KEY_2, instanceId2, isPlaying = false)
- mediaControl2 = sortedMedia?.get(0) as MediaCommonViewModel.MediaControl
- mediaControl1 = sortedMedia?.get(1) as MediaCommonViewModel.MediaControl
+ mediaControl2 = sortedMedia?.get(0) as MediaControlViewModel
+ mediaControl1 = sortedMedia?.get(1) as MediaControlViewModel
assertThat(mediaControl2.instanceId).isEqualTo(instanceId2)
assertThat(mediaControl1.instanceId).isEqualTo(instanceId1)
underTest.onReorderingAllowed()
- mediaControl1 = sortedMedia?.get(0) as MediaCommonViewModel.MediaControl
- mediaControl2 = sortedMedia?.get(1) as MediaCommonViewModel.MediaControl
+ mediaControl1 = sortedMedia?.get(0) as MediaControlViewModel
+ mediaControl2 = sortedMedia?.get(1) as MediaControlViewModel
assertThat(mediaControl1.instanceId).isEqualTo(instanceId1)
assertThat(mediaControl2.instanceId).isEqualTo(instanceId2)
}
@@ -115,7 +115,7 @@ class MediaCarouselViewModelTest : SysuiTestCase() {
loadMediaControl(KEY, instanceId)
- val mediaControl = sortedMedia?.get(0) as MediaCommonViewModel.MediaControl
+ val mediaControl = sortedMedia?.get(0) as MediaControlViewModel
assertThat(mediaControl.instanceId).isEqualTo(instanceId)
// when media control is added to carousel
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/mediaprojection/appselector/view/TaskPreviewSizeProviderTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/mediaprojection/appselector/view/TaskPreviewSizeProviderTest.kt
index 55e52b780488..18b14a4b3752 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/mediaprojection/appselector/view/TaskPreviewSizeProviderTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/mediaprojection/appselector/view/TaskPreviewSizeProviderTest.kt
@@ -55,8 +55,6 @@ class TaskPreviewSizeProviderTest : SysuiTestCase() {
@Before
fun setup() {
- whenever(mockContext.getSystemService(eq(WindowManager::class.java)))
- .thenReturn(windowManager)
whenever(mockContext.resources).thenReturn(resources)
}
@@ -154,7 +152,8 @@ class TaskPreviewSizeProviderTest : SysuiTestCase() {
return TaskPreviewSizeProvider(
mockContext,
windowMetricsProvider,
- testConfigurationController
+ testConfigurationController,
+ windowManager
)
.also { it.addCallback(listener) }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/model/SysUiStateExtTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/model/SysUiStateExtTest.kt
index d1b552906fbb..5d4de02f9aaa 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/model/SysUiStateExtTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/model/SysUiStateExtTest.kt
@@ -16,7 +16,6 @@
package com.android.systemui.model
-import android.view.Display
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
@@ -35,7 +34,7 @@ class SysUiStateExtTest : SysuiTestCase() {
@Test
fun updateFlags() {
- underTest.updateFlags(Display.DEFAULT_DISPLAY, 1L to true, 2L to false, 4L to true)
+ underTest.updateFlags(1L to true, 2L to false, 4L to true)
assertThat(underTest.flags and 1L).isNotEqualTo(0L)
assertThat(underTest.flags and 2L).isEqualTo(0L)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/navigationbar/views/NavigationBarTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/navigationbar/views/NavigationBarTest.java
index 09e49eb217b0..0e8c5079579e 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/navigationbar/views/NavigationBarTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/navigationbar/views/NavigationBarTest.java
@@ -82,7 +82,6 @@ import android.view.inputmethod.InputMethodManager;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
-import com.android.app.viewcapture.ViewCaptureAwareWindowManager;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.UiEventLogger;
import com.android.systemui.SysuiTestCase;
@@ -218,8 +217,6 @@ public class NavigationBarTest extends SysuiTestCase {
@Mock
private WindowManager mWindowManager;
@Mock
- private ViewCaptureAwareWindowManager mViewCaptureAwareWindowManager;
- @Mock
private TelecomManager mTelecomManager;
@Mock
private InputMethodManager mInputMethodManager;
@@ -685,7 +682,6 @@ public class NavigationBarTest extends SysuiTestCase {
null,
context,
mWindowManager,
- mViewCaptureAwareWindowManager,
() -> mAssistManager,
mock(AccessibilityManager.class),
deviceProvisionedController,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayActionsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayActionsViewModelTest.kt
index 52a0a5445002..7fb66ce21919 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayActionsViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayActionsViewModelTest.kt
@@ -22,8 +22,7 @@ import androidx.test.filters.SmallTest
import com.android.compose.animation.scene.Back
import com.android.compose.animation.scene.Swipe
import com.android.compose.animation.scene.UserActionResult.HideOverlay
-import com.android.compose.animation.scene.UserActionResult.ShowOverlay
-import com.android.compose.animation.scene.UserActionResult.ShowOverlay.HideCurrentOverlays
+import com.android.compose.animation.scene.UserActionResult.ReplaceByOverlay
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.flags.EnableSceneContainer
@@ -77,10 +76,8 @@ class NotificationsShadeOverlayActionsViewModelTest : SysuiTestCase() {
underTest.activateIn(this)
val action =
- (actions?.get(Swipe.Down(fromSource = SceneContainerArea.EndHalf)) as? ShowOverlay)
+ (actions?.get(Swipe.Down(fromSource = SceneContainerArea.TopEdgeEndHalf))
+ as? ReplaceByOverlay)
assertThat(action?.overlay).isEqualTo(Overlays.QuickSettingsShade)
- val overlaysToHide = action?.hideCurrentOverlays as? HideCurrentOverlays.Some
- assertThat(overlaysToHide).isNotNull()
- assertThat(overlaysToHide?.overlays).containsExactly(Overlays.NotificationsShade)
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayContentViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayContentViewModelTest.kt
index 80ce43d61003..ffcd95bc7a4a 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayContentViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayContentViewModelTest.kt
@@ -157,7 +157,7 @@ class NotificationsShadeOverlayContentViewModelTest : SysuiTestCase() {
}
@Test
- fun showMedia_noActiveMedia_false() =
+ fun showMedia_InactiveMedia_false() =
testScope.runTest {
kosmos.mediaFilterRepository.addSelectedUserMediaEntry(MediaData(active = false))
runCurrent()
@@ -166,6 +166,16 @@ class NotificationsShadeOverlayContentViewModelTest : SysuiTestCase() {
}
@Test
+ fun showMedia_noMedia_false() =
+ testScope.runTest {
+ kosmos.mediaFilterRepository.addSelectedUserMediaEntry(MediaData(active = true))
+ kosmos.mediaFilterRepository.clearSelectedUserMedia()
+ runCurrent()
+
+ assertThat(underTest.showMedia).isFalse()
+ }
+
+ @Test
fun showMedia_qsDisabled_false() =
testScope.runTest {
kosmos.mediaFilterRepository.addSelectedUserMediaEntry(MediaData(active = true))
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/power/PowerUITest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/power/PowerUITest.java
index 338ed7596bd6..70ded2a7f21c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/power/PowerUITest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/power/PowerUITest.java
@@ -40,11 +40,11 @@ import android.service.vr.IVrStateCallbacks;
import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
import android.testing.TestableResources;
+import android.view.WindowManager;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
-import com.android.app.viewcapture.ViewCapture;
import com.android.settingslib.fuelgauge.Estimate;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.broadcast.BroadcastDispatcher;
@@ -54,8 +54,6 @@ import com.android.systemui.res.R;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.CommandQueue;
-import dagger.Lazy;
-
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -96,7 +94,7 @@ public class PowerUITest extends SysuiTestCase {
@Mock private BroadcastDispatcher mBroadcastDispatcher;
@Mock private CommandQueue mCommandQueue;
@Mock private IVrManager mVrManager;
- @Mock private Lazy<ViewCapture> mLazyViewCapture;
+ @Mock private WindowManager mWindowManager;
@Before
public void setup() {
@@ -709,7 +707,7 @@ public class PowerUITest extends SysuiTestCase {
mWakefulnessLifecycle,
mPowerManager,
mUserTracker,
- mLazyViewCapture);
+ mWindowManager);
mPowerUI.mThermalService = mThermalServiceMock;
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qrcodescanner/controller/QRCodeScannerControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/qrcodescanner/controller/QRCodeScannerControllerTest.java
index 0f631509bfba..d9990ba8e9ae 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qrcodescanner/controller/QRCodeScannerControllerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qrcodescanner/controller/QRCodeScannerControllerTest.java
@@ -275,14 +275,14 @@ public class QRCodeScannerControllerTest extends SysuiTestCase {
setUpLocal(/* deviceConfigActivity */ null, /* defaultActivity */
"abc/.def", /* validateActivity */ true, /* enableSetting */true,
/* enableOnLockScreen */ true);
- mSecureSettings.putStringForUser(LOCK_SCREEN_SHOW_QR_CODE_SCANNER, "0",
+ mSecureSettings.putIntForUser(LOCK_SCREEN_SHOW_QR_CODE_SCANNER, 0,
UserHandle.USER_CURRENT);
- mSecureSettings.putStringForUser(LOCK_SCREEN_SHOW_QR_CODE_SCANNER, "0",
+ mSecureSettings.putIntForUser(LOCK_SCREEN_SHOW_QR_CODE_SCANNER, 0,
UserHandle.USER_CURRENT);
- mSecureSettings.putStringForUser(LOCK_SCREEN_SHOW_QR_CODE_SCANNER, "1",
+ mSecureSettings.putIntForUser(LOCK_SCREEN_SHOW_QR_CODE_SCANNER, 1,
UserHandle.USER_CURRENT);
- mSecureSettings.putStringForUser(LOCK_SCREEN_SHOW_QR_CODE_SCANNER, "1",
+ mSecureSettings.putIntForUser(LOCK_SCREEN_SHOW_QR_CODE_SCANNER, 1,
UserHandle.USER_CURRENT);
// Once from setup + twice from this function
verify(mCallback, times(3)).onQRCodeScannerPreferenceChanged();
@@ -297,14 +297,14 @@ public class QRCodeScannerControllerTest extends SysuiTestCase {
assertThat(mController.isEnabledForLockScreenButton()).isTrue();
assertThat(mController.isAbleToLaunchScannerActivity()).isTrue();
- mSecureSettings.putStringForUser(LOCK_SCREEN_SHOW_QR_CODE_SCANNER, "0",
+ mSecureSettings.putIntForUser(LOCK_SCREEN_SHOW_QR_CODE_SCANNER, 0,
UserHandle.USER_CURRENT);
verifyActivityDetails("abc/.def");
assertThat(mController.isEnabledForLockScreenButton()).isTrue();
assertThat(mController.isAllowedOnLockScreen()).isTrue();
assertThat(mController.isAbleToLaunchScannerActivity()).isTrue();
- mSecureSettings.putStringForUser(LOCK_SCREEN_SHOW_QR_CODE_SCANNER, "1",
+ mSecureSettings.putIntForUser(LOCK_SCREEN_SHOW_QR_CODE_SCANNER, 1,
UserHandle.USER_CURRENT);
verifyActivityDetails("abc/.def");
assertThat(mController.isEnabledForLockScreenButton()).isTrue();
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/domain/interactor/EditTilesListInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/domain/interactor/EditTilesListInteractorTest.kt
index 053a59aa533a..5f8adf0a3292 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/domain/interactor/EditTilesListInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/domain/interactor/EditTilesListInteractorTest.kt
@@ -32,12 +32,12 @@ import com.android.systemui.qs.pipeline.data.repository.FakeInstalledTilesCompon
import com.android.systemui.qs.pipeline.data.repository.fakeInstalledTilesRepository
import com.android.systemui.qs.pipeline.shared.TileSpec
import com.android.systemui.qs.shared.model.TileCategory
+import com.android.systemui.qs.tiles.base.shared.model.QSTileConfig
+import com.android.systemui.qs.tiles.base.shared.model.fakeQSTileConfigProvider
+import com.android.systemui.qs.tiles.base.shared.model.qSTileConfigProvider
import com.android.systemui.qs.tiles.impl.battery.qsBatterySaverTileConfig
import com.android.systemui.qs.tiles.impl.flashlight.qsFlashlightTileConfig
import com.android.systemui.qs.tiles.impl.internet.qsInternetTileConfig
-import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
-import com.android.systemui.qs.tiles.viewmodel.fakeQSTileConfigProvider
-import com.android.systemui.qs.tiles.viewmodel.qSTileConfigProvider
import com.android.systemui.settings.userTracker
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
@@ -58,12 +58,7 @@ class EditTilesListInteractorTest : SysuiTestCase() {
private val batteryTileConfig = kosmos.qsBatterySaverTileConfig
private val serviceInfo =
- FakeInstalledTilesComponentRepository.ServiceInfo(
- component,
- tileName,
- icon,
- appName,
- )
+ FakeInstalledTilesComponentRepository.ServiceInfo(component, tileName, icon, appName)
private val underTest =
with(kosmos) {
@@ -79,7 +74,7 @@ class EditTilesListInteractorTest : SysuiTestCase() {
with(kosmos) {
fakeInstalledTilesRepository.setInstalledServicesForUser(
userTracker.userId,
- listOf(serviceInfo)
+ listOf(serviceInfo),
)
with(fakeQSTileConfigProvider) {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/domain/interactor/NewTilesAvailabilityInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/domain/interactor/NewTilesAvailabilityInteractorTest.kt
index 9d2528dbc64f..530ca9590532 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/domain/interactor/NewTilesAvailabilityInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/domain/interactor/NewTilesAvailabilityInteractorTest.kt
@@ -22,7 +22,7 @@ import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.kosmos.testScope
import com.android.systemui.qs.pipeline.shared.TileSpec
-import com.android.systemui.qs.tiles.base.interactor.QSTileAvailabilityInteractor
+import com.android.systemui.qs.tiles.base.domain.interactor.QSTileAvailabilityInteractor
import com.android.systemui.statusbar.connectivity.ConnectivityModule.Companion.AIRPLANE_MODE_TILE_SPEC
import com.android.systemui.statusbar.connectivity.ConnectivityModule.Companion.HOTSPOT_TILE_SPEC
import com.android.systemui.statusbar.policy.PolicyModule.Companion.WORK_MODE_TILE_SPEC
@@ -38,61 +38,72 @@ import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
@SmallTest
class NewTilesAvailabilityInteractorTest : SysuiTestCase() {
- private val kosmos = testKosmos().apply {
- tileAvailabilityInteractorsMap = buildMap {
- put(AIRPLANE_MODE_TILE_SPEC, QSTileAvailabilityInteractor.AlwaysAvailableInteractor)
- put(WORK_MODE_TILE_SPEC, FakeTileAvailabilityInteractor(
- mapOf(
- fakeUserRepository.getSelectedUserInfo().id to flowOf(true),
- ).withDefault { flowOf(false) }
- ))
- put(HOTSPOT_TILE_SPEC, FakeTileAvailabilityInteractor(
- emptyMap<Int, Flow<Boolean>>().withDefault { flowOf(false) }
- ))
+ private val kosmos =
+ testKosmos().apply {
+ tileAvailabilityInteractorsMap = buildMap {
+ put(AIRPLANE_MODE_TILE_SPEC, QSTileAvailabilityInteractor.AlwaysAvailableInteractor)
+ put(
+ WORK_MODE_TILE_SPEC,
+ FakeTileAvailabilityInteractor(
+ mapOf(fakeUserRepository.getSelectedUserInfo().id to flowOf(true))
+ .withDefault { flowOf(false) }
+ ),
+ )
+ put(
+ HOTSPOT_TILE_SPEC,
+ FakeTileAvailabilityInteractor(
+ emptyMap<Int, Flow<Boolean>>().withDefault { flowOf(false) }
+ ),
+ )
+ }
}
- }
private val underTest by lazy { kosmos.newTilesAvailabilityInteractor }
@Test
- fun defaultUser_getAvailabilityFlow() = with(kosmos) {
- testScope.runTest {
- val availability by collectLastValue(underTest.newTilesAvailable)
+ fun defaultUser_getAvailabilityFlow() =
+ with(kosmos) {
+ testScope.runTest {
+ val availability by collectLastValue(underTest.newTilesAvailable)
- assertThat(availability).isEqualTo(
- mapOf(
+ assertThat(availability)
+ .isEqualTo(
+ mapOf(
TileSpec.create(AIRPLANE_MODE_TILE_SPEC) to true,
TileSpec.create(WORK_MODE_TILE_SPEC) to true,
TileSpec.create(HOTSPOT_TILE_SPEC) to false,
+ )
)
- )
+ }
}
- }
@Test
- fun getAvailabilityFlow_userChange() = with(kosmos) {
- testScope.runTest {
- val availability by collectLastValue(underTest.newTilesAvailable)
- fakeUserRepository.asMainUser()
+ fun getAvailabilityFlow_userChange() =
+ with(kosmos) {
+ testScope.runTest {
+ val availability by collectLastValue(underTest.newTilesAvailable)
+ fakeUserRepository.asMainUser()
- assertThat(availability).isEqualTo(
- mapOf(
+ assertThat(availability)
+ .isEqualTo(
+ mapOf(
TileSpec.create(AIRPLANE_MODE_TILE_SPEC) to true,
TileSpec.create(WORK_MODE_TILE_SPEC) to false,
TileSpec.create(HOTSPOT_TILE_SPEC) to false,
+ )
)
- )
+ }
}
- }
@Test
- fun noAvailabilityInteractor_emptyMap() = with(kosmos) {
- testScope.runTest {
- tileAvailabilityInteractorsMap = emptyMap()
+ fun noAvailabilityInteractor_emptyMap() =
+ with(kosmos) {
+ testScope.runTest {
+ tileAvailabilityInteractorsMap = emptyMap()
- val availability by collectLastValue(underTest.newTilesAvailable)
+ val availability by collectLastValue(underTest.newTilesAvailable)
- assertThat(availability).isEmpty()
+ assertThat(availability).isEmpty()
+ }
}
- }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/domain/interactor/TilesAvailabilityInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/domain/interactor/TilesAvailabilityInteractorTest.kt
index 67fb1003a6ce..7743c2c05574 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/domain/interactor/TilesAvailabilityInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/domain/interactor/TilesAvailabilityInteractorTest.kt
@@ -28,7 +28,7 @@ import com.android.systemui.qs.FakeQSFactory
import com.android.systemui.qs.FakeQSTile
import com.android.systemui.qs.pipeline.shared.TileSpec
import com.android.systemui.qs.qsTileFactory
-import com.android.systemui.qs.tiles.base.interactor.QSTileAvailabilityInteractor
+import com.android.systemui.qs.tiles.base.domain.interactor.QSTileAvailabilityInteractor
import com.android.systemui.statusbar.connectivity.ConnectivityModule.Companion.AIRPLANE_MODE_TILE_SPEC
import com.android.systemui.statusbar.connectivity.ConnectivityModule.Companion.HOTSPOT_TILE_SPEC
import com.android.systemui.statusbar.connectivity.ConnectivityModule.Companion.INTERNET_TILE_SPEC
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/compose/EditTileListStateTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/compose/EditTileListStateTest.kt
index 8b9ae9a0606d..2dd2f7c0f562 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/compose/EditTileListStateTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/compose/EditTileListStateTest.kt
@@ -23,6 +23,7 @@ import com.android.systemui.SysuiTestCase
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.qs.panels.shared.model.SizedTile
import com.android.systemui.qs.panels.shared.model.SizedTileImpl
+import com.android.systemui.qs.panels.ui.compose.selection.PlacementEvent
import com.android.systemui.qs.panels.ui.model.GridCell
import com.android.systemui.qs.panels.ui.model.TileGridCell
import com.android.systemui.qs.panels.ui.viewmodel.EditTileViewModel
@@ -108,6 +109,76 @@ class EditTileListStateTest : SysuiTestCase() {
assertThat(underTest.tiles.toStrings()).doesNotContain(TestEditTiles[0].tile.tileSpec.spec)
}
+ @Test
+ fun targetIndexForPlacementToTileSpec_returnsCorrectIndex() {
+ val placementEvent =
+ PlacementEvent.PlaceToTileSpec(
+ movingSpec = TestEditTiles[0].tile.tileSpec,
+ targetSpec = TestEditTiles[3].tile.tileSpec,
+ )
+ val index = underTest.targetIndexForPlacement(placementEvent)
+
+ assertThat(index).isEqualTo(3)
+ }
+
+ @Test
+ fun targetIndexForPlacementToIndex_indexOutOfBounds_returnsCorrectIndex() {
+ val placementEventTooLow =
+ PlacementEvent.PlaceToIndex(
+ movingSpec = TestEditTiles[0].tile.tileSpec,
+ targetIndex = -1,
+ )
+ val index1 = underTest.targetIndexForPlacement(placementEventTooLow)
+
+ assertThat(index1).isEqualTo(0)
+
+ val placementEventTooHigh =
+ PlacementEvent.PlaceToIndex(
+ movingSpec = TestEditTiles[0].tile.tileSpec,
+ targetIndex = 10,
+ )
+ val index2 = underTest.targetIndexForPlacement(placementEventTooHigh)
+ assertThat(index2).isEqualTo(TestEditTiles.size)
+ }
+
+ @Test
+ fun targetIndexForPlacementToIndex_movingBack_returnsCorrectIndex() {
+ /**
+ * With the grid: [ a ] [ b ] [ c ] [ Large D ] [ e ] [ f ]
+ *
+ * Moving 'e' to the spacer at index 3 will result in the tilespec order: a, b, c, e, d, f
+ *
+ * 'e' is now at index 3
+ */
+ val placementEvent =
+ PlacementEvent.PlaceToIndex(
+ movingSpec = TestEditTiles[4].tile.tileSpec,
+ targetIndex = 3,
+ )
+ val index = underTest.targetIndexForPlacement(placementEvent)
+
+ assertThat(index).isEqualTo(3)
+ }
+
+ @Test
+ fun targetIndexForPlacementToIndex_movingForward_returnsCorrectIndex() {
+ /**
+ * With the grid: [ a ] [ b ] [ c ] [ Large D ] [ e ] [ f ]
+ *
+ * Moving '1' to the spacer at index 3 will result in the tilespec order: b, c, a, d, e, f
+ *
+ * 'a' is now at index 2
+ */
+ val placementEvent =
+ PlacementEvent.PlaceToIndex(
+ movingSpec = TestEditTiles[0].tile.tileSpec,
+ targetIndex = 3,
+ )
+ val index = underTest.targetIndexForPlacement(placementEvent)
+
+ assertThat(index).isEqualTo(2)
+ }
+
private fun List<GridCell>.toStrings(): List<String> {
return map {
if (it is TileGridCell) {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/compose/selection/MutableSelectionStateTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/compose/selection/MutableSelectionStateTest.kt
index ab217a3f50ef..33ee3379c0d6 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/compose/selection/MutableSelectionStateTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/compose/selection/MutableSelectionStateTest.kt
@@ -19,8 +19,14 @@ package com.android.systemui.qs.panels.ui.compose.selection
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
+import com.android.systemui.qs.panels.ui.compose.selection.TileState.GreyedOut
+import com.android.systemui.qs.panels.ui.compose.selection.TileState.None
+import com.android.systemui.qs.panels.ui.compose.selection.TileState.Placeable
+import com.android.systemui.qs.panels.ui.compose.selection.TileState.Removable
+import com.android.systemui.qs.panels.ui.compose.selection.TileState.Selected
import com.android.systemui.qs.pipeline.shared.TileSpec
import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.test.runTest
import org.junit.Test
import org.junit.runner.RunWith
@@ -45,7 +51,104 @@ class MutableSelectionStateTest : SysuiTestCase() {
assertThat(underTest.selection).isEqualTo(newSpec)
}
+ @Test
+ fun placementModeEnabled_tapOnIndex_sendsCorrectPlacementEvent() {
+ // Tap while in placement mode
+ underTest.enterPlacementMode(TEST_SPEC)
+ underTest.onTap(2)
+
+ assertThat(underTest.placementEnabled).isFalse()
+ val event = underTest.placementEvent as PlacementEvent.PlaceToIndex
+ assertThat(event.movingSpec).isEqualTo(TEST_SPEC)
+ assertThat(event.targetIndex).isEqualTo(2)
+ }
+
+ @Test
+ fun placementModeDisabled_tapOnIndex_doesNotSendPlacementEvent() {
+ // Tap while placement mode is disabled
+ underTest.onTap(2)
+
+ assertThat(underTest.placementEnabled).isFalse()
+ assertThat(underTest.placementEvent).isNull()
+ }
+
+ @Test
+ fun placementModeEnabled_tapOnSelection_exitPlacementMode() {
+ // Tap while in placement mode
+ underTest.enterPlacementMode(TEST_SPEC)
+ underTest.onTap(TEST_SPEC)
+
+ assertThat(underTest.placementEnabled).isFalse()
+ assertThat(underTest.placementEvent).isNull()
+ }
+
+ @Test
+ fun placementModeEnabled_tapOnTileSpec_sendsCorrectPlacementEvent() {
+ // Tap while in placement mode
+ underTest.enterPlacementMode(TEST_SPEC)
+ underTest.onTap(TEST_SPEC_2)
+
+ assertThat(underTest.placementEnabled).isFalse()
+ val event = underTest.placementEvent as PlacementEvent.PlaceToTileSpec
+ assertThat(event.movingSpec).isEqualTo(TEST_SPEC)
+ assertThat(event.targetSpec).isEqualTo(TEST_SPEC_2)
+ }
+
+ @Test
+ fun placementModeDisabled_tapOnSelection_unselect() {
+ // Select the tile and tap on it
+ underTest.select(TEST_SPEC)
+ underTest.onTap(TEST_SPEC)
+
+ assertThat(underTest.placementEnabled).isFalse()
+ assertThat(underTest.selected).isFalse()
+ }
+
+ @Test
+ fun placementModeDisabled_tapOnTile_selects() {
+ // Select a tile but tap a second one
+ underTest.select(TEST_SPEC)
+ underTest.onTap(TEST_SPEC_2)
+
+ assertThat(underTest.placementEnabled).isFalse()
+ assertThat(underTest.selection).isEqualTo(TEST_SPEC_2)
+ }
+
+ @Test
+ fun tileStateFor_selectedTile_returnsSingleSelection() = runTest {
+ underTest.select(TEST_SPEC)
+
+ assertThat(underTest.tileStateFor(TEST_SPEC, None, canShowRemovalBadge = true))
+ .isEqualTo(Selected)
+ assertThat(underTest.tileStateFor(TEST_SPEC_2, None, canShowRemovalBadge = true))
+ .isEqualTo(Removable)
+ assertThat(underTest.tileStateFor(TEST_SPEC_3, None, canShowRemovalBadge = true))
+ .isEqualTo(Removable)
+ }
+
+ @Test
+ fun tileStateFor_placementMode_returnsSinglePlaceable() = runTest {
+ underTest.enterPlacementMode(TEST_SPEC)
+
+ assertThat(underTest.tileStateFor(TEST_SPEC, None, canShowRemovalBadge = true))
+ .isEqualTo(Placeable)
+ assertThat(underTest.tileStateFor(TEST_SPEC_2, None, canShowRemovalBadge = true))
+ .isEqualTo(GreyedOut)
+ assertThat(underTest.tileStateFor(TEST_SPEC_3, None, canShowRemovalBadge = true))
+ .isEqualTo(GreyedOut)
+ }
+
+ @Test
+ fun tileStateFor_nonRemovableTile_returnsNoneState() = runTest {
+ assertThat(underTest.tileStateFor(TEST_SPEC, None, canShowRemovalBadge = true))
+ .isEqualTo(Removable)
+ assertThat(underTest.tileStateFor(TEST_SPEC_2, None, canShowRemovalBadge = false))
+ .isEqualTo(None)
+ }
+
companion object {
private val TEST_SPEC = TileSpec.create("testSpec")
+ private val TEST_SPEC_2 = TileSpec.create("testSpec2")
+ private val TEST_SPEC_3 = TileSpec.create("testSpec3")
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/viewmodel/DetailsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/viewmodel/DetailsViewModelTest.kt
index 1d42424bc6ed..8d50cdc001ca 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/viewmodel/DetailsViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/viewmodel/DetailsViewModelTest.kt
@@ -19,13 +19,17 @@ package com.android.systemui.qs.panels.ui.viewmodel
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
+import com.android.systemui.flags.EnableSceneContainer
import com.android.systemui.kosmos.testScope
import com.android.systemui.qs.FakeQSTile
import com.android.systemui.qs.pipeline.data.repository.tileSpecRepository
import com.android.systemui.qs.pipeline.domain.interactor.currentTilesInteractor
import com.android.systemui.qs.pipeline.shared.TileSpec
+import com.android.systemui.shade.domain.interactor.disableDualShade
+import com.android.systemui.shade.domain.interactor.enableDualShade
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Before
@@ -34,6 +38,7 @@ import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
@SmallTest
+@EnableSceneContainer
class DetailsViewModelTest : SysuiTestCase() {
private val kosmos = testKosmos()
private lateinit var underTest: DetailsViewModel
@@ -45,10 +50,12 @@ class DetailsViewModelTest : SysuiTestCase() {
underTest = kosmos.detailsViewModel
}
+ @OptIn(ExperimentalCoroutinesApi::class)
@Test
- fun changeTileDetailsViewModel() =
+ fun changeTileDetailsViewModelWithDualShadeEnabled() =
with(kosmos) {
testScope.runTest {
+ kosmos.enableDualShade()
val specs = listOf(spec, specNoDetails)
tileSpecRepository.setTiles(0, specs)
runCurrent()
@@ -85,4 +92,36 @@ class DetailsViewModelTest : SysuiTestCase() {
assertThat(underTest.onTileClicked(null)).isFalse()
}
}
+
+ @OptIn(ExperimentalCoroutinesApi::class)
+ @Test
+ fun ignoreChangingTileDetailsViewModelWithDualShadeDisabled() =
+ with(kosmos) {
+ testScope.runTest {
+ kosmos.disableDualShade()
+ val specs = listOf(spec, specNoDetails)
+ tileSpecRepository.setTiles(0, specs)
+ runCurrent()
+
+ val tiles = currentTilesInteractor.currentTiles.value
+
+ assertThat(currentTilesInteractor.currentTilesSpecs.size).isEqualTo(2)
+ assertThat(tiles[1].spec).isEqualTo(specNoDetails)
+ (tiles[1].tile as FakeQSTile).hasDetailsViewModel = false
+
+ assertThat(underTest.activeTileDetails).isNull()
+
+ // Click on the tile who has the `spec`.
+ assertThat(underTest.onTileClicked(spec)).isFalse()
+ assertThat(underTest.activeTileDetails).isNull()
+
+ // Click on a tile who dose not have a valid spec.
+ assertThat(underTest.onTileClicked(null)).isFalse()
+ assertThat(underTest.activeTileDetails).isNull()
+
+ // Click on a tile who dose not have a detailed view.
+ assertThat(underTest.onTileClicked(specNoDetails)).isFalse()
+ assertThat(underTest.activeTileDetails).isNull()
+ }
+ }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeViewModelTest.kt
index 50229eb4348d..71ab0a2e4265 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeViewModelTest.kt
@@ -49,6 +49,9 @@ import com.android.systemui.qs.pipeline.shared.TileSpec
import com.android.systemui.qs.pipeline.shared.metricSpec
import com.android.systemui.qs.qsTileFactory
import com.android.systemui.qs.shared.model.TileCategory
+import com.android.systemui.qs.tiles.base.shared.model.QSTileConfig
+import com.android.systemui.qs.tiles.base.shared.model.fakeQSTileConfigProvider
+import com.android.systemui.qs.tiles.base.shared.model.qSTileConfigProvider
import com.android.systemui.qs.tiles.impl.airplane.qsAirplaneModeTileConfig
import com.android.systemui.qs.tiles.impl.alarm.qsAlarmTileConfig
import com.android.systemui.qs.tiles.impl.battery.qsBatterySaverTileConfig
@@ -56,9 +59,6 @@ import com.android.systemui.qs.tiles.impl.flashlight.qsFlashlightTileConfig
import com.android.systemui.qs.tiles.impl.internet.qsInternetTileConfig
import com.android.systemui.qs.tiles.impl.sensorprivacy.qsCameraSensorPrivacyToggleTileConfig
import com.android.systemui.qs.tiles.impl.sensorprivacy.qsMicrophoneSensorPrivacyToggleTileConfig
-import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
-import com.android.systemui.qs.tiles.viewmodel.fakeQSTileConfigProvider
-import com.android.systemui.qs.tiles.viewmodel.qSTileConfigProvider
import com.android.systemui.settings.userTracker
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractorImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractorImplTest.kt
index 5bde7ad27b7a..12f1e66c71e5 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractorImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractorImplTest.kt
@@ -49,7 +49,7 @@ import com.android.systemui.qs.pipeline.shared.TileSpec
import com.android.systemui.qs.pipeline.shared.logging.QSPipelineLogger
import com.android.systemui.qs.pipeline.shared.logging.qsLogger
import com.android.systemui.qs.qsTileFactory
-import com.android.systemui.qs.tiles.di.newQSTileFactory
+import com.android.systemui.qs.tiles.base.ui.model.newQSTileFactory
import com.android.systemui.qs.toProto
import com.android.systemui.settings.fakeUserTracker
import com.android.systemui.testKosmos
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/CastTileTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/CastTileTest.java
index 765c5749cd4b..d880aa604849 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/CastTileTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/CastTileTest.java
@@ -50,6 +50,7 @@ import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.QSHost;
import com.android.systemui.qs.QsEventLogger;
import com.android.systemui.qs.logging.QSLogger;
+import com.android.systemui.qs.tiles.dialog.CastDetailsViewModel;
import com.android.systemui.shade.domain.interactor.FakeShadeDialogContextInteractor;
import com.android.systemui.shade.domain.interactor.ShadeDialogContextInteractor;
import com.android.systemui.statusbar.connectivity.IconState;
@@ -63,6 +64,7 @@ import com.android.systemui.statusbar.policy.HotspotController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import org.junit.After;
+import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -104,6 +106,8 @@ public class CastTileTest extends SysuiTestCase {
private DialogTransitionAnimator mDialogTransitionAnimator;
@Mock
private QsEventLogger mUiEventLogger;
+ @Mock
+ private CastDetailsViewModel.Factory mCastDetailsViewModelFactory;
private final TileJavaAdapter mJavaAdapter = new TileJavaAdapter();
private final FakeConnectivityRepository mConnectivityRepository =
@@ -517,6 +521,29 @@ public class CastTileTest extends SysuiTestCase {
assertTrue(mCastTile.getState().forceExpandIcon);
}
+ @Test
+ public void testDetailsViewUnavailableState_returnsNull() {
+ createAndStartTileNewImpl();
+ mTestableLooper.processAllMessages();
+
+ assertEquals(Tile.STATE_UNAVAILABLE, mCastTile.getState().state);
+ mCastTile.getDetailsViewModel(Assert::assertNull);
+ }
+
+ @Test
+ public void testDetailsViewAvailableState_returnsNotNull() {
+ createAndStartTileNewImpl();
+ CastDevice device = createConnectedCastDevice();
+ List<CastDevice> devices = new ArrayList<>();
+ devices.add(device);
+ when(mController.getCastDevices()).thenReturn(devices);
+ mConnectivityRepository.setWifiConnected(true);
+ mTestableLooper.processAllMessages();
+
+ assertEquals(Tile.STATE_ACTIVE, mCastTile.getState().state);
+ mCastTile.getDetailsViewModel(Assert::assertNotNull);
+ }
+
/**
* For simplicity, let this method still set the field even though that's kind of gross
*/
@@ -540,7 +567,8 @@ public class CastTileTest extends SysuiTestCase {
mConnectivityRepository,
mJavaAdapter,
mFeatureFlags,
- mShadeDialogContextInteractor
+ mShadeDialogContextInteractor,
+ mCastDetailsViewModelFactory
);
mCastTile.initialize();
@@ -584,7 +612,8 @@ public class CastTileTest extends SysuiTestCase {
mConnectivityRepository,
mJavaAdapter,
mFeatureFlags,
- mShadeDialogContextInteractor
+ mShadeDialogContextInteractor,
+ mCastDetailsViewModelFactory
);
mCastTile.initialize();
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/ModesDndTileTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/ModesDndTileTest.kt
index 4f1b4e56ca30..6e5a700dde7f 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/ModesDndTileTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/ModesDndTileTest.kt
@@ -38,14 +38,14 @@ import com.android.systemui.qs.QSHost
import com.android.systemui.qs.QsEventLogger
import com.android.systemui.qs.logging.QSLogger
import com.android.systemui.qs.shared.QSSettingsPackageRepository
-import com.android.systemui.qs.tiles.base.actions.FakeQSTileIntentUserInputHandler
+import com.android.systemui.qs.tiles.base.domain.actions.FakeQSTileIntentUserInputHandler
+import com.android.systemui.qs.tiles.base.shared.model.QSTileConfigProvider
+import com.android.systemui.qs.tiles.base.shared.model.QSTileConfigTestBuilder
+import com.android.systemui.qs.tiles.base.shared.model.QSTileUIConfig
import com.android.systemui.qs.tiles.impl.modes.domain.interactor.ModesDndTileDataInteractor
import com.android.systemui.qs.tiles.impl.modes.domain.interactor.ModesDndTileUserActionInteractor
import com.android.systemui.qs.tiles.impl.modes.domain.model.ModesDndTileModel
import com.android.systemui.qs.tiles.impl.modes.ui.ModesDndTileMapper
-import com.android.systemui.qs.tiles.viewmodel.QSTileConfigProvider
-import com.android.systemui.qs.tiles.viewmodel.QSTileConfigTestBuilder
-import com.android.systemui.qs.tiles.viewmodel.QSTileUIConfig
import com.android.systemui.res.R
import com.android.systemui.statusbar.policy.data.repository.zenModeRepository
import com.android.systemui.statusbar.policy.domain.interactor.zenModeInteractor
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/ModesTileTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/ModesTileTest.kt
index bbc0dbcad355..c218a1b4ceb9 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/ModesTileTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/ModesTileTest.kt
@@ -37,14 +37,14 @@ import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.qs.QSHost
import com.android.systemui.qs.QsEventLogger
import com.android.systemui.qs.logging.QSLogger
-import com.android.systemui.qs.tiles.base.actions.FakeQSTileIntentUserInputHandler
+import com.android.systemui.qs.tiles.base.domain.actions.FakeQSTileIntentUserInputHandler
+import com.android.systemui.qs.tiles.base.shared.model.QSTileConfigProvider
+import com.android.systemui.qs.tiles.base.shared.model.QSTileConfigTestBuilder
+import com.android.systemui.qs.tiles.base.shared.model.QSTileUIConfig
import com.android.systemui.qs.tiles.impl.modes.domain.interactor.ModesTileDataInteractor
import com.android.systemui.qs.tiles.impl.modes.domain.interactor.ModesTileUserActionInteractor
import com.android.systemui.qs.tiles.impl.modes.domain.model.ModesTileModel
-import com.android.systemui.qs.tiles.impl.modes.ui.ModesTileMapper
-import com.android.systemui.qs.tiles.viewmodel.QSTileConfigProvider
-import com.android.systemui.qs.tiles.viewmodel.QSTileConfigTestBuilder
-import com.android.systemui.qs.tiles.viewmodel.QSTileUIConfig
+import com.android.systemui.qs.tiles.impl.modes.ui.mapper.ModesTileMapper
import com.android.systemui.res.R
import com.android.systemui.statusbar.policy.data.repository.zenModeRepository
import com.android.systemui.statusbar.policy.domain.interactor.zenModeInteractor
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserInputHandlerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/domain/actions/QSTileIntentUserInputHandlerTest.kt
index 02a81419ea78..94449637ebe7 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserInputHandlerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/domain/actions/QSTileIntentUserInputHandlerTest.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2025 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.qs.tiles.base.actions
+package com.android.systemui.qs.tiles.base.domain.actions
import android.app.PendingIntent
import android.content.ComponentName
@@ -119,7 +119,7 @@ class QSTileIntentUserInputHandlerTest : SysuiTestCase() {
.postStartActivityDismissingKeyguard(
argThat(IntentMatcher(expectedIntent)),
eq(0),
- any()
+ any(),
)
}
@@ -149,7 +149,7 @@ class QSTileIntentUserInputHandlerTest : SysuiTestCase() {
packageManager.queryIntentActivitiesAsUser(
any(Intent::class.java),
any(ResolveInfoFlags::class.java),
- eq(user.identifier)
+ eq(user.identifier),
)
)
.thenReturn(infos.map { ResolveInfo().apply { activityInfo = it } })
@@ -157,6 +157,7 @@ class QSTileIntentUserInputHandlerTest : SysuiTestCase() {
private class IntentMatcher(intent: Intent) : ArgumentMatcher<Intent> {
private val expectedIntent = intent
+
override fun matches(argument: Intent?): Boolean {
return argument?.action.equals(expectedIntent.action) &&
argument?.`package`.equals(expectedIntent.`package`) &&
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/interactor/DisabledByPolicyInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/domain/interactor/DisabledByPolicyInteractorTest.kt
index c0e5a9bc9990..5c2edc228d7e 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/interactor/DisabledByPolicyInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/domain/interactor/DisabledByPolicyInteractorTest.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2025 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.qs.tiles.base.interactor
+package com.android.systemui.qs.tiles.base.domain.interactor
import android.content.ComponentName
import android.content.Context
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/logging/QSTileLoggerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/shared/logging/QSTileLoggerTest.kt
index 6a33b5f58820..ec88f710c6ac 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/logging/QSTileLoggerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/shared/logging/QSTileLoggerTest.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2025 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.qs.tiles.base.logging
+package com.android.systemui.qs.tiles.base.shared.logging
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
@@ -26,8 +26,8 @@ import com.android.systemui.log.LogBufferFactory
import com.android.systemui.log.LogcatEchoTrackerAlways
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.qs.pipeline.shared.TileSpec
-import com.android.systemui.qs.tiles.viewmodel.QSTileState
-import com.android.systemui.qs.tiles.viewmodel.QSTileUserAction
+import com.android.systemui.qs.tiles.base.shared.model.QSTileState
+import com.android.systemui.qs.tiles.base.shared.model.QSTileUserAction
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfigProviderTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/shared/model/QSTileConfigProviderTest.kt
index 40971a87480d..fd83895ed790 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfigProviderTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/shared/model/QSTileConfigProviderTest.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2025 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.qs.tiles.viewmodel
+package com.android.systemui.qs.tiles.base.shared.model
import android.platform.test.annotations.EnabledOnRavenwood
import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -34,7 +34,7 @@ class QSTileConfigProviderTest : SysuiTestCase() {
private val underTest =
createQSTileConfigProviderImpl(
- mapOf(VALID_SPEC.spec to QSTileConfigTestBuilder.build { tileSpec = VALID_SPEC }),
+ mapOf(VALID_SPEC.spec to QSTileConfigTestBuilder.build { tileSpec = VALID_SPEC })
)
@Test
@@ -66,11 +66,7 @@ class QSTileConfigProviderTest : SysuiTestCase() {
private fun createQSTileConfigProviderImpl(
configs: Map<String, QSTileConfig>
- ): QSTileConfigProviderImpl =
- QSTileConfigProviderImpl(
- configs,
- mock<QsEventLogger>(),
- )
+ ): QSTileConfigProviderImpl = QSTileConfigProviderImpl(configs, mock<QsEventLogger>())
private companion object {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/analytics/QSTileAnalyticsTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/ui/analytics/QSTileAnalyticsTest.kt
index fd09e3ca4bb8..ee0f5cd56acf 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/analytics/QSTileAnalyticsTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/ui/analytics/QSTileAnalyticsTest.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2025 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.qs.tiles.base.analytics
+package com.android.systemui.qs.tiles.base.ui.analytics
import android.platform.test.annotations.EnabledOnRavenwood
import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -23,8 +23,8 @@ import com.android.internal.logging.InstanceId
import com.android.internal.logging.UiEventLogger
import com.android.systemui.SysuiTestCase
import com.android.systemui.qs.QSEvent
-import com.android.systemui.qs.tiles.viewmodel.QSTileConfigTestBuilder
-import com.android.systemui.qs.tiles.viewmodel.QSTileUserAction
+import com.android.systemui.qs.tiles.base.shared.model.QSTileConfigTestBuilder
+import com.android.systemui.qs.tiles.base.shared.model.QSTileUserAction
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -57,7 +57,7 @@ class QSTileAnalyticsTest : SysuiTestCase() {
eq(QSEvent.QS_ACTION_CLICK),
eq(0),
eq("test_spec"),
- eq(InstanceId.fakeInstanceId(0))
+ eq(InstanceId.fakeInstanceId(0)),
)
}
@@ -70,7 +70,7 @@ class QSTileAnalyticsTest : SysuiTestCase() {
eq(QSEvent.QS_ACTION_LONG_PRESS),
eq(0),
eq("test_spec"),
- eq(InstanceId.fakeInstanceId(0))
+ eq(InstanceId.fakeInstanceId(0)),
)
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/ui/viewmodel/QSTileViewModelImplTest.kt
index da3cebd24e6b..4ebd22abe60d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/ui/viewmodel/QSTileViewModelImplTest.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.qs.tiles.base.viewmodel
+package com.android.systemui.qs.tiles.base.ui.viewmodel
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
@@ -23,16 +23,16 @@ import com.android.systemui.classifier.FalsingManagerFake
import com.android.systemui.common.shared.model.ContentDescription
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.qs.FakeTileDetailsViewModel
-import com.android.systemui.qs.tiles.base.analytics.QSTileAnalytics
-import com.android.systemui.qs.tiles.base.interactor.FakeDisabledByPolicyInteractor
-import com.android.systemui.qs.tiles.base.interactor.FakeQSTileDataInteractor
-import com.android.systemui.qs.tiles.base.interactor.FakeQSTileUserActionInteractor
-import com.android.systemui.qs.tiles.base.interactor.QSTileDataToStateMapper
-import com.android.systemui.qs.tiles.base.logging.QSTileLogger
-import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
-import com.android.systemui.qs.tiles.viewmodel.QSTileConfigTestBuilder
-import com.android.systemui.qs.tiles.viewmodel.QSTilePolicy
-import com.android.systemui.qs.tiles.viewmodel.QSTileState
+import com.android.systemui.qs.tiles.base.domain.interactor.FakeDisabledByPolicyInteractor
+import com.android.systemui.qs.tiles.base.domain.interactor.FakeQSTileDataInteractor
+import com.android.systemui.qs.tiles.base.domain.interactor.FakeQSTileUserActionInteractor
+import com.android.systemui.qs.tiles.base.shared.logging.QSTileLogger
+import com.android.systemui.qs.tiles.base.shared.model.QSTileConfig
+import com.android.systemui.qs.tiles.base.shared.model.QSTileConfigTestBuilder
+import com.android.systemui.qs.tiles.base.shared.model.QSTilePolicy
+import com.android.systemui.qs.tiles.base.shared.model.QSTileState
+import com.android.systemui.qs.tiles.base.ui.analytics.QSTileAnalytics
+import com.android.systemui.qs.tiles.base.ui.model.QSTileDataToStateMapper
import com.android.systemui.user.data.repository.FakeUserRepository
import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/ui/viewmodel/QSTileViewModelTest.kt
index 4e9b63517d6d..d2f8b07b31b8 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/ui/viewmodel/QSTileViewModelTest.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2025 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.qs.tiles.viewmodel
+package com.android.systemui.qs.tiles.base.ui.viewmodel
import android.os.UserHandle
import android.platform.test.annotations.EnabledOnRavenwood
@@ -26,14 +26,17 @@ import com.android.systemui.common.shared.model.ContentDescription
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.coroutines.collectValues
import com.android.systemui.qs.FakeTileDetailsViewModel
-import com.android.systemui.qs.tiles.base.analytics.QSTileAnalytics
-import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
-import com.android.systemui.qs.tiles.base.interactor.FakeDisabledByPolicyInteractor
-import com.android.systemui.qs.tiles.base.interactor.FakeQSTileDataInteractor
-import com.android.systemui.qs.tiles.base.interactor.FakeQSTileUserActionInteractor
-import com.android.systemui.qs.tiles.base.interactor.QSTileDataToStateMapper
-import com.android.systemui.qs.tiles.base.logging.QSTileLogger
-import com.android.systemui.qs.tiles.base.viewmodel.QSTileViewModelImpl
+import com.android.systemui.qs.tiles.base.domain.interactor.FakeDisabledByPolicyInteractor
+import com.android.systemui.qs.tiles.base.domain.interactor.FakeQSTileDataInteractor
+import com.android.systemui.qs.tiles.base.domain.interactor.FakeQSTileUserActionInteractor
+import com.android.systemui.qs.tiles.base.domain.model.DataUpdateTrigger
+import com.android.systemui.qs.tiles.base.shared.logging.QSTileLogger
+import com.android.systemui.qs.tiles.base.shared.model.QSTileConfig
+import com.android.systemui.qs.tiles.base.shared.model.QSTileConfigTestBuilder
+import com.android.systemui.qs.tiles.base.shared.model.QSTilePolicy
+import com.android.systemui.qs.tiles.base.shared.model.QSTileState
+import com.android.systemui.qs.tiles.base.ui.analytics.QSTileAnalytics
+import com.android.systemui.qs.tiles.base.ui.model.QSTileDataToStateMapper
import com.android.systemui.user.data.repository.FakeUserRepository
import com.android.systemui.util.mockito.eq
import com.android.systemui.util.time.FakeSystemClock
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelUserInputTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/ui/viewmodel/QSTileViewModelUserInputTest.kt
index 166e9500cff9..0f939a95261b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelUserInputTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/base/ui/viewmodel/QSTileViewModelUserInputTest.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2025 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.qs.tiles.viewmodel
+package com.android.systemui.qs.tiles.base.ui.viewmodel
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.MediumTest
@@ -23,16 +23,21 @@ import com.android.systemui.classifier.FalsingManagerFake
import com.android.systemui.common.shared.model.ContentDescription
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.qs.FakeTileDetailsViewModel
-import com.android.systemui.qs.tiles.base.analytics.QSTileAnalytics
-import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
-import com.android.systemui.qs.tiles.base.interactor.FakeDisabledByPolicyInteractor
-import com.android.systemui.qs.tiles.base.interactor.FakeDisabledByPolicyInteractor.Companion.DISABLED_RESTRICTION
-import com.android.systemui.qs.tiles.base.interactor.FakeDisabledByPolicyInteractor.Companion.ENABLED_RESTRICTION
-import com.android.systemui.qs.tiles.base.interactor.FakeQSTileDataInteractor
-import com.android.systemui.qs.tiles.base.interactor.FakeQSTileUserActionInteractor
-import com.android.systemui.qs.tiles.base.interactor.QSTileDataToStateMapper
-import com.android.systemui.qs.tiles.base.logging.QSTileLogger
-import com.android.systemui.qs.tiles.base.viewmodel.QSTileViewModelImpl
+import com.android.systemui.qs.tiles.base.domain.interactor.FakeDisabledByPolicyInteractor
+import com.android.systemui.qs.tiles.base.domain.interactor.FakeDisabledByPolicyInteractor.Companion.DISABLED_RESTRICTION
+import com.android.systemui.qs.tiles.base.domain.interactor.FakeDisabledByPolicyInteractor.Companion.DISABLED_RESTRICTION_2
+import com.android.systemui.qs.tiles.base.domain.interactor.FakeDisabledByPolicyInteractor.Companion.ENABLED_RESTRICTION
+import com.android.systemui.qs.tiles.base.domain.interactor.FakeQSTileDataInteractor
+import com.android.systemui.qs.tiles.base.domain.interactor.FakeQSTileUserActionInteractor
+import com.android.systemui.qs.tiles.base.domain.model.DataUpdateTrigger
+import com.android.systemui.qs.tiles.base.shared.logging.QSTileLogger
+import com.android.systemui.qs.tiles.base.shared.model.QSTileConfig
+import com.android.systemui.qs.tiles.base.shared.model.QSTileConfigTestBuilder
+import com.android.systemui.qs.tiles.base.shared.model.QSTilePolicy
+import com.android.systemui.qs.tiles.base.shared.model.QSTileState
+import com.android.systemui.qs.tiles.base.shared.model.QSTileUserAction
+import com.android.systemui.qs.tiles.base.ui.analytics.QSTileAnalytics
+import com.android.systemui.qs.tiles.base.ui.model.QSTileDataToStateMapper
import com.android.systemui.user.data.repository.FakeUserRepository
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.eq
@@ -171,10 +176,7 @@ class QSTileViewModelUserInputTest : SysuiTestCase() {
QSTileConfigTestBuilder.build {
policy =
QSTilePolicy.Restricted(
- listOf(
- DISABLED_RESTRICTION,
- FakeDisabledByPolicyInteractor.DISABLED_RESTRICTION_2,
- )
+ listOf(DISABLED_RESTRICTION, DISABLED_RESTRICTION_2)
)
}
@@ -199,7 +201,7 @@ class QSTileViewModelUserInputTest : SysuiTestCase() {
.logUserActionRejectedByPolicy(
eq(userAction),
eq(tileConfig.tileSpec),
- eq(FakeDisabledByPolicyInteractor.DISABLED_RESTRICTION_2),
+ eq(DISABLED_RESTRICTION_2),
)
verify(qsTileAnalytics, never()).trackUserAction(any(), any())
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/dialog/CastDetailsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/dialog/CastDetailsViewModelTest.kt
new file mode 100644
index 000000000000..468c3dc3be93
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/dialog/CastDetailsViewModelTest.kt
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2025 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.qs.tiles.dialog
+
+import android.content.Context
+import android.media.MediaRouter
+import android.provider.Settings
+import android.testing.TestableLooper.RunWithLooper
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.internal.app.MediaRouteDialogPresenter
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.qs.tiles.base.domain.actions.FakeQSTileIntentUserInputHandler
+import com.android.systemui.qs.tiles.base.domain.actions.intentInputs
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.doReturn
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.stub
+
+@SmallTest
+@RunWithLooper(setAsMainLooper = true)
+@RunWith(AndroidJUnit4::class)
+class CastDetailsViewModelTest : SysuiTestCase() {
+ var inputHandler: FakeQSTileIntentUserInputHandler = FakeQSTileIntentUserInputHandler()
+ private var context: Context = mock()
+ private var mediaRouter: MediaRouter = mock()
+ private var selectedRoute: MediaRouter.RouteInfo = mock()
+
+ @Test
+ fun testClickOnSettingsButton() {
+ var viewModel = CastDetailsViewModel(inputHandler, context, MediaRouter.ROUTE_TYPE_REMOTE_DISPLAY)
+
+ viewModel.clickOnSettingsButton()
+
+ assertThat(inputHandler.handledInputs).hasSize(1)
+ val intentInput = inputHandler.intentInputs.last()
+ assertThat(intentInput.expandable).isNull()
+ assertThat(intentInput.intent.action).isEqualTo(Settings.ACTION_CAST_SETTINGS)
+ }
+
+ @Test
+ fun testShouldShowChooserDialog() {
+ context.stub {
+ on { getSystemService(MediaRouter::class.java) } doReturn mediaRouter
+ }
+ mediaRouter.stub {
+ on { selectedRoute } doReturn selectedRoute
+ }
+
+ var viewModel =
+ CastDetailsViewModel(inputHandler, context, MediaRouter.ROUTE_TYPE_REMOTE_DISPLAY)
+
+ assertThat(viewModel.shouldShowChooserDialog())
+ .isEqualTo(
+ MediaRouteDialogPresenter.shouldShowChooserDialog(
+ context,
+ MediaRouter.ROUTE_TYPE_REMOTE_DISPLAY,
+ )
+ )
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/airplane/domain/interactor/AirplaneModeTileDataInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/airplane/domain/interactor/AirplaneModeTileDataInteractorTest.kt
index fda75ca76cec..78756d6769d1 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/airplane/domain/interactor/AirplaneModeTileDataInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/airplane/domain/interactor/AirplaneModeTileDataInteractorTest.kt
@@ -22,7 +22,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectValues
-import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
+import com.android.systemui.qs.tiles.base.domain.model.DataUpdateTrigger
import com.android.systemui.qs.tiles.impl.airplane.domain.model.AirplaneModeTileModel
import com.android.systemui.statusbar.pipeline.airplane.data.repository.FakeAirplaneModeRepository
import com.google.common.truth.Truth.assertThat
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/airplane/domain/interactor/AirplaneModeTileUserActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/airplane/domain/interactor/AirplaneModeTileUserActionInteractorTest.kt
index d27e81039602..ce6bcdfe6b36 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/airplane/domain/interactor/AirplaneModeTileUserActionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/airplane/domain/interactor/AirplaneModeTileUserActionInteractorTest.kt
@@ -22,10 +22,10 @@ import android.telephony.TelephonyManager
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
-import com.android.systemui.qs.tiles.base.actions.FakeQSTileIntentUserInputHandler
-import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandlerSubject.Companion.assertThat
-import com.android.systemui.qs.tiles.base.interactor.QSTileInputTestKtx.click
-import com.android.systemui.qs.tiles.base.interactor.QSTileInputTestKtx.longClick
+import com.android.systemui.qs.tiles.base.domain.actions.FakeQSTileIntentUserInputHandler
+import com.android.systemui.qs.tiles.base.domain.actions.QSTileIntentUserInputHandlerSubject.Companion.assertThat
+import com.android.systemui.qs.tiles.base.domain.model.QSTileInputTestKtx.click
+import com.android.systemui.qs.tiles.base.domain.model.QSTileInputTestKtx.longClick
import com.android.systemui.qs.tiles.impl.airplane.domain.model.AirplaneModeTileModel
import com.android.systemui.statusbar.pipeline.airplane.data.repository.FakeAirplaneModeRepository
import com.android.systemui.statusbar.pipeline.airplane.domain.interactor.AirplaneModeInteractor
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/AirplaneModeMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/airplane/ui/mapper/AirplaneModeTileMapperTest.kt
index 311f1f792bba..ccb238626786 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/AirplaneModeMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/airplane/ui/mapper/AirplaneModeTileMapperTest.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2024 The Android Open Source Project
+ * Copyright (C) 2025 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.qs.tiles.impl
+package com.android.systemui.qs.tiles.impl.airplane.ui.mapper
import android.graphics.drawable.TestStubDrawable
import android.service.quicksettings.Tile
@@ -23,11 +23,10 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.common.shared.model.Icon
-import com.android.systemui.qs.tiles.impl.airplane.domain.AirplaneModeMapper
+import com.android.systemui.qs.tiles.base.shared.model.QSTileState
+import com.android.systemui.qs.tiles.base.ui.model.QSTileStateSubject
import com.android.systemui.qs.tiles.impl.airplane.domain.model.AirplaneModeTileModel
import com.android.systemui.qs.tiles.impl.airplane.qsAirplaneModeTileConfig
-import com.android.systemui.qs.tiles.impl.custom.QSTileStateSubject
-import com.android.systemui.qs.tiles.viewmodel.QSTileState
import com.android.systemui.res.R
import com.android.systemui.testKosmos
import org.junit.Before
@@ -36,16 +35,16 @@ import org.junit.runner.RunWith
@SmallTest
@RunWith(AndroidJUnit4::class)
-class AirplaneModeMapperTest : SysuiTestCase() {
+class AirplaneModeTileMapperTest : SysuiTestCase() {
private val kosmos = testKosmos()
private val airplaneModeConfig = kosmos.qsAirplaneModeTileConfig
- private lateinit var mapper: AirplaneModeMapper
+ private lateinit var mapper: AirplaneModeTileMapper
@Before
fun setup() {
mapper =
- AirplaneModeMapper(
+ AirplaneModeTileMapper(
context.orCreateTestableResources
.apply {
addOverride(R.drawable.qs_airplane_icon_off, TestStubDrawable())
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/alarm/domain/interactor/AlarmTileDataInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/alarm/domain/interactor/AlarmTileDataInteractorTest.kt
index afbc3e810743..5fb0ea666813 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/alarm/domain/interactor/AlarmTileDataInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/alarm/domain/interactor/AlarmTileDataInteractorTest.kt
@@ -25,7 +25,7 @@ import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.coroutines.collectValues
-import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
+import com.android.systemui.qs.tiles.base.domain.model.DataUpdateTrigger
import com.android.systemui.qs.tiles.impl.alarm.domain.model.AlarmTileModel
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/alarm/domain/interactor/AlarmTileUserActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/alarm/domain/interactor/AlarmTileUserActionInteractorTest.kt
index be2da174250b..e7dd145cecc5 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/alarm/domain/interactor/AlarmTileUserActionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/alarm/domain/interactor/AlarmTileUserActionInteractorTest.kt
@@ -22,9 +22,9 @@ import android.provider.AlarmClock
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
-import com.android.systemui.qs.tiles.base.actions.FakeQSTileIntentUserInputHandler
-import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandlerSubject
-import com.android.systemui.qs.tiles.base.interactor.QSTileInputTestKtx.click
+import com.android.systemui.qs.tiles.base.domain.actions.FakeQSTileIntentUserInputHandler
+import com.android.systemui.qs.tiles.base.domain.actions.QSTileIntentUserInputHandlerSubject
+import com.android.systemui.qs.tiles.base.domain.model.QSTileInputTestKtx.click
import com.android.systemui.qs.tiles.impl.alarm.domain.model.AlarmTileModel
import com.android.systemui.util.mockito.mock
import com.google.common.truth.Truth.assertThat
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/alarm/domain/AlarmTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/alarm/ui/mapper/AlarmTileMapperTest.kt
index bb58ecaa2566..9b1d0a5a0b58 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/alarm/domain/AlarmTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/alarm/ui/mapper/AlarmTileMapperTest.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2025 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.qs.tiles.impl.alarm.domain
+package com.android.systemui.qs.tiles.impl.alarm.ui.mapper
import android.app.AlarmManager
import android.graphics.drawable.TestStubDrawable
@@ -23,10 +23,10 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.qs.tiles.base.shared.model.QSTileState
+import com.android.systemui.qs.tiles.base.ui.model.QSTileStateSubject
import com.android.systemui.qs.tiles.impl.alarm.domain.model.AlarmTileModel
import com.android.systemui.qs.tiles.impl.alarm.qsAlarmTileConfig
-import com.android.systemui.qs.tiles.impl.custom.QSTileStateSubject
-import com.android.systemui.qs.tiles.viewmodel.QSTileState
import com.android.systemui.res.R
import com.android.systemui.testKosmos
import com.android.systemui.util.time.FakeSystemClock
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/battery/doman/interactor/BatterySaverTileDataInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/battery/doman/interactor/BatterySaverTileDataInteractorTest.kt
index 1ef1a72b7483..8d3f8d4b4166 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/battery/doman/interactor/BatterySaverTileDataInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/battery/doman/interactor/BatterySaverTileDataInteractorTest.kt
@@ -24,7 +24,7 @@ import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.kosmos.testScope
-import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
+import com.android.systemui.qs.tiles.base.domain.model.DataUpdateTrigger
import com.android.systemui.qs.tiles.impl.battery.domain.interactor.BatterySaverTileDataInteractor
import com.android.systemui.testKosmos
import com.android.systemui.utils.leaks.FakeBatteryController
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/battery/doman/interactor/BatterySaverTileUserActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/battery/doman/interactor/BatterySaverTileUserActionInteractorTest.kt
index 62c51e6252ce..68a4d4131086 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/battery/doman/interactor/BatterySaverTileUserActionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/battery/doman/interactor/BatterySaverTileUserActionInteractorTest.kt
@@ -22,9 +22,9 @@ import android.testing.LeakCheck
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
-import com.android.systemui.qs.tiles.base.actions.FakeQSTileIntentUserInputHandler
-import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandlerSubject
-import com.android.systemui.qs.tiles.base.interactor.QSTileInputTestKtx
+import com.android.systemui.qs.tiles.base.domain.actions.FakeQSTileIntentUserInputHandler
+import com.android.systemui.qs.tiles.base.domain.actions.QSTileIntentUserInputHandlerSubject
+import com.android.systemui.qs.tiles.base.domain.model.QSTileInputTestKtx
import com.android.systemui.qs.tiles.impl.battery.domain.interactor.BatterySaverTileUserActionInteractor
import com.android.systemui.qs.tiles.impl.battery.domain.model.BatterySaverTileModel
import com.android.systemui.utils.leaks.FakeBatteryController
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/battery/ui/BatterySaverTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/battery/ui/mapper/BatterySaverTileMapperTest.kt
index cb50ec9a70f4..8128c0c75043 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/battery/ui/BatterySaverTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/battery/ui/mapper/BatterySaverTileMapperTest.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2024 The Android Open Source Project
+ * Copyright (C) 2025 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.qs.tiles.impl.battery.ui
+package com.android.systemui.qs.tiles.impl.battery.ui.mapper
import android.graphics.drawable.TestStubDrawable
import android.widget.Switch
@@ -22,10 +22,10 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.qs.tiles.base.shared.model.QSTileState
+import com.android.systemui.qs.tiles.base.ui.model.QSTileStateSubject
import com.android.systemui.qs.tiles.impl.battery.domain.model.BatterySaverTileModel
import com.android.systemui.qs.tiles.impl.battery.qsBatterySaverTileConfig
-import com.android.systemui.qs.tiles.impl.custom.QSTileStateSubject
-import com.android.systemui.qs.tiles.viewmodel.QSTileState
import com.android.systemui.res.R
import com.android.systemui.testKosmos
import org.junit.Before
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/interactor/ColorCorrectionTileDataInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/interactor/ColorCorrectionTileDataInteractorTest.kt
index 4c156b77132b..4819539f4a80 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/interactor/ColorCorrectionTileDataInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/interactor/ColorCorrectionTileDataInteractorTest.kt
@@ -23,7 +23,7 @@ import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.accessibility.data.repository.FakeColorCorrectionRepository
import com.android.systemui.coroutines.collectValues
-import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
+import com.android.systemui.qs.tiles.base.domain.model.DataUpdateTrigger
import com.android.systemui.qs.tiles.impl.colorcorrection.domain.model.ColorCorrectionTileModel
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.flow.flowOf
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/interactor/ColorCorrectionTileUserActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/interactor/ColorCorrectionTileUserActionInteractorTest.kt
index 0cf3734dd92b..90533f518259 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/interactor/ColorCorrectionTileUserActionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/interactor/ColorCorrectionTileUserActionInteractorTest.kt
@@ -24,9 +24,9 @@ import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.accessibility.data.repository.FakeColorCorrectionRepository
import com.android.systemui.qs.shared.QSSettingsPackageRepository
-import com.android.systemui.qs.tiles.base.actions.FakeQSTileIntentUserInputHandler
-import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandlerSubject
-import com.android.systemui.qs.tiles.base.interactor.QSTileInputTestKtx
+import com.android.systemui.qs.tiles.base.domain.actions.FakeQSTileIntentUserInputHandler
+import com.android.systemui.qs.tiles.base.domain.actions.QSTileIntentUserInputHandlerSubject
+import com.android.systemui.qs.tiles.base.domain.model.QSTileInputTestKtx
import com.android.systemui.qs.tiles.impl.colorcorrection.domain.model.ColorCorrectionTileModel
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.test.runTest
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/ColorCorrectionTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/colorcorrection/ui/mapper/ColorCorrectionTileMapperTest.kt
index bcd443cc78e1..124e013ba784 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/ColorCorrectionTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/colorcorrection/ui/mapper/ColorCorrectionTileMapperTest.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2025 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.qs.tiles.impl.colorcorrection.domain
+package com.android.systemui.qs.tiles.impl.colorcorrection.ui.mapper
import android.graphics.drawable.TestStubDrawable
import android.widget.Switch
@@ -22,10 +22,10 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.qs.tiles.base.shared.model.QSTileState
+import com.android.systemui.qs.tiles.base.ui.model.QSTileStateSubject
import com.android.systemui.qs.tiles.impl.colorcorrection.domain.model.ColorCorrectionTileModel
import com.android.systemui.qs.tiles.impl.colorcorrection.qsColorCorrectionTileConfig
-import com.android.systemui.qs.tiles.impl.custom.QSTileStateSubject
-import com.android.systemui.qs.tiles.viewmodel.QSTileState
import com.android.systemui.res.R
import com.android.systemui.testKosmos
import org.junit.Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/CustomTileDefaultsRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/data/repository/CustomTileDefaultsRepositoryTest.kt
index 10530a25b06e..80e865de43e7 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/CustomTileDefaultsRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/data/repository/CustomTileDefaultsRepositoryTest.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2025 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.qs.tiles.impl.custom
+package com.android.systemui.qs.tiles.impl.custom.data.repository
import android.content.ComponentName
import android.content.Context
@@ -25,9 +25,7 @@ import android.os.UserHandle
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
-import com.android.systemui.qs.tiles.impl.custom.data.entity.CustomTileDefaults
-import com.android.systemui.qs.tiles.impl.custom.data.repository.CustomTileDefaultsRepository
-import com.android.systemui.qs.tiles.impl.custom.data.repository.CustomTileDefaultsRepositoryImpl
+import com.android.systemui.qs.tiles.impl.custom.data.model.CustomTileDefaults
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.eq
import com.android.systemui.util.mockito.whenever
@@ -199,6 +197,7 @@ class CustomTileDefaultsRepositoryTest : SysuiTestCase() {
appInfoIcon = APP_INFO_ICON_1,
isSystemApp = isSystemApp,
)
+
private fun PackageManager.setupApp2(isSystemApp: Boolean = false) =
setupApp(
componentName = COMPONENT_NAME_2,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/CustomTilePackageUpdatesRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/data/repository/CustomTilePackageUpdatesRepositoryTest.kt
index 835dba21ce05..93dcf7d2e80b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/CustomTilePackageUpdatesRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/data/repository/CustomTilePackageUpdatesRepositoryTest.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2025 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.qs.tiles.impl.custom
+package com.android.systemui.qs.tiles.impl.custom.data.repository
import android.annotation.SuppressLint
import android.content.BroadcastReceiver
@@ -28,8 +28,6 @@ import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectValues
import com.android.systemui.qs.pipeline.shared.TileSpec
-import com.android.systemui.qs.tiles.impl.custom.data.repository.CustomTilePackageUpdatesRepository
-import com.android.systemui.qs.tiles.impl.custom.data.repository.CustomTilePackageUpdatesRepositoryImpl
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.capture
import com.android.systemui.util.mockito.eq
@@ -111,7 +109,7 @@ class CustomTilePackageUpdatesRepositoryTest : SysuiTestCase() {
any(),
any(),
nullable(),
- nullable()
+ nullable(),
)
listenerCaptor.value.onReceive(mockedContext, Intent(Intent.ACTION_MAIN))
runCurrent()
@@ -144,9 +142,9 @@ class CustomTilePackageUpdatesRepositoryTest : SysuiTestCase() {
type = IntentFilter.SCHEME_PACKAGE
putExtra(
Intent.EXTRA_CHANGED_COMPONENT_NAME_LIST,
- arrayOf(componentName.packageName)
+ arrayOf(componentName.packageName),
)
- }
+ },
)
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/data/repository/CustomTileRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/data/repository/CustomTileRepositoryTest.kt
index 3c0e7d51b4e8..f8489c17a69c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/data/repository/CustomTileRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/data/repository/CustomTileRepositoryTest.kt
@@ -28,11 +28,11 @@ import com.android.systemui.kosmos.testScope
import com.android.systemui.qs.external.TileServiceKey
import com.android.systemui.qs.pipeline.shared.TileSpec
import com.android.systemui.qs.tiles.impl.custom.TileSubject.Companion.assertThat
-import com.android.systemui.qs.tiles.impl.custom.commons.copy
import com.android.systemui.qs.tiles.impl.custom.customTileSpec
import com.android.systemui.qs.tiles.impl.custom.customTileStatePersister
-import com.android.systemui.qs.tiles.impl.custom.data.entity.CustomTileDefaults
+import com.android.systemui.qs.tiles.impl.custom.data.model.CustomTileDefaults
import com.android.systemui.qs.tiles.impl.custom.packageManagerAdapterFacade
+import com.android.systemui.qs.tiles.impl.custom.shared.model.copy
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.flow.first
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileDataInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileDataInteractorTest.kt
index 2cc3678c2065..8e106490591b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileDataInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileDataInteractorTest.kt
@@ -31,7 +31,7 @@ import com.android.systemui.qs.external.iQSTileService
import com.android.systemui.qs.external.tileServiceManagerFacade
import com.android.systemui.qs.external.tileServicesFacade
import com.android.systemui.qs.pipeline.shared.TileSpec
-import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
+import com.android.systemui.qs.tiles.base.domain.model.DataUpdateTrigger
import com.android.systemui.qs.tiles.impl.custom.TileSubject.Companion.assertThat
import com.android.systemui.qs.tiles.impl.custom.customTileDefaultsRepository
import com.android.systemui.qs.tiles.impl.custom.customTileInteractor
@@ -39,7 +39,7 @@ import com.android.systemui.qs.tiles.impl.custom.customTilePackagesUpdatesReposi
import com.android.systemui.qs.tiles.impl.custom.customTileRepository
import com.android.systemui.qs.tiles.impl.custom.customTileServiceInteractor
import com.android.systemui.qs.tiles.impl.custom.customTileSpec
-import com.android.systemui.qs.tiles.impl.custom.data.entity.CustomTileDefaults
+import com.android.systemui.qs.tiles.impl.custom.data.model.CustomTileDefaults
import com.android.systemui.qs.tiles.impl.custom.qsTileLogger
import com.android.systemui.testKosmos
import com.android.systemui.user.data.repository.fakeUserRepository
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileInteractorTest.kt
index a317dc525df9..6fef9f1da3af 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileInteractorTest.kt
@@ -33,7 +33,7 @@ import com.android.systemui.qs.tiles.impl.custom.customTileDefaultsRepository
import com.android.systemui.qs.tiles.impl.custom.customTileRepository
import com.android.systemui.qs.tiles.impl.custom.customTileSpec
import com.android.systemui.qs.tiles.impl.custom.customTileStatePersister
-import com.android.systemui.qs.tiles.impl.custom.data.entity.CustomTileDefaults
+import com.android.systemui.qs.tiles.impl.custom.data.model.CustomTileDefaults
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.flow.first
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileUserActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileUserActionInteractorTest.kt
index 72e5766e409a..df4f097c8a8d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileUserActionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileUserActionInteractorTest.kt
@@ -38,14 +38,14 @@ import com.android.systemui.kosmos.testScope
import com.android.systemui.qs.external.componentName
import com.android.systemui.qs.external.iQSTileService
import com.android.systemui.qs.pipeline.shared.TileSpec
-import com.android.systemui.qs.tiles.base.actions.FakeQSTileIntentUserInputHandler
-import com.android.systemui.qs.tiles.base.actions.intentInputs
-import com.android.systemui.qs.tiles.base.actions.pendingIntentInputs
-import com.android.systemui.qs.tiles.base.interactor.QSTileInputTestKtx.click
-import com.android.systemui.qs.tiles.base.interactor.QSTileInputTestKtx.longClick
+import com.android.systemui.qs.tiles.base.domain.actions.FakeQSTileIntentUserInputHandler
+import com.android.systemui.qs.tiles.base.domain.actions.intentInputs
+import com.android.systemui.qs.tiles.base.domain.actions.pendingIntentInputs
+import com.android.systemui.qs.tiles.base.domain.model.QSTileInputTestKtx.click
+import com.android.systemui.qs.tiles.base.domain.model.QSTileInputTestKtx.longClick
import com.android.systemui.qs.tiles.impl.custom.customTileServiceInteractor
import com.android.systemui.qs.tiles.impl.custom.customTileSpec
-import com.android.systemui.qs.tiles.impl.custom.domain.entity.CustomTileDataModel
+import com.android.systemui.qs.tiles.impl.custom.domain.model.CustomTileDataModel
import com.android.systemui.qs.tiles.impl.custom.qsTileLogger
import com.android.systemui.testKosmos
import com.android.systemui.user.data.repository.fakeUserRepository
@@ -144,7 +144,7 @@ class CustomTileUserActionInteractorTest : SysuiTestCase() {
assertThat(
intent.getParcelableExtra(
Intent.EXTRA_COMPONENT_NAME,
- ComponentName::class.java
+ ComponentName::class.java,
)
)
.isEqualTo(componentName)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/ui/mapper/CustomTileMapperTest.kt
index 608adf183163..41f687251a0b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/custom/ui/mapper/CustomTileMapperTest.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2025 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.qs.tiles.impl.custom.domain.interactor
+package com.android.systemui.qs.tiles.impl.custom.ui.mapper
import android.app.IUriGrantsManager
import android.content.ComponentName
@@ -32,12 +32,11 @@ import com.android.systemui.SysuiTestCase
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testScope
import com.android.systemui.qs.pipeline.shared.TileSpec
-import com.android.systemui.qs.tiles.impl.custom.QSTileStateSubject.Companion.assertThat
+import com.android.systemui.qs.tiles.base.shared.model.QSTileState
+import com.android.systemui.qs.tiles.base.ui.model.QSTileStateSubject.Companion.assertThat
import com.android.systemui.qs.tiles.impl.custom.customTileQsTileConfig
import com.android.systemui.qs.tiles.impl.custom.customTileSpec
-import com.android.systemui.qs.tiles.impl.custom.domain.CustomTileMapper
-import com.android.systemui.qs.tiles.impl.custom.domain.entity.CustomTileDataModel
-import com.android.systemui.qs.tiles.viewmodel.QSTileState
+import com.android.systemui.qs.tiles.impl.custom.domain.model.CustomTileDataModel
import com.android.systemui.testKosmos
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.eq
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/flashlight/domain/interactor/FlashlightTileDataInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/flashlight/domain/interactor/FlashlightTileDataInteractorTest.kt
index d42eb5ebc9ab..5df7ae348e4b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/flashlight/domain/interactor/FlashlightTileDataInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/flashlight/domain/interactor/FlashlightTileDataInteractorTest.kt
@@ -24,7 +24,7 @@ import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.coroutines.collectValues
-import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
+import com.android.systemui.qs.tiles.base.domain.model.DataUpdateTrigger
import com.android.systemui.qs.tiles.impl.flashlight.domain.model.FlashlightTileModel
import com.android.systemui.utils.leaks.FakeFlashlightController
import com.google.common.truth.Truth.assertThat
@@ -57,6 +57,7 @@ class FlashlightTileDataInteractorTest : SysuiTestCase() {
assertThat(availability).isTrue()
}
+
@Test
fun availabilityOffMatchesController() = runTest {
controller.hasFlashlight = false
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/flashlight/domain/interactor/FlashlightTileUserActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/flashlight/domain/interactor/FlashlightTileUserActionInteractorTest.kt
index 1f19c98a93a3..d7536dcc32e1 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/flashlight/domain/interactor/FlashlightTileUserActionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/flashlight/domain/interactor/FlashlightTileUserActionInteractorTest.kt
@@ -20,7 +20,7 @@ import android.app.ActivityManager
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
-import com.android.systemui.qs.tiles.base.interactor.QSTileInputTestKtx.click
+import com.android.systemui.qs.tiles.base.domain.model.QSTileInputTestKtx.click
import com.android.systemui.qs.tiles.impl.flashlight.domain.model.FlashlightTileModel
import com.android.systemui.statusbar.policy.FlashlightController
import com.android.systemui.util.mockito.mock
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/flashlight/domain/FlashlightMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/flashlight/ui/mapper/FlashlightMapperTest.kt
index 2da144e5ee98..aebc67b9ce5d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/flashlight/domain/FlashlightMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/flashlight/ui/mapper/FlashlightMapperTest.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2025 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.
@@ -14,16 +14,16 @@
* limitations under the License.
*/
-package com.android.systemui.qs.tiles.impl.flashlight.domain
+package com.android.systemui.qs.tiles.impl.flashlight.ui.mapper
import android.graphics.drawable.TestStubDrawable
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.qs.tiles.base.shared.model.QSTileState
import com.android.systemui.qs.tiles.impl.flashlight.domain.model.FlashlightTileModel
import com.android.systemui.qs.tiles.impl.flashlight.qsFlashlightTileConfig
-import com.android.systemui.qs.tiles.viewmodel.QSTileState
import com.android.systemui.res.R
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/fontscaling/domain/interactor/FontScalingTileDataInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/fontscaling/domain/interactor/FontScalingTileDataInteractorTest.kt
index cd825045de44..585c1a7c5e3f 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/fontscaling/domain/interactor/FontScalingTileDataInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/fontscaling/domain/interactor/FontScalingTileDataInteractorTest.kt
@@ -22,7 +22,7 @@ import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.coroutines.collectValues
-import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
+import com.android.systemui.qs.tiles.base.domain.model.DataUpdateTrigger
import com.google.common.truth.Truth
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.test.runCurrent
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/fontscaling/domain/interactor/FontScalingUserActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/fontscaling/domain/interactor/FontScalingUserActionInteractorTest.kt
index 93f2bc34372e..11b9f7712e2b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/fontscaling/domain/interactor/FontScalingUserActionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/fontscaling/domain/interactor/FontScalingUserActionInteractorTest.kt
@@ -29,10 +29,10 @@ import com.android.systemui.animation.LaunchableView
import com.android.systemui.kosmos.testScope
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.qs.shared.QSSettingsPackageRepository
-import com.android.systemui.qs.tiles.base.actions.FakeQSTileIntentUserInputHandler
-import com.android.systemui.qs.tiles.base.actions.intentInputs
-import com.android.systemui.qs.tiles.base.interactor.QSTileInputTestKtx
-import com.android.systemui.qs.tiles.base.interactor.QSTileInputTestKtx.click
+import com.android.systemui.qs.tiles.base.domain.actions.FakeQSTileIntentUserInputHandler
+import com.android.systemui.qs.tiles.base.domain.actions.intentInputs
+import com.android.systemui.qs.tiles.base.domain.model.QSTileInputTestKtx
+import com.android.systemui.qs.tiles.base.domain.model.QSTileInputTestKtx.click
import com.android.systemui.qs.tiles.impl.fontscaling.domain.model.FontScalingTileModel
import com.android.systemui.statusbar.phone.FakeKeyguardStateController
import com.android.systemui.statusbar.phone.SystemUIDialog
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/fontscaling/domain/FontScalingTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/fontscaling/ui/mapper/FontScalingTileMapperTest.kt
index 45720b86a859..1e794f917cb9 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/fontscaling/domain/FontScalingTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/fontscaling/ui/mapper/FontScalingTileMapperTest.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2024 The Android Open Source Project
+ * Copyright (C) 2025 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.qs.tiles.impl.fontscaling.domain
+package com.android.systemui.qs.tiles.impl.fontscaling.ui.mapper
import android.graphics.drawable.TestStubDrawable
import android.widget.Switch
@@ -22,10 +22,10 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.common.shared.model.Icon
-import com.android.systemui.qs.tiles.impl.custom.QSTileStateSubject
+import com.android.systemui.qs.tiles.base.shared.model.QSTileState
+import com.android.systemui.qs.tiles.base.ui.model.QSTileStateSubject
import com.android.systemui.qs.tiles.impl.fontscaling.domain.model.FontScalingTileModel
import com.android.systemui.qs.tiles.impl.fontscaling.qsFontScalingTileConfig
-import com.android.systemui.qs.tiles.viewmodel.QSTileState
import com.android.systemui.res.R
import com.android.systemui.testKosmos
import org.junit.Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/interactor/HearingDevicesTileDataInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/interactor/HearingDevicesTileDataInteractorTest.kt
index 4d38e7588578..ae37684522fc 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/interactor/HearingDevicesTileDataInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/interactor/HearingDevicesTileDataInteractorTest.kt
@@ -25,7 +25,7 @@ import com.android.systemui.accessibility.hearingaid.HearingDevicesChecker
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.coroutines.collectValues
import com.android.systemui.kosmos.testScope
-import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
+import com.android.systemui.qs.tiles.base.domain.model.DataUpdateTrigger
import com.android.systemui.qs.tiles.impl.hearingdevices.domain.model.HearingDevicesTileModel
import com.android.systemui.statusbar.policy.fakeBluetoothController
import com.android.systemui.testKosmos
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/interactor/HearingDevicesTileUserActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/interactor/HearingDevicesTileUserActionInteractorTest.kt
index 0ba057b1881b..5ab728f6e9e3 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/interactor/HearingDevicesTileUserActionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/interactor/HearingDevicesTileUserActionInteractorTest.kt
@@ -24,9 +24,9 @@ import com.android.systemui.accessibility.hearingaid.HearingDevicesDialogManager
import com.android.systemui.accessibility.hearingaid.HearingDevicesUiEventLogger.Companion.LAUNCH_SOURCE_QS_TILE
import com.android.systemui.kosmos.testScope
import com.android.systemui.qs.shared.QSSettingsPackageRepository
-import com.android.systemui.qs.tiles.base.actions.FakeQSTileIntentUserInputHandler
-import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandlerSubject
-import com.android.systemui.qs.tiles.base.interactor.QSTileInputTestKtx
+import com.android.systemui.qs.tiles.base.domain.actions.FakeQSTileIntentUserInputHandler
+import com.android.systemui.qs.tiles.base.domain.actions.QSTileIntentUserInputHandlerSubject
+import com.android.systemui.qs.tiles.base.domain.model.QSTileInputTestKtx
import com.android.systemui.qs.tiles.impl.hearingdevices.domain.model.HearingDevicesTileModel
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/HearingDevicesTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/hearingdevices/ui/mapper/HearingDevicesTileMapperTest.kt
index c41034202c88..b0129f0c060d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/HearingDevicesTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/hearingdevices/ui/mapper/HearingDevicesTileMapperTest.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2024 The Android Open Source Project
+ * Copyright (C) 2025 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.
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.systemui.qs.tiles.impl.hearingdevices.domain
+package com.android.systemui.qs.tiles.impl.hearingdevices.ui.mapper
import android.graphics.drawable.TestStubDrawable
import android.widget.Switch
@@ -21,10 +21,10 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.common.shared.model.Icon
-import com.android.systemui.qs.tiles.impl.custom.QSTileStateSubject
+import com.android.systemui.qs.tiles.base.shared.model.QSTileState
+import com.android.systemui.qs.tiles.base.ui.model.QSTileStateSubject
import com.android.systemui.qs.tiles.impl.hearingdevices.domain.model.HearingDevicesTileModel
import com.android.systemui.qs.tiles.impl.hearingdevices.qsHearingDevicesTileConfig
-import com.android.systemui.qs.tiles.viewmodel.QSTileState
import com.android.systemui.res.R
import com.android.systemui.testKosmos
import org.junit.Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/internet/domain/interactor/InternetTileDataInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/internet/domain/interactor/InternetTileDataInteractorTest.kt
index 45582f017fb0..a8747e81a2e3 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/internet/domain/interactor/InternetTileDataInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/internet/domain/interactor/InternetTileDataInteractorTest.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2024 The Android Open Source Project
+ * Copyright (C) 2025 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.
@@ -31,7 +31,7 @@ import com.android.systemui.flags.FakeFeatureFlagsClassic
import com.android.systemui.flags.Flags
import com.android.systemui.kosmos.testScope
import com.android.systemui.log.table.logcatTableLogBuffer
-import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
+import com.android.systemui.qs.tiles.base.domain.model.DataUpdateTrigger
import com.android.systemui.qs.tiles.impl.internet.domain.model.InternetTileModel
import com.android.systemui.res.R
import com.android.systemui.statusbar.connectivity.WifiIcons
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/internet/domain/interactor/InternetTileUserActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/internet/domain/interactor/InternetTileUserActionInteractorTest.kt
index d019a456a4f5..8b58e5674176 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/internet/domain/interactor/InternetTileUserActionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/internet/domain/interactor/InternetTileUserActionInteractorTest.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2024 The Android Open Source Project
+ * Copyright (C) 2025 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.
@@ -22,9 +22,9 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.kosmos.testScope
-import com.android.systemui.qs.tiles.base.actions.FakeQSTileIntentUserInputHandler
-import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandlerSubject
-import com.android.systemui.qs.tiles.base.interactor.QSTileInputTestKtx
+import com.android.systemui.qs.tiles.base.domain.actions.FakeQSTileIntentUserInputHandler
+import com.android.systemui.qs.tiles.base.domain.actions.QSTileIntentUserInputHandlerSubject
+import com.android.systemui.qs.tiles.base.domain.model.QSTileInputTestKtx
import com.android.systemui.qs.tiles.dialog.InternetDialogManager
import com.android.systemui.qs.tiles.impl.internet.domain.model.InternetTileModel
import com.android.systemui.statusbar.connectivity.AccessPointController
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/internet/domain/InternetTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/internet/ui/mapper/InternetTileMapperTest.kt
index e15664eba6b9..da91f70173c6 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/internet/domain/InternetTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/internet/ui/mapper/InternetTileMapperTest.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2024 The Android Open Source Project
+ * Copyright (C) 2025 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.qs.tiles.impl.internet.domain
+package com.android.systemui.qs.tiles.impl.internet.ui.mapper
import android.graphics.drawable.TestStubDrawable
import android.os.fakeExecutorHandler
@@ -28,10 +28,10 @@ import com.android.systemui.common.shared.model.ContentDescription.Companion.loa
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.common.shared.model.Text
import com.android.systemui.common.shared.model.Text.Companion.loadText
-import com.android.systemui.qs.tiles.impl.custom.QSTileStateSubject
+import com.android.systemui.qs.tiles.base.shared.model.QSTileState
+import com.android.systemui.qs.tiles.base.ui.model.QSTileStateSubject
import com.android.systemui.qs.tiles.impl.internet.domain.model.InternetTileModel
import com.android.systemui.qs.tiles.impl.internet.qsInternetTileConfig
-import com.android.systemui.qs.tiles.viewmodel.QSTileState
import com.android.systemui.res.R
import com.android.systemui.statusbar.connectivity.WifiIcons.WIFI_FULL_ICONS
import com.android.systemui.statusbar.pipeline.mobile.domain.model.SignalIconModel
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/inversion/domain/interactor/ColorInversionTileDataInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/inversion/domain/interactor/ColorInversionTileDataInteractorTest.kt
index 228e99305926..70828d5d6ff8 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/inversion/domain/interactor/ColorInversionTileDataInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/inversion/domain/interactor/ColorInversionTileDataInteractorTest.kt
@@ -23,7 +23,7 @@ import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.accessibility.data.repository.FakeColorInversionRepository
import com.android.systemui.coroutines.collectValues
-import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
+import com.android.systemui.qs.tiles.base.domain.model.DataUpdateTrigger
import com.android.systemui.qs.tiles.impl.inversion.domain.model.ColorInversionTileModel
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.flow.flowOf
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/inversion/domain/interactor/ColorInversionUserActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/inversion/domain/interactor/ColorInversionUserActionInteractorTest.kt
index 3f77b86971e1..e4a1ce2766c1 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/inversion/domain/interactor/ColorInversionUserActionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/inversion/domain/interactor/ColorInversionUserActionInteractorTest.kt
@@ -24,9 +24,9 @@ import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.accessibility.data.repository.FakeColorInversionRepository
import com.android.systemui.qs.shared.QSSettingsPackageRepository
-import com.android.systemui.qs.tiles.base.actions.FakeQSTileIntentUserInputHandler
-import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandlerSubject
-import com.android.systemui.qs.tiles.base.interactor.QSTileInputTestKtx
+import com.android.systemui.qs.tiles.base.domain.actions.FakeQSTileIntentUserInputHandler
+import com.android.systemui.qs.tiles.base.domain.actions.QSTileIntentUserInputHandlerSubject
+import com.android.systemui.qs.tiles.base.domain.model.QSTileInputTestKtx
import com.android.systemui.qs.tiles.impl.inversion.domain.model.ColorInversionTileModel
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.test.runTest
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/inversion/domain/ColorInversionTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/inversion/ui/mapper/ColorInversionTileMapperTest.kt
index 48e114603723..509a178ffcc3 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/inversion/domain/ColorInversionTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/inversion/ui/mapper/ColorInversionTileMapperTest.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2025 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.qs.tiles.impl.inversion.domain
+package com.android.systemui.qs.tiles.impl.inversion.ui.mapper
import android.graphics.drawable.TestStubDrawable
import android.widget.Switch
@@ -23,10 +23,10 @@ import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.qs.tileimpl.SubtitleArrayMapping
-import com.android.systemui.qs.tiles.impl.custom.QSTileStateSubject
+import com.android.systemui.qs.tiles.base.shared.model.QSTileState
+import com.android.systemui.qs.tiles.base.ui.model.QSTileStateSubject
import com.android.systemui.qs.tiles.impl.inversion.domain.model.ColorInversionTileModel
import com.android.systemui.qs.tiles.impl.inversion.qsColorInversionTileConfig
-import com.android.systemui.qs.tiles.viewmodel.QSTileState
import com.android.systemui.res.R
import com.android.systemui.testKosmos
import org.junit.Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingDataInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/irecording/domain/interactor/IssueRecordingDataInteractorTest.kt
index 57f85bd8c5ac..1ee6ac6ccea8 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingDataInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/irecording/domain/interactor/IssueRecordingDataInteractorTest.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2024 The Android Open Source Project
+ * Copyright (C) 2025 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.qs.tiles.impl.irecording
+package com.android.systemui.qs.tiles.impl.irecording.domain.interactor
import android.os.Handler
import android.os.UserHandle
@@ -24,7 +24,7 @@ import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.kosmos.testCase
import com.android.systemui.kosmos.testScope
-import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
+import com.android.systemui.qs.tiles.base.domain.model.DataUpdateTrigger
import com.android.systemui.recordissue.IssueRecordingState
import com.android.systemui.settings.fakeUserFileManager
import com.android.systemui.settings.userTracker
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingUserActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/irecording/domain/interactor/IssueRecordingUserActionInteractorTest.kt
index 125419ba3bdf..2791b74f2483 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingUserActionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/irecording/domain/interactor/IssueRecordingUserActionInteractorTest.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2024 The Android Open Source Project
+ * Copyright (C) 2025 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.qs.tiles.impl.irecording
+package com.android.systemui.qs.tiles.impl.irecording.domain.interactor
import android.os.Handler
import android.os.UserHandle
@@ -28,8 +28,9 @@ import com.android.systemui.kosmos.testScope
import com.android.systemui.plugins.activityStarter
import com.android.systemui.plugins.statusbar.statusBarStateController
import com.android.systemui.qs.pipeline.domain.interactor.panelInteractor
-import com.android.systemui.qs.tiles.base.interactor.QSTileInput
-import com.android.systemui.qs.tiles.viewmodel.QSTileUserAction
+import com.android.systemui.qs.tiles.base.domain.model.QSTileInput
+import com.android.systemui.qs.tiles.base.shared.model.QSTileUserAction
+import com.android.systemui.qs.tiles.impl.irecording.data.model.IssueRecordingModel
import com.android.systemui.recordissue.IssueRecordingState
import com.android.systemui.recordissue.RecordIssueDialogDelegate
import com.android.systemui.screenrecord.RecordingController
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/irecording/ui/mapper/IssueRecordingMapperTest.kt
index 0201168181a9..e469330ab21c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/irecording/ui/mapper/IssueRecordingMapperTest.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2024 The Android Open Source Project
+ * Copyright (C) 2025 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.qs.tiles.impl.irecording
+package com.android.systemui.qs.tiles.impl.irecording.ui.mapper
import android.content.res.mainResources
import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -24,9 +24,10 @@ import com.android.systemui.kosmos.testCase
import com.android.systemui.qs.pipeline.shared.TileSpec
import com.android.systemui.qs.qsEventLogger
import com.android.systemui.qs.shared.model.TileCategory
-import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
-import com.android.systemui.qs.tiles.viewmodel.QSTileState
-import com.android.systemui.qs.tiles.viewmodel.QSTileUIConfig
+import com.android.systemui.qs.tiles.base.shared.model.QSTileConfig
+import com.android.systemui.qs.tiles.base.shared.model.QSTileState
+import com.android.systemui.qs.tiles.base.shared.model.QSTileUIConfig
+import com.android.systemui.qs.tiles.impl.irecording.data.model.IssueRecordingModel
import com.android.systemui.recordissue.RecordIssueModule
import com.android.systemui.res.R
import com.android.systemui.testKosmos
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/location/interactor/LocationTileDataInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/location/domain/interactor/LocationTileDataInteractorTest.kt
index 52ce95b1d742..9c527145b6b4 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/location/interactor/LocationTileDataInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/location/domain/interactor/LocationTileDataInteractorTest.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2025 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.qs.tiles.impl.location.interactor
+package com.android.systemui.qs.tiles.impl.location.domain.interactor
import android.os.UserHandle
import android.platform.test.annotations.EnabledOnRavenwood
@@ -24,8 +24,7 @@ import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.coroutines.collectValues
-import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
-import com.android.systemui.qs.tiles.impl.location.domain.interactor.LocationTileDataInteractor
+import com.android.systemui.qs.tiles.base.domain.model.DataUpdateTrigger
import com.android.systemui.qs.tiles.impl.location.domain.model.LocationTileModel
import com.android.systemui.utils.leaks.FakeLocationController
import com.google.common.truth.Truth
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/location/interactor/LocationTileUserActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/location/domain/interactor/LocationTileUserActionInteractorTest.kt
index 435aea07c9a0..bd632531b62d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/location/interactor/LocationTileUserActionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/location/domain/interactor/LocationTileUserActionInteractorTest.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.qs.tiles.impl.location.interactor
+package com.android.systemui.qs.tiles.impl.location.domain.interactor
import android.platform.test.annotations.EnabledOnRavenwood
import android.provider.Settings
@@ -23,11 +23,10 @@ import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.kosmos.testScope
import com.android.systemui.plugins.ActivityStarter
-import com.android.systemui.qs.tiles.base.actions.FakeQSTileIntentUserInputHandler
-import com.android.systemui.qs.tiles.base.actions.intentInputs
-import com.android.systemui.qs.tiles.base.interactor.QSTileInputTestKtx.click
-import com.android.systemui.qs.tiles.base.interactor.QSTileInputTestKtx.longClick
-import com.android.systemui.qs.tiles.impl.location.domain.interactor.LocationTileUserActionInteractor
+import com.android.systemui.qs.tiles.base.domain.actions.FakeQSTileIntentUserInputHandler
+import com.android.systemui.qs.tiles.base.domain.actions.intentInputs
+import com.android.systemui.qs.tiles.base.domain.model.QSTileInputTestKtx.click
+import com.android.systemui.qs.tiles.base.domain.model.QSTileInputTestKtx.longClick
import com.android.systemui.qs.tiles.impl.location.domain.model.LocationTileModel
import com.android.systemui.statusbar.phone.FakeKeyguardStateController
import com.android.systemui.statusbar.policy.LocationController
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/location/domain/LocationTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/location/ui/mapper/LocationTileMapperTest.kt
index a6ad54935f08..807e37339d3f 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/location/domain/LocationTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/location/ui/mapper/LocationTileMapperTest.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2025 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.
@@ -14,16 +14,16 @@
* limitations under the License.
*/
-package com.android.systemui.qs.tiles.impl.location.domain
+package com.android.systemui.qs.tiles.impl.location.ui.mapper
import android.graphics.drawable.TestStubDrawable
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.qs.tiles.base.shared.model.QSTileState
import com.android.systemui.qs.tiles.impl.location.domain.model.LocationTileModel
import com.android.systemui.qs.tiles.impl.location.qsLocationTileConfig
-import com.android.systemui.qs.tiles.viewmodel.QSTileState
import com.android.systemui.res.R
import com.android.systemui.testKosmos
import com.google.common.truth.Truth
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesDndTileDataInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesDndTileDataInteractorTest.kt
index 23d7b86df875..588832046776 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesDndTileDataInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesDndTileDataInteractorTest.kt
@@ -27,7 +27,7 @@ import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.kosmos.testDispatcher
import com.android.systemui.kosmos.testScope
-import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
+import com.android.systemui.qs.tiles.base.domain.model.DataUpdateTrigger
import com.android.systemui.statusbar.policy.data.repository.fakeZenModeRepository
import com.android.systemui.statusbar.policy.domain.interactor.zenModeInteractor
import com.android.systemui.testKosmos
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesDndTileUserActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesDndTileUserActionInteractorTest.kt
index fda62e49ca72..4ef61d900460 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesDndTileUserActionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesDndTileUserActionInteractorTest.kt
@@ -26,9 +26,9 @@ import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.kosmos.mainCoroutineContext
import com.android.systemui.kosmos.testScope
import com.android.systemui.qs.shared.QSSettingsPackageRepository
-import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandlerSubject
-import com.android.systemui.qs.tiles.base.actions.qsTileIntentUserInputHandler
-import com.android.systemui.qs.tiles.base.interactor.QSTileInputTestKtx
+import com.android.systemui.qs.tiles.base.domain.actions.QSTileIntentUserInputHandlerSubject
+import com.android.systemui.qs.tiles.base.domain.actions.qsTileIntentUserInputHandler
+import com.android.systemui.qs.tiles.base.domain.model.QSTileInputTestKtx
import com.android.systemui.qs.tiles.impl.modes.domain.model.ModesDndTileModel
import com.android.systemui.statusbar.policy.data.repository.zenModeRepository
import com.android.systemui.statusbar.policy.domain.interactor.zenModeInteractor
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileDataInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileDataInteractorTest.kt
index 0b641cee29a2..1df2e1baf6aa 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileDataInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileDataInteractorTest.kt
@@ -34,7 +34,7 @@ import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.coroutines.collectValues
import com.android.systemui.kosmos.testDispatcher
import com.android.systemui.kosmos.testScope
-import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
+import com.android.systemui.qs.tiles.base.domain.model.DataUpdateTrigger
import com.android.systemui.qs.tiles.impl.modes.domain.model.ModesTileModel
import com.android.systemui.statusbar.policy.data.repository.fakeZenModeRepository
import com.android.systemui.statusbar.policy.domain.interactor.zenModeInteractor
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileUserActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileUserActionInteractorTest.kt
index 24e4279642d8..9d0b26f7104c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileUserActionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileUserActionInteractorTest.kt
@@ -30,9 +30,9 @@ import com.android.systemui.common.shared.model.asIcon
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.kosmos.mainCoroutineContext
import com.android.systemui.kosmos.testScope
-import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandlerSubject
-import com.android.systemui.qs.tiles.base.actions.qsTileIntentUserInputHandler
-import com.android.systemui.qs.tiles.base.interactor.QSTileInputTestKtx
+import com.android.systemui.qs.tiles.base.domain.actions.QSTileIntentUserInputHandlerSubject
+import com.android.systemui.qs.tiles.base.domain.actions.qsTileIntentUserInputHandler
+import com.android.systemui.qs.tiles.base.domain.model.QSTileInputTestKtx
import com.android.systemui.qs.tiles.impl.modes.domain.model.ModesTileModel
import com.android.systemui.statusbar.policy.data.repository.zenModeRepository
import com.android.systemui.statusbar.policy.domain.interactor.zenModeInteractor
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/ui/ModesDndTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/ui/ModesDndTileMapperTest.kt
index fb48d228746a..318ce0eaab35 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/ui/ModesDndTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/ui/ModesDndTileMapperTest.kt
@@ -23,10 +23,10 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.qs.tiles.base.shared.model.QSTileConfigTestBuilder
+import com.android.systemui.qs.tiles.base.shared.model.QSTileState
+import com.android.systemui.qs.tiles.base.shared.model.QSTileUIConfig
import com.android.systemui.qs.tiles.impl.modes.domain.model.ModesDndTileModel
-import com.android.systemui.qs.tiles.viewmodel.QSTileConfigTestBuilder
-import com.android.systemui.qs.tiles.viewmodel.QSTileState
-import com.android.systemui.qs.tiles.viewmodel.QSTileUIConfig
import com.android.systemui.res.R
import com.google.common.truth.Truth.assertThat
import org.junit.Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/ui/ModesTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/ui/mapper/ModesTileMapperTest.kt
index d73044f6b479..e8a85f46d381 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/ui/ModesTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/ui/mapper/ModesTileMapperTest.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2024 The Android Open Source Project
+ * Copyright (C) 2025 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.qs.tiles.impl.modes.ui
+package com.android.systemui.qs.tiles.impl.modes.ui.mapper
import android.app.Flags
import android.graphics.drawable.TestStubDrawable
@@ -23,10 +23,10 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.common.shared.model.asIcon
+import com.android.systemui.qs.tiles.base.shared.model.QSTileConfigTestBuilder
+import com.android.systemui.qs.tiles.base.shared.model.QSTileState
+import com.android.systemui.qs.tiles.base.shared.model.QSTileUIConfig
import com.android.systemui.qs.tiles.impl.modes.domain.model.ModesTileModel
-import com.android.systemui.qs.tiles.viewmodel.QSTileConfigTestBuilder
-import com.android.systemui.qs.tiles.viewmodel.QSTileState
-import com.android.systemui.qs.tiles.viewmodel.QSTileUIConfig
import com.android.systemui.res.R
import com.google.common.truth.Truth.assertThat
import org.junit.Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/night/domain/interactor/NightDisplayTileUserActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/night/domain/interactor/NightDisplayTileUserActionInteractorTest.kt
index 51975b0508a9..a0d0b2353585 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/night/domain/interactor/NightDisplayTileUserActionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/night/domain/interactor/NightDisplayTileUserActionInteractorTest.kt
@@ -26,9 +26,9 @@ import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.accessibility.data.repository.NightDisplayRepository
import com.android.systemui.dagger.NightDisplayListenerModule
-import com.android.systemui.qs.tiles.base.actions.FakeQSTileIntentUserInputHandler
-import com.android.systemui.qs.tiles.base.actions.intentInputs
-import com.android.systemui.qs.tiles.base.interactor.QSTileInputTestKtx
+import com.android.systemui.qs.tiles.base.domain.actions.FakeQSTileIntentUserInputHandler
+import com.android.systemui.qs.tiles.base.domain.actions.intentInputs
+import com.android.systemui.qs.tiles.base.domain.model.QSTileInputTestKtx
import com.android.systemui.qs.tiles.impl.custom.qsTileLogger
import com.android.systemui.qs.tiles.impl.night.domain.model.NightDisplayTileModel
import com.android.systemui.testKosmos
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/night/ui/NightDisplayTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/night/ui/mapper/NightDisplayTileMapperTest.kt
index 0b0b88e455da..25c70160fceb 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/night/ui/NightDisplayTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/night/ui/mapper/NightDisplayTileMapperTest.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2024 The Android Open Source Project
+ * Copyright (C) 2025 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.qs.tiles.impl.night.ui
+package com.android.systemui.qs.tiles.impl.night.ui.mapper
import android.graphics.drawable.TestStubDrawable
import android.service.quicksettings.Tile
@@ -24,11 +24,11 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.common.shared.model.Icon
-import com.android.systemui.qs.tiles.base.logging.QSTileLogger
-import com.android.systemui.qs.tiles.impl.custom.QSTileStateSubject
+import com.android.systemui.qs.tiles.base.shared.logging.QSTileLogger
+import com.android.systemui.qs.tiles.base.shared.model.QSTileState
+import com.android.systemui.qs.tiles.base.ui.model.QSTileStateSubject
import com.android.systemui.qs.tiles.impl.night.domain.model.NightDisplayTileModel
import com.android.systemui.qs.tiles.impl.night.qsNightDisplayTileConfig
-import com.android.systemui.qs.tiles.viewmodel.QSTileState
import com.android.systemui.res.R
import com.android.systemui.testKosmos
import com.android.systemui.util.mockito.mock
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/notes/domain/interactor/NotesTileDataInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/notes/domain/interactor/NotesTileDataInteractorTest.kt
index 84ab6905a007..51cad304db33 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/notes/domain/interactor/NotesTileDataInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/notes/domain/interactor/NotesTileDataInteractorTest.kt
@@ -25,7 +25,7 @@ import com.android.systemui.Flags
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.kosmos.testScope
-import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
+import com.android.systemui.qs.tiles.base.domain.model.DataUpdateTrigger
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.flow.flowOf
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/notes/domain/interactor/NotesTileUserActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/notes/domain/interactor/NotesTileUserActionInteractorTest.kt
index 282af9159e2b..c29a490d23b5 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/notes/domain/interactor/NotesTileUserActionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/notes/domain/interactor/NotesTileUserActionInteractorTest.kt
@@ -24,9 +24,9 @@ import com.android.systemui.kosmos.testScope
import com.android.systemui.notetask.NoteTaskController
import com.android.systemui.notetask.NoteTaskEntryPoint
import com.android.systemui.qs.pipeline.domain.interactor.PanelInteractor
-import com.android.systemui.qs.tiles.base.actions.FakeQSTileIntentUserInputHandler
-import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandlerSubject
-import com.android.systemui.qs.tiles.base.interactor.QSTileInputTestKtx
+import com.android.systemui.qs.tiles.base.domain.actions.FakeQSTileIntentUserInputHandler
+import com.android.systemui.qs.tiles.base.domain.actions.QSTileIntentUserInputHandlerSubject
+import com.android.systemui.qs.tiles.base.domain.model.QSTileInputTestKtx
import com.android.systemui.qs.tiles.impl.notes.domain.model.NotesTileModel
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/notes/domain/NotesTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/notes/ui/mapper/NotesTileMapperTest.kt
index ecf6f2a9cd1d..0234584abda3 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/notes/domain/NotesTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/notes/ui/mapper/NotesTileMapperTest.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2024 The Android Open Source Project
+ * Copyright (C) 2025 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.qs.tiles.impl.notes.domain
+package com.android.systemui.qs.tiles.impl.notes.ui.mapper
import android.graphics.drawable.TestStubDrawable
import android.widget.Button
@@ -22,10 +22,10 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.common.shared.model.Icon
-import com.android.systemui.qs.tiles.impl.custom.QSTileStateSubject
+import com.android.systemui.qs.tiles.base.shared.model.QSTileState
+import com.android.systemui.qs.tiles.base.ui.model.QSTileStateSubject
import com.android.systemui.qs.tiles.impl.notes.domain.model.NotesTileModel
import com.android.systemui.qs.tiles.impl.notes.qsNotesTileConfig
-import com.android.systemui.qs.tiles.viewmodel.QSTileState
import com.android.systemui.res.R
import com.android.systemui.testKosmos
import kotlin.test.Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/onehanded/domain/interactor/OneHandedModeTileDataInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/onehanded/domain/interactor/OneHandedModeTileDataInteractorTest.kt
index 16b0caa78cfa..d0ea6913aef4 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/onehanded/domain/interactor/OneHandedModeTileDataInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/onehanded/domain/interactor/OneHandedModeTileDataInteractorTest.kt
@@ -22,7 +22,7 @@ import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.accessibility.data.repository.oneHandedModeRepository
import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
+import com.android.systemui.qs.tiles.base.domain.model.DataUpdateTrigger
import com.android.systemui.qs.tiles.impl.onehanded.domain.OneHandedModeTileDataInteractor
import com.android.systemui.testKosmos
import com.android.wm.shell.onehanded.OneHanded
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/onehanded/domain/interactor/OneHandedModeTileUserActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/onehanded/domain/interactor/OneHandedModeTileUserActionInteractorTest.kt
index 3f17d4cec643..a8f1808ba6f4 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/onehanded/domain/interactor/OneHandedModeTileUserActionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/onehanded/domain/interactor/OneHandedModeTileUserActionInteractorTest.kt
@@ -23,9 +23,9 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.accessibility.data.repository.FakeOneHandedModeRepository
-import com.android.systemui.qs.tiles.base.actions.FakeQSTileIntentUserInputHandler
-import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandlerSubject
-import com.android.systemui.qs.tiles.base.interactor.QSTileInputTestKtx
+import com.android.systemui.qs.tiles.base.domain.actions.FakeQSTileIntentUserInputHandler
+import com.android.systemui.qs.tiles.base.domain.actions.QSTileIntentUserInputHandlerSubject
+import com.android.systemui.qs.tiles.base.domain.model.QSTileInputTestKtx
import com.android.systemui.qs.tiles.impl.onehanded.domain.OneHandedModeTileUserActionInteractor
import com.android.systemui.qs.tiles.impl.onehanded.domain.model.OneHandedModeTileModel
import com.google.common.truth.Truth.assertThat
@@ -42,11 +42,7 @@ class OneHandedModeTileUserActionInteractorTest : SysuiTestCase() {
private val repository = FakeOneHandedModeRepository()
private val inputHandler = FakeQSTileIntentUserInputHandler()
- private val underTest =
- OneHandedModeTileUserActionInteractor(
- repository,
- inputHandler,
- )
+ private val underTest = OneHandedModeTileUserActionInteractor(repository, inputHandler)
@Test
fun handleClickWhenEnabled() = runTest {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/onehanded/ui/OneHandedModeTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/onehanded/ui/mapper/OneHandedModeTileMapperTest.kt
index 3fba857bae1a..fe10457b2103 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/onehanded/ui/OneHandedModeTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/onehanded/ui/mapper/OneHandedModeTileMapperTest.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2024 The Android Open Source Project
+ * Copyright (C) 2025 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.qs.tiles.impl.onehanded.ui
+package com.android.systemui.qs.tiles.impl.onehanded.ui.mapper
import android.graphics.drawable.TestStubDrawable
import android.widget.Switch
@@ -23,10 +23,10 @@ import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.qs.tileimpl.SubtitleArrayMapping
-import com.android.systemui.qs.tiles.impl.custom.QSTileStateSubject
+import com.android.systemui.qs.tiles.base.shared.model.QSTileState
+import com.android.systemui.qs.tiles.base.ui.model.QSTileStateSubject
import com.android.systemui.qs.tiles.impl.onehanded.domain.model.OneHandedModeTileModel
import com.android.systemui.qs.tiles.impl.onehanded.qsOneHandedModeTileConfig
-import com.android.systemui.qs.tiles.viewmodel.QSTileState
import com.android.systemui.res.R
import com.android.systemui.testKosmos
import org.junit.Before
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/qr/domain/interactor/QRCodeScannerTileDataInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/qr/domain/interactor/QRCodeScannerTileDataInteractorTest.kt
index c41ce2f7854c..82535db6ab4e 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/qr/domain/interactor/QRCodeScannerTileDataInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/qr/domain/interactor/QRCodeScannerTileDataInteractorTest.kt
@@ -24,7 +24,7 @@ import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.qrcodescanner.controller.QRCodeScannerController
import com.android.systemui.qrcodescanner.controller.QRCodeScannerController.Callback
-import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
+import com.android.systemui.qs.tiles.base.domain.model.DataUpdateTrigger
import com.android.systemui.qs.tiles.impl.qr.domain.model.QRCodeScannerTileModel
import com.android.systemui.util.mockito.argumentCaptor
import com.android.systemui.util.mockito.mock
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/qr/domain/interactor/QRCodeScannerTileUserActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/qr/domain/interactor/QRCodeScannerTileUserActionInteractorTest.kt
index ef21df6e2636..8c0dae80730c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/qr/domain/interactor/QRCodeScannerTileUserActionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/qr/domain/interactor/QRCodeScannerTileUserActionInteractorTest.kt
@@ -21,9 +21,9 @@ import android.platform.test.annotations.EnabledOnRavenwood
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
-import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandlerSubject
-import com.android.systemui.qs.tiles.base.actions.qsTileIntentUserInputHandler
-import com.android.systemui.qs.tiles.base.interactor.QSTileInputTestKtx
+import com.android.systemui.qs.tiles.base.domain.actions.QSTileIntentUserInputHandlerSubject
+import com.android.systemui.qs.tiles.base.domain.actions.qsTileIntentUserInputHandler
+import com.android.systemui.qs.tiles.base.domain.model.QSTileInputTestKtx
import com.android.systemui.qs.tiles.impl.qr.domain.model.QRCodeScannerTileModel
import com.android.systemui.qs.tiles.impl.qr.qrCodeScannerTileUserActionInteractor
import com.android.systemui.testKosmos
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/qr/ui/QRCodeScannerTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/qr/ui/mapper/QRCodeScannerTileMapperTest.kt
index 9e20aae6103e..8e453caf296d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/qr/ui/QRCodeScannerTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/qr/ui/mapper/QRCodeScannerTileMapperTest.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2024 The Android Open Source Project
+ * Copyright (C) 2025 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.qs.tiles.impl.qr.ui
+package com.android.systemui.qs.tiles.impl.qr.ui.mapper
import android.content.Intent
import android.graphics.drawable.TestStubDrawable
@@ -23,10 +23,11 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.common.shared.model.Icon
-import com.android.systemui.qs.tiles.impl.custom.QSTileStateSubject
+import com.android.systemui.qs.tiles.base.shared.model.QSTileState
+import com.android.systemui.qs.tiles.base.ui.model.QSTileStateSubject
import com.android.systemui.qs.tiles.impl.qr.domain.model.QRCodeScannerTileModel
import com.android.systemui.qs.tiles.impl.qr.qsQRCodeScannerTileConfig
-import com.android.systemui.qs.tiles.viewmodel.QSTileState
+import com.android.systemui.res.R
import com.android.systemui.testKosmos
import com.android.systemui.util.mockito.mock
import org.junit.Before
@@ -46,12 +47,7 @@ class QRCodeScannerTileMapperTest : SysuiTestCase() {
mapper =
QRCodeScannerTileMapper(
context.orCreateTestableResources
- .apply {
- addOverride(
- com.android.systemui.res.R.drawable.ic_qr_code_scanner,
- TestStubDrawable(),
- )
- }
+ .apply { addOverride(R.drawable.ic_qr_code_scanner, TestStubDrawable()) }
.resources,
context.theme,
)
@@ -91,9 +87,9 @@ class QRCodeScannerTileMapperTest : SysuiTestCase() {
val label = context.getString(com.android.systemui.res.R.string.qr_code_scanner_title)
return QSTileState(
Icon.Loaded(
- context.getDrawable(com.android.systemui.res.R.drawable.ic_qr_code_scanner)!!,
+ context.getDrawable(R.drawable.ic_qr_code_scanner)!!,
null,
- com.android.systemui.res.R.drawable.ic_qr_code_scanner,
+ R.drawable.ic_qr_code_scanner,
),
label,
activationState,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/reducebrightness/domain/interactor/ReduceBrightColorsTileDataInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/reducebrightness/domain/interactor/ReduceBrightColorsTileDataInteractorTest.kt
index fecb3deaaf36..db46a4f3fc2f 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/reducebrightness/domain/interactor/ReduceBrightColorsTileDataInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/reducebrightness/domain/interactor/ReduceBrightColorsTileDataInteractorTest.kt
@@ -24,7 +24,7 @@ import com.android.systemui.SysuiTestCase
import com.android.systemui.accessibility.reduceBrightColorsController
import com.android.systemui.coroutines.collectValues
import com.android.systemui.kosmos.testScope
-import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
+import com.android.systemui.qs.tiles.base.domain.model.DataUpdateTrigger
import com.android.systemui.qs.tiles.impl.reducebrightness.domain.model.ReduceBrightColorsTileModel
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/reducebrightness/domain/interactor/ReduceBrightColorsTileUserActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/reducebrightness/domain/interactor/ReduceBrightColorsTileUserActionInteractorTest.kt
index 77321385b09e..221a6f17bed0 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/reducebrightness/domain/interactor/ReduceBrightColorsTileUserActionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/reducebrightness/domain/interactor/ReduceBrightColorsTileUserActionInteractorTest.kt
@@ -28,9 +28,9 @@ import com.android.server.display.feature.flags.Flags
import com.android.systemui.SysuiTestCase
import com.android.systemui.accessibility.extradim.ExtraDimDialogManager
import com.android.systemui.accessibility.reduceBrightColorsController
-import com.android.systemui.qs.tiles.base.actions.FakeQSTileIntentUserInputHandler
-import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandlerSubject
-import com.android.systemui.qs.tiles.base.interactor.QSTileInputTestKtx
+import com.android.systemui.qs.tiles.base.domain.actions.FakeQSTileIntentUserInputHandler
+import com.android.systemui.qs.tiles.base.domain.actions.QSTileIntentUserInputHandlerSubject
+import com.android.systemui.qs.tiles.base.domain.model.QSTileInputTestKtx
import com.android.systemui.qs.tiles.impl.reducebrightness.domain.model.ReduceBrightColorsTileModel
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/reducebrightness/ui/ReduceBrightColorsTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/reducebrightness/ui/mapper/ReduceBrightColorsTileMapperTest.kt
index ebf70dad7149..dabf0b025281 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/reducebrightness/ui/ReduceBrightColorsTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/reducebrightness/ui/mapper/ReduceBrightColorsTileMapperTest.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2024 The Android Open Source Project
+ * Copyright (C) 2025 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.qs.tiles.impl.reducebrightness.ui
+package com.android.systemui.qs.tiles.impl.reducebrightness.ui.mapper
import android.graphics.drawable.TestStubDrawable
import android.service.quicksettings.Tile
@@ -23,10 +23,10 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.common.shared.model.Icon
-import com.android.systemui.qs.tiles.impl.custom.QSTileStateSubject
+import com.android.systemui.qs.tiles.base.shared.model.QSTileState
+import com.android.systemui.qs.tiles.base.ui.model.QSTileStateSubject
import com.android.systemui.qs.tiles.impl.reducebrightness.domain.model.ReduceBrightColorsTileModel
import com.android.systemui.qs.tiles.impl.reducebrightness.qsReduceBrightColorsTileConfig
-import com.android.systemui.qs.tiles.viewmodel.QSTileState
import com.android.systemui.res.R
import com.android.systemui.testKosmos
import org.junit.Before
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/rotation/domain/interactor/RotationLockTileDataInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/rotation/domain/interactor/RotationLockTileDataInteractorTest.kt
index 6f05c3178754..ef67910f8799 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/rotation/domain/interactor/RotationLockTileDataInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/rotation/domain/interactor/RotationLockTileDataInteractorTest.kt
@@ -29,7 +29,7 @@ import com.android.systemui.camera.data.repository.fakeCameraAutoRotateRepositor
import com.android.systemui.camera.data.repository.fakeCameraSensorPrivacyRepository
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.kosmos.testScope
-import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
+import com.android.systemui.qs.tiles.base.domain.model.DataUpdateTrigger
import com.android.systemui.testKosmos
import com.android.systemui.util.mockito.eq
import com.android.systemui.util.mockito.whenever
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/rotation/domain/interactor/RotationLockTileUserActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/rotation/domain/interactor/RotationLockTileUserActionInteractorTest.kt
index 1653ce369ea1..e60e2106e556 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/rotation/domain/interactor/RotationLockTileUserActionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/rotation/domain/interactor/RotationLockTileUserActionInteractorTest.kt
@@ -22,9 +22,9 @@ import android.testing.LeakCheck
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
-import com.android.systemui.qs.tiles.base.actions.FakeQSTileIntentUserInputHandler
-import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandlerSubject
-import com.android.systemui.qs.tiles.base.interactor.QSTileInputTestKtx
+import com.android.systemui.qs.tiles.base.domain.actions.FakeQSTileIntentUserInputHandler
+import com.android.systemui.qs.tiles.base.domain.actions.QSTileIntentUserInputHandlerSubject
+import com.android.systemui.qs.tiles.base.domain.model.QSTileInputTestKtx
import com.android.systemui.qs.tiles.impl.rotation.domain.model.RotationLockTileModel
import com.android.systemui.utils.leaks.FakeRotationLockController
import com.google.common.truth.Truth.assertThat
@@ -39,11 +39,7 @@ class RotationLockTileUserActionInteractorTest : SysuiTestCase() {
private val controller = FakeRotationLockController(LeakCheck())
private val inputHandler = FakeQSTileIntentUserInputHandler()
- private val underTest =
- RotationLockTileUserActionInteractor(
- controller,
- inputHandler,
- )
+ private val underTest = RotationLockTileUserActionInteractor(controller, inputHandler)
@Test
fun handleClickWhenEnabled() = runTest {
@@ -69,14 +65,7 @@ class RotationLockTileUserActionInteractorTest : SysuiTestCase() {
fun handleLongClickWhenDisabled() = runTest {
val enabled = false
- underTest.handleInput(
- QSTileInputTestKtx.longClick(
- RotationLockTileModel(
- enabled,
- false,
- )
- )
- )
+ underTest.handleInput(QSTileInputTestKtx.longClick(RotationLockTileModel(enabled, false)))
QSTileIntentUserInputHandlerSubject.assertThat(inputHandler).handledOneIntentInput {
assertThat(it.intent.action).isEqualTo(Settings.ACTION_AUTO_ROTATE_SETTINGS)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/rotation/ui/mapper/RotationLockTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/rotation/ui/mapper/RotationLockTileMapperTest.kt
index 0e6aaa6320c8..92c7a1ea76a2 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/rotation/ui/mapper/RotationLockTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/rotation/ui/mapper/RotationLockTileMapperTest.kt
@@ -25,10 +25,10 @@ import com.android.systemui.common.shared.model.Icon
import com.android.systemui.defaultDeviceState
import com.android.systemui.deviceStateManager
import com.android.systemui.foldedDeviceStateList
-import com.android.systemui.qs.tiles.impl.custom.QSTileStateSubject
+import com.android.systemui.qs.tiles.base.shared.model.QSTileState
+import com.android.systemui.qs.tiles.base.ui.model.QSTileStateSubject
import com.android.systemui.qs.tiles.impl.rotation.domain.model.RotationLockTileModel
import com.android.systemui.qs.tiles.impl.rotation.qsRotationLockTileConfig
-import com.android.systemui.qs.tiles.viewmodel.QSTileState
import com.android.systemui.res.R
import com.android.systemui.statusbar.policy.DevicePostureController
import com.android.systemui.statusbar.policy.devicePostureController
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/saver/domain/interactor/DataSaverTileDataInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/saver/domain/interactor/DataSaverTileDataInteractorTest.kt
index c286ea7a5062..97bd199780a0 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/saver/domain/interactor/DataSaverTileDataInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/saver/domain/interactor/DataSaverTileDataInteractorTest.kt
@@ -24,7 +24,7 @@ import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.coroutines.collectValues
-import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
+import com.android.systemui.qs.tiles.base.domain.model.DataUpdateTrigger
import com.android.systemui.qs.tiles.impl.saver.domain.model.DataSaverTileModel
import com.android.systemui.utils.leaks.FakeDataSaverController
import com.google.common.truth.Truth
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/saver/domain/interactor/DataSaverTileUserActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/saver/domain/interactor/DataSaverTileUserActionInteractorTest.kt
index 87ac034a6316..ee57b25a15ed 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/saver/domain/interactor/DataSaverTileUserActionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/saver/domain/interactor/DataSaverTileUserActionInteractorTest.kt
@@ -24,9 +24,9 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.animation.DialogTransitionAnimator
-import com.android.systemui.qs.tiles.base.actions.FakeQSTileIntentUserInputHandler
-import com.android.systemui.qs.tiles.base.actions.intentInputs
-import com.android.systemui.qs.tiles.base.interactor.QSTileInputTestKtx
+import com.android.systemui.qs.tiles.base.domain.actions.FakeQSTileIntentUserInputHandler
+import com.android.systemui.qs.tiles.base.domain.actions.intentInputs
+import com.android.systemui.qs.tiles.base.domain.model.QSTileInputTestKtx
import com.android.systemui.qs.tiles.impl.saver.domain.model.DataSaverTileModel
import com.android.systemui.settings.UserFileManager
import com.android.systemui.shade.domain.interactor.FakeShadeDialogContextInteractor
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/saver/domain/DataSaverTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/saver/ui/mapper/DataSaverTileMapperTest.kt
index 079921640705..638b76d685f3 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/saver/domain/DataSaverTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/saver/ui/mapper/DataSaverTileMapperTest.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2025 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.qs.tiles.impl.saver.domain
+package com.android.systemui.qs.tiles.impl.saver.ui.mapper
import android.graphics.drawable.TestStubDrawable
import android.widget.Switch
@@ -22,10 +22,10 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.common.shared.model.Icon
-import com.android.systemui.qs.tiles.impl.custom.QSTileStateSubject
+import com.android.systemui.qs.tiles.base.shared.model.QSTileState
+import com.android.systemui.qs.tiles.base.ui.model.QSTileStateSubject
import com.android.systemui.qs.tiles.impl.saver.domain.model.DataSaverTileModel
import com.android.systemui.qs.tiles.impl.saver.qsDataSaverTileConfig
-import com.android.systemui.qs.tiles.viewmodel.QSTileState
import com.android.systemui.res.R
import com.android.systemui.testKosmos
import org.junit.Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/interactor/ScreenRecordTileDataInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/interactor/ScreenRecordTileDataInteractorTest.kt
index 127160d8728d..0c98271a70fc 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/interactor/ScreenRecordTileDataInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/interactor/ScreenRecordTileDataInteractorTest.kt
@@ -23,7 +23,7 @@ import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.kosmos.testScope
-import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
+import com.android.systemui.qs.tiles.base.domain.model.DataUpdateTrigger
import com.android.systemui.screenrecord.data.model.ScreenRecordModel
import com.android.systemui.screenrecord.data.repository.screenRecordRepository
import com.android.systemui.testKosmos
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/interactor/ScreenRecordTileUserActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/interactor/ScreenRecordTileUserActionInteractorTest.kt
index f47e0578461a..9ffffa4b7669 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/interactor/ScreenRecordTileUserActionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/interactor/ScreenRecordTileUserActionInteractorTest.kt
@@ -30,7 +30,7 @@ import com.android.systemui.kosmos.testScope
import com.android.systemui.mediaprojection.MediaProjectionMetricsLogger
import com.android.systemui.plugins.ActivityStarter.OnDismissAction
import com.android.systemui.qs.pipeline.domain.interactor.PanelInteractor
-import com.android.systemui.qs.tiles.base.interactor.QSTileInputTestKtx
+import com.android.systemui.qs.tiles.base.domain.model.QSTileInputTestKtx
import com.android.systemui.screenrecord.RecordingController
import com.android.systemui.screenrecord.data.model.ScreenRecordModel
import com.android.systemui.screenrecord.data.repository.ScreenRecordRepositoryImpl
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/screenrecord/ui/ScreenRecordTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/screenrecord/ui/mapper/ScreenRecordTileMapperTest.kt
index d118c096fd16..6acfc4441027 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/screenrecord/ui/ScreenRecordTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/screenrecord/ui/mapper/ScreenRecordTileMapperTest.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.qs.tiles.impl.screenrecord.ui
+package com.android.systemui.qs.tiles.impl.screenrecord.ui.mapper
import android.graphics.drawable.TestStubDrawable
import android.text.TextUtils
@@ -23,10 +23,10 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.common.shared.model.Icon
-import com.android.systemui.qs.tiles.impl.custom.QSTileStateSubject
-import com.android.systemui.qs.tiles.impl.screenrecord.domain.ui.ScreenRecordTileMapper
+import com.android.systemui.qs.tiles.base.shared.model.QSTileState
+import com.android.systemui.qs.tiles.base.ui.model.QSTileStateSubject
+import com.android.systemui.qs.tiles.impl.screenrecord.domain.ui.mapper.ScreenRecordTileMapper
import com.android.systemui.qs.tiles.impl.screenrecord.qsScreenRecordTileConfig
-import com.android.systemui.qs.tiles.viewmodel.QSTileState
import com.android.systemui.res.R
import com.android.systemui.screenrecord.data.model.ScreenRecordModel
import com.android.systemui.testKosmos
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/sensorprivacy/domain/interactor/SensorPrivacyToggleTileDataInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/sensorprivacy/domain/interactor/SensorPrivacyToggleTileDataInteractorTest.kt
index 4a28fc02042d..7d1d7d868c8e 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/sensorprivacy/domain/interactor/SensorPrivacyToggleTileDataInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/sensorprivacy/domain/interactor/SensorPrivacyToggleTileDataInteractorTest.kt
@@ -24,8 +24,7 @@ import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.kosmos.testScope
-import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
-import com.android.systemui.qs.tiles.impl.sensorprivacy.SensorPrivacyToggleTileDataInteractor
+import com.android.systemui.qs.tiles.base.domain.model.DataUpdateTrigger
import com.android.systemui.statusbar.policy.IndividualSensorPrivacyController
import com.android.systemui.testKosmos
import com.android.systemui.util.mockito.argumentCaptor
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/sensorprivacy/domain/interactor/SensorPrivacyToggleTileUserActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/sensorprivacy/domain/interactor/SensorPrivacyToggleTileUserActionInteractorTest.kt
index 7856fcaf4a1b..2630fddcbc88 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/sensorprivacy/domain/interactor/SensorPrivacyToggleTileUserActionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/sensorprivacy/domain/interactor/SensorPrivacyToggleTileUserActionInteractorTest.kt
@@ -27,10 +27,9 @@ import com.android.systemui.SysuiTestCase
import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
import com.android.systemui.plugins.activityStarter
-import com.android.systemui.qs.tiles.base.actions.FakeQSTileIntentUserInputHandler
-import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandlerSubject
-import com.android.systemui.qs.tiles.base.interactor.QSTileInputTestKtx
-import com.android.systemui.qs.tiles.impl.sensorprivacy.domain.SensorPrivacyToggleTileUserActionInteractor
+import com.android.systemui.qs.tiles.base.domain.actions.FakeQSTileIntentUserInputHandler
+import com.android.systemui.qs.tiles.base.domain.actions.QSTileIntentUserInputHandlerSubject
+import com.android.systemui.qs.tiles.base.domain.model.QSTileInputTestKtx
import com.android.systemui.qs.tiles.impl.sensorprivacy.domain.model.SensorPrivacyToggleTileModel
import com.android.systemui.statusbar.policy.IndividualSensorPrivacyController
import com.android.systemui.testKosmos
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/sensorprivacy/ui/SensorPrivacyToggleTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/sensorprivacy/ui/mapper/SensorPrivacyToggleTileMapperTest.kt
index 3b810dc451f9..d9a30b235bb2 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/sensorprivacy/ui/SensorPrivacyToggleTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/sensorprivacy/ui/mapper/SensorPrivacyToggleTileMapperTest.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2024 The Android Open Source Project
+ * Copyright (C) 2025 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.qs.tiles.impl.sensorprivacy.ui
+package com.android.systemui.qs.tiles.impl.sensorprivacy.ui.mapper
import android.graphics.drawable.TestStubDrawable
import android.hardware.SensorPrivacyManager.Sensors.CAMERA
@@ -24,13 +24,14 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.common.shared.model.Icon
-import com.android.systemui.qs.tiles.impl.custom.QSTileStateSubject
+import com.android.systemui.qs.tiles.base.shared.model.QSTileState
+import com.android.systemui.qs.tiles.base.ui.model.QSTileStateSubject
import com.android.systemui.qs.tiles.impl.sensorprivacy.domain.model.SensorPrivacyToggleTileModel
import com.android.systemui.qs.tiles.impl.sensorprivacy.qsCameraSensorPrivacyToggleTileConfig
import com.android.systemui.qs.tiles.impl.sensorprivacy.qsMicrophoneSensorPrivacyToggleTileConfig
-import com.android.systemui.qs.tiles.impl.sensorprivacy.ui.SensorPrivacyTileResources.CameraPrivacyTileResources
-import com.android.systemui.qs.tiles.impl.sensorprivacy.ui.SensorPrivacyTileResources.MicrophonePrivacyTileResources
-import com.android.systemui.qs.tiles.viewmodel.QSTileState
+import com.android.systemui.qs.tiles.impl.sensorprivacy.ui.model.SensorPrivacyTileResources
+import com.android.systemui.qs.tiles.impl.sensorprivacy.ui.model.SensorPrivacyTileResources.CameraPrivacyTileResources
+import com.android.systemui.qs.tiles.impl.sensorprivacy.ui.model.SensorPrivacyTileResources.MicrophonePrivacyTileResources
import com.android.systemui.res.R
import com.android.systemui.testKosmos
import org.junit.Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/uimodenight/domain/UiModeNightTileDataInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/uimodenight/domain/interactor/UiModeNightTileDataInteractorTest.kt
index 96538b730354..36a60c4157a2 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/uimodenight/domain/UiModeNightTileDataInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/uimodenight/domain/interactor/UiModeNightTileDataInteractorTest.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2025 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.qs.tiles.impl.uimodenight.domain
+package com.android.systemui.qs.tiles.impl.uimodenight.domain.interactor
import android.app.UiModeManager
import android.content.res.Configuration
@@ -27,9 +27,8 @@ import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.coroutines.collectValues
-import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
-import com.android.systemui.qs.tiles.impl.uimodenight.domain.interactor.UiModeNightTileDataInteractor
-import com.android.systemui.qs.tiles.impl.uimodenight.domain.model.UiModeNightTileModel
+import com.android.systemui.qs.tiles.base.domain.model.DataUpdateTrigger
+import com.android.systemui.qs.tiles.impl.uimodenight.domain.interactor.model.UiModeNightTileModel
import com.android.systemui.statusbar.phone.ConfigurationControllerImpl
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.util.mockito.mock
@@ -77,7 +76,7 @@ class UiModeNightTileDataInteractorTest : SysuiTestCase() {
uiModeManager,
batteryController,
locationController,
- dateFormatUtil
+ dateFormatUtil,
)
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/uimodenight/domain/UiModeNightTileUserActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/uimodenight/domain/interactor/UiModeNightTileUserActionInteractorTest.kt
index eea6d161d4ff..894a6cb62ce5 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/uimodenight/domain/UiModeNightTileUserActionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/uimodenight/domain/interactor/UiModeNightTileUserActionInteractorTest.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2025 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.qs.tiles.impl.uimodenight.domain
+package com.android.systemui.qs.tiles.impl.uimodenight.domain.interactor
import android.app.UiModeManager
import android.platform.test.annotations.EnabledOnRavenwood
@@ -22,11 +22,10 @@ import android.provider.Settings
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
-import com.android.systemui.qs.tiles.base.actions.FakeQSTileIntentUserInputHandler
-import com.android.systemui.qs.tiles.base.actions.intentInputs
-import com.android.systemui.qs.tiles.base.interactor.QSTileInputTestKtx
+import com.android.systemui.qs.tiles.base.domain.actions.FakeQSTileIntentUserInputHandler
+import com.android.systemui.qs.tiles.base.domain.actions.intentInputs
+import com.android.systemui.qs.tiles.base.domain.model.QSTileInputTestKtx
import com.android.systemui.qs.tiles.impl.uimodenight.UiModeNightTileModelHelper.createModel
-import com.android.systemui.qs.tiles.impl.uimodenight.domain.interactor.UiModeNightTileUserActionInteractor
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.mock
import com.google.common.truth.Truth
@@ -57,7 +56,7 @@ class UiModeNightTileUserActionInteractorTest : SysuiTestCase() {
UiModeNightTileUserActionInteractor(
EmptyCoroutineContext,
uiModeManager,
- qsTileIntentUserActionHandler
+ qsTileIntentUserActionHandler,
)
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/uimodenight/domain/UiModeNightTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/uimodenight/ui/mapper/UiModeNightTileMapperTest.kt
index 547bbbc895a6..e7653f238576 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/uimodenight/domain/UiModeNightTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/uimodenight/ui/mapper/UiModeNightTileMapperTest.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.qs.tiles.impl.uimodenight.domain
+package com.android.systemui.qs.tiles.impl.uimodenight.ui.mapper
import android.app.UiModeManager
import android.graphics.drawable.TestStubDrawable
@@ -25,10 +25,10 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.common.shared.model.Icon
-import com.android.systemui.qs.tiles.impl.custom.QSTileStateSubject
+import com.android.systemui.qs.tiles.base.shared.model.QSTileState
+import com.android.systemui.qs.tiles.base.ui.model.QSTileStateSubject
import com.android.systemui.qs.tiles.impl.uimodenight.UiModeNightTileModelHelper.createModel
import com.android.systemui.qs.tiles.impl.uimodenight.qsUiModeNightTileConfig
-import com.android.systemui.qs.tiles.viewmodel.QSTileState
import com.android.systemui.res.R
import com.android.systemui.testKosmos
import kotlin.reflect.KClass
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/work/domain/interactor/WorkModeTileDataInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/work/domain/interactor/WorkModeTileDataInteractorTest.kt
index 86513006cef1..4f594167cdfd 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/work/domain/interactor/WorkModeTileDataInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/work/domain/interactor/WorkModeTileDataInteractorTest.kt
@@ -23,7 +23,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
+import com.android.systemui.qs.tiles.base.domain.model.DataUpdateTrigger
import com.android.systemui.qs.tiles.impl.work.domain.model.WorkModeTileModel
import com.android.systemui.utils.leaks.FakeManagedProfileController
import com.google.common.truth.Truth.assertThat
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/work/domain/interactor/WorkModeTileUserActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/work/domain/interactor/WorkModeTileUserActionInteractorTest.kt
index 8a63e2c8800f..3d08a01bc943 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/work/domain/interactor/WorkModeTileUserActionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/work/domain/interactor/WorkModeTileUserActionInteractorTest.kt
@@ -22,9 +22,9 @@ import android.testing.LeakCheck
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
-import com.android.systemui.qs.tiles.base.actions.FakeQSTileIntentUserInputHandler
-import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandlerSubject
-import com.android.systemui.qs.tiles.base.interactor.QSTileInputTestKtx
+import com.android.systemui.qs.tiles.base.domain.actions.FakeQSTileIntentUserInputHandler
+import com.android.systemui.qs.tiles.base.domain.actions.QSTileIntentUserInputHandlerSubject
+import com.android.systemui.qs.tiles.base.domain.model.QSTileInputTestKtx
import com.android.systemui.qs.tiles.impl.work.domain.model.WorkModeTileModel
import com.android.systemui.utils.leaks.FakeManagedProfileController
import com.google.common.truth.Truth.assertThat
@@ -40,11 +40,7 @@ class WorkModeTileUserActionInteractorTest : SysuiTestCase() {
private val inputHandler = FakeQSTileIntentUserInputHandler()
private val profileController = FakeManagedProfileController(LeakCheck())
- private val underTest =
- WorkModeTileUserActionInteractor(
- profileController,
- inputHandler,
- )
+ private val underTest = WorkModeTileUserActionInteractor(profileController, inputHandler)
@Test
fun handleClickWhenEnabled() = runTest {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/work/ui/WorkModeTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/work/ui/mapper/WorkModeTileMapperTest.kt
index ed3fc5058c44..b420d44c7ecc 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/work/ui/WorkModeTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/work/ui/mapper/WorkModeTileMapperTest.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2024 The Android Open Source Project
+ * Copyright (C) 2025 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.qs.tiles.impl.work.ui
+package com.android.systemui.qs.tiles.impl.work.ui.mapper
import android.app.admin.DevicePolicyResources
import android.app.admin.DevicePolicyResourcesManager
@@ -26,10 +26,10 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.common.shared.model.Icon
-import com.android.systemui.qs.tiles.impl.custom.QSTileStateSubject
+import com.android.systemui.qs.tiles.base.shared.model.QSTileState
+import com.android.systemui.qs.tiles.base.ui.model.QSTileStateSubject
import com.android.systemui.qs.tiles.impl.work.domain.model.WorkModeTileModel
import com.android.systemui.qs.tiles.impl.work.qsWorkModeTileConfig
-import com.android.systemui.qs.tiles.viewmodel.QSTileState
import com.android.systemui.res.R
import com.android.systemui.testKosmos
import com.android.systemui.util.mockito.any
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsContainerViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsContainerViewModelTest.kt
index 701e55d0759d..d11701570e32 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsContainerViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsContainerViewModelTest.kt
@@ -95,11 +95,21 @@ class QuickSettingsContainerViewModelTest : SysuiTestCase() {
}
@Test
- fun showMedia_noActiveMedia_false() =
+ fun showMedia_InactiveMedia_true() =
testScope.runTest {
kosmos.mediaFilterRepository.addSelectedUserMediaEntry(MediaData(active = false))
runCurrent()
+ assertThat(underTest.showMedia).isTrue()
+ }
+
+ @Test
+ fun showMedia_noMedia_false() =
+ testScope.runTest {
+ kosmos.mediaFilterRepository.addSelectedUserMediaEntry(MediaData(active = true))
+ kosmos.mediaFilterRepository.clearSelectedUserMedia()
+ runCurrent()
+
assertThat(underTest.showMedia).isFalse()
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayActionsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayActionsViewModelTest.kt
index b98059a1fe90..882d296d94b4 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayActionsViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayActionsViewModelTest.kt
@@ -22,8 +22,7 @@ import androidx.test.filters.SmallTest
import com.android.compose.animation.scene.Back
import com.android.compose.animation.scene.Swipe
import com.android.compose.animation.scene.UserActionResult.HideOverlay
-import com.android.compose.animation.scene.UserActionResult.ShowOverlay
-import com.android.compose.animation.scene.UserActionResult.ShowOverlay.HideCurrentOverlays
+import com.android.compose.animation.scene.UserActionResult.ReplaceByOverlay
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.flags.EnableSceneContainer
@@ -90,11 +89,8 @@ class QuickSettingsShadeOverlayActionsViewModelTest : SysuiTestCase() {
underTest.activateIn(this)
val action =
- (actions?.get(Swipe.Down(fromSource = SceneContainerArea.StartHalf))
- as? ShowOverlay)
+ (actions?.get(Swipe.Down(fromSource = SceneContainerArea.TopEdgeStartHalf))
+ as? ReplaceByOverlay)
assertThat(action?.overlay).isEqualTo(Overlays.NotificationsShade)
- val overlaysToHide = action?.hideCurrentOverlays as? HideCurrentOverlays.Some
- assertThat(overlaysToHide).isNotNull()
- assertThat(overlaysToHide?.overlays).containsExactly(Overlays.QuickSettingsShade)
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt
index 1743e056b65c..6d4fffdefb1b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt
@@ -22,7 +22,6 @@ import android.os.PowerManager
import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
import android.provider.Settings
-import android.view.Display
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.compose.animation.scene.ObservableTransitionState
@@ -1213,15 +1212,15 @@ class SceneContainerStartableTest : SysuiTestCase() {
fakeSceneDataSource.pause()
sceneInteractor.changeScene(sceneKey, "reason")
runCurrent()
- verify(sysUiState, times(index)).commitUpdate(Display.DEFAULT_DISPLAY)
+ verify(sysUiState, times(index)).commitUpdate()
fakeSceneDataSource.unpause(expectedScene = sceneKey)
runCurrent()
- verify(sysUiState, times(index)).commitUpdate(Display.DEFAULT_DISPLAY)
+ verify(sysUiState, times(index)).commitUpdate()
transitionStateFlow.value = ObservableTransitionState.Idle(sceneKey)
runCurrent()
- verify(sysUiState, times(index + 1)).commitUpdate(Display.DEFAULT_DISPLAY)
+ verify(sysUiState, times(index + 1)).commitUpdate()
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerSwipeDetectorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerSwipeDetectorTest.kt
index a09e5cd9de9b..7536bb3cc02a 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerSwipeDetectorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerSwipeDetectorTest.kt
@@ -32,6 +32,8 @@ import com.android.systemui.scene.ui.viewmodel.SceneContainerArea.Resolved.LeftE
import com.android.systemui.scene.ui.viewmodel.SceneContainerArea.Resolved.LeftHalf
import com.android.systemui.scene.ui.viewmodel.SceneContainerArea.Resolved.RightEdge
import com.android.systemui.scene.ui.viewmodel.SceneContainerArea.Resolved.RightHalf
+import com.android.systemui.scene.ui.viewmodel.SceneContainerArea.Resolved.TopEdgeLeftHalf
+import com.android.systemui.scene.ui.viewmodel.SceneContainerArea.Resolved.TopEdgeRightHalf
import com.android.systemui.scene.ui.viewmodel.SceneContainerArea.StartEdge
import com.android.systemui.scene.ui.viewmodel.SceneContainerArea.StartHalf
import com.google.common.truth.Truth.assertThat
@@ -55,9 +57,9 @@ class SceneContainerSwipeDetectorTest : SysuiTestCase() {
}
@Test
- fun source_swipeVerticallyOnTopLeft_detectsLeftHalf() {
+ fun source_swipeVerticallyOnTopLeft_detectsTopEdgeLeftHalf() {
val detectedEdge = swipeVerticallyFrom(x = 1, y = edgeSize - 1)
- assertThat(detectedEdge).isEqualTo(LeftHalf)
+ assertThat(detectedEdge).isEqualTo(TopEdgeLeftHalf)
}
@Test
@@ -69,7 +71,7 @@ class SceneContainerSwipeDetectorTest : SysuiTestCase() {
@Test
fun source_swipeVerticallyOnTopRight_detectsRightHalf() {
val detectedEdge = swipeVerticallyFrom(x = screenWidth - 1, y = edgeSize - 1)
- assertThat(detectedEdge).isEqualTo(RightHalf)
+ assertThat(detectedEdge).isEqualTo(TopEdgeRightHalf)
}
@Test
@@ -79,14 +81,26 @@ class SceneContainerSwipeDetectorTest : SysuiTestCase() {
}
@Test
- fun source_swipeVerticallyToLeftOfSplit_detectsLeftHalf() {
+ fun source_swipeVerticallyOnTopEdge_ToLeftOfSplit_detectsTopEdgeLeftHalf() {
val detectedEdge = swipeVerticallyFrom(x = (screenWidth / 2) - 1, y = edgeSize - 1)
+ assertThat(detectedEdge).isEqualTo(TopEdgeLeftHalf)
+ }
+
+ @Test
+ fun source_swipeVerticallyBelowTopEdge_ToLeftOfSplit_detectsLeftHalf() {
+ val detectedEdge = swipeVerticallyFrom(x = (screenWidth / 2) - 1, y = edgeSize + 1)
assertThat(detectedEdge).isEqualTo(LeftHalf)
}
@Test
- fun source_swipeVerticallyToRightOfSplit_detectsRightHalf() {
+ fun source_swipeVerticallyOnTopEdge_toRightOfSplit_detectsTopEdgeRightHalf() {
val detectedEdge = swipeVerticallyFrom(x = (screenWidth / 2) + 1, y = edgeSize - 1)
+ assertThat(detectedEdge).isEqualTo(TopEdgeRightHalf)
+ }
+
+ @Test
+ fun source_swipeVerticallyBelowTopEdge_toRightOfSplit_detectsRightHalf() {
+ val detectedEdge = swipeVerticallyFrom(x = (screenWidth / 2) + 1, y = edgeSize + 1)
assertThat(detectedEdge).isEqualTo(RightHalf)
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
index 94db429c2225..676e1ea5321a 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
@@ -172,6 +172,7 @@ import com.android.systemui.user.domain.interactor.UserSwitcherInteractor;
import com.android.systemui.util.kotlin.JavaAdapter;
import com.android.systemui.util.time.FakeSystemClock;
import com.android.systemui.util.time.SystemClock;
+import com.android.systemui.utils.windowmanager.WindowManagerProvider;
import com.android.systemui.wallpapers.ui.viewmodel.WallpaperFocalAreaViewModel;
import com.android.wm.shell.animation.FlingAnimationUtils;
@@ -296,6 +297,7 @@ public class NotificationPanelViewControllerBaseTest extends SysuiTestCase {
@Mock private LargeScreenHeaderHelper mLargeScreenHeaderHelper;
@Mock private StatusBarLongPressGestureDetector mStatusBarLongPressGestureDetector;
@Mock protected SysUIStateDisplaysInteractor mSysUIStateDisplaysInteractor;
+ @Mock private WindowManagerProvider mWindowManagerProvider;
protected final int mMaxUdfpsBurnInOffsetY = 5;
protected FakeFeatureFlagsClassic mFeatureFlags = new FakeFeatureFlagsClassic();
protected KeyguardClockInteractor mKeyguardClockInteractor;
@@ -672,7 +674,8 @@ public class NotificationPanelViewControllerBaseTest extends SysuiTestCase {
mCastController,
new ResourcesSplitShadeStateController(),
() -> mKosmos.getCommunalTransitionViewModel(),
- () -> mLargeScreenHeaderHelper
+ () -> mLargeScreenHeaderHelper,
+ mWindowManagerProvider
);
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java
index 3788049256a2..fd17b7a32fd3 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java
@@ -50,7 +50,6 @@ import android.view.WindowManager;
import androidx.test.filters.SmallTest;
-import com.android.app.viewcapture.ViewCaptureAwareWindowManager;
import com.android.internal.colorextraction.ColorExtractor;
import com.android.systemui.Flags;
import com.android.systemui.SysuiTestCase;
@@ -100,7 +99,7 @@ public class NotificationShadeWindowControllerImplTest extends SysuiTestCase {
@Rule public final CheckFlagsRule checkFlagsRule =
DeviceFlagsValueProvider.createCheckFlagsRule();
- @Mock private ViewCaptureAwareWindowManager mWindowManager;
+ @Mock private WindowManager mWindowManager;
@Mock private DozeParameters mDozeParameters;
@Spy private final NotificationShadeWindowView mNotificationShadeWindowView = spy(
new NotificationShadeWindowView(mContext, null));
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/QuickSettingsControllerImplBaseTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/QuickSettingsControllerImplBaseTest.java
index 7433267ab3b0..db0c07c50dc6 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/QuickSettingsControllerImplBaseTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/QuickSettingsControllerImplBaseTest.java
@@ -28,6 +28,7 @@ import android.os.Handler;
import android.os.Looper;
import android.view.ViewGroup;
import android.view.ViewParent;
+import android.view.WindowManager;
import android.view.accessibility.AccessibilityManager;
import com.android.internal.logging.MetricsLogger;
@@ -79,6 +80,7 @@ import com.android.systemui.statusbar.policy.data.repository.FakeUserSetupReposi
import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
import com.android.systemui.user.domain.interactor.UserSwitcherInteractor;
import com.android.systemui.util.kotlin.JavaAdapter;
+import com.android.systemui.utils.windowmanager.WindowManagerProvider;
import dagger.Lazy;
@@ -147,6 +149,7 @@ public class QuickSettingsControllerImplBaseTest extends SysuiTestCase {
@Mock protected UserSwitcherInteractor mUserSwitcherInteractor;
@Mock protected SelectedUserInteractor mSelectedUserInteractor;
@Mock protected LargeScreenHeaderHelper mLargeScreenHeaderHelper;
+ @Mock protected WindowManagerProvider mWindowManagerProvider;
protected FakeDisableFlagsRepository mDisableFlagsRepository =
mKosmos.getFakeDisableFlagsRepository();
@@ -232,6 +235,9 @@ public class QuickSettingsControllerImplBaseTest extends SysuiTestCase {
mMainHandler = new Handler(Looper.getMainLooper());
+ WindowManager wm = mContext.getSystemService(WindowManager.class);
+ when(mWindowManagerProvider.getWindowManager(mContext)).thenReturn(wm);
+
mQsController = new QuickSettingsControllerImpl(
mPanelViewControllerLazy,
mPanelView,
@@ -268,7 +274,8 @@ public class QuickSettingsControllerImplBaseTest extends SysuiTestCase {
mCastController,
splitShadeStateController,
() -> mKosmos.getCommunalTransitionViewModel(),
- () -> mLargeScreenHeaderHelper
+ () -> mLargeScreenHeaderHelper,
+ mWindowManagerProvider
);
mQsController.init();
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ShadeControllerSceneImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ShadeControllerSceneImplTest.kt
index 32eec56af8ea..b376558371f3 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ShadeControllerSceneImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ShadeControllerSceneImplTest.kt
@@ -21,6 +21,7 @@ import androidx.test.filters.SmallTest
import com.android.compose.animation.scene.ObservableTransitionState
import com.android.compose.animation.scene.SceneKey
import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor
import com.android.systemui.flags.EnableSceneContainer
import com.android.systemui.flags.Flags
@@ -30,8 +31,10 @@ import com.android.systemui.keyguard.shared.model.SuccessFingerprintAuthenticati
import com.android.systemui.kosmos.testCase
import com.android.systemui.kosmos.testScope
import com.android.systemui.scene.domain.interactor.sceneInteractor
+import com.android.systemui.scene.shared.model.Overlays
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.shade.domain.interactor.ShadeInteractor
+import com.android.systemui.shade.domain.interactor.enableDualShade
import com.android.systemui.shade.domain.interactor.shadeInteractor
import com.android.systemui.statusbar.CommandQueue
import com.android.systemui.testKosmos
@@ -177,6 +180,40 @@ class ShadeControllerSceneImplTest : SysuiTestCase() {
verify(testRunnable, times(1)).run()
}
+ @Test
+ fun instantCollapseShade_notificationShadeHidden() =
+ testScope.runTest {
+ val currentOverlays by collectLastValue(sceneInteractor.currentOverlays)
+ // GIVEN the dual shade configuration with the notification shade overlay visible
+ kosmos.enableDualShade()
+ runCurrent()
+ sceneInteractor.showOverlay(Overlays.NotificationsShade, "test")
+ assertThat(currentOverlays).isEqualTo(setOf(Overlays.NotificationsShade))
+
+ // WHEN shade instantly collapses
+ underTest.instantCollapseShade()
+
+ // THEN overlay was hidden
+ assertThat(currentOverlays).isEmpty()
+ }
+
+ @Test
+ fun instantCollapseShade_qsShadeHidden() =
+ testScope.runTest {
+ val currentOverlays by collectLastValue(sceneInteractor.currentOverlays)
+ // GIVEN the dual shade configuration with the QS shade overlay visible
+ kosmos.enableDualShade()
+ runCurrent()
+ sceneInteractor.showOverlay(Overlays.QuickSettingsShade, "test")
+ assertThat(currentOverlays).isEqualTo(setOf(Overlays.QuickSettingsShade))
+
+ // WHEN shade instantly collapses
+ underTest.instantCollapseShade()
+
+ // THEN overlay was hidden
+ assertThat(currentOverlays).isEmpty()
+ }
+
private fun setScene(key: SceneKey) {
sceneInteractor.changeScene(key, "test")
sceneInteractor.setTransitionState(
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/carrier/ShadeCarrierGroupControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/carrier/ShadeCarrierGroupControllerTest.java
index cd55bb23c1fc..6f1150e5fd96 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/carrier/ShadeCarrierGroupControllerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/carrier/ShadeCarrierGroupControllerTest.java
@@ -46,6 +46,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.android.keyguard.CarrierTextManager;
+import com.android.systemui.kairos.KairosNetwork;
import com.android.systemui.log.core.FakeLogBuffer;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.statusbar.connectivity.IconState;
@@ -55,6 +56,7 @@ import com.android.systemui.statusbar.connectivity.SignalCallback;
import com.android.systemui.statusbar.connectivity.ui.MobileContextProvider;
import com.android.systemui.statusbar.pipeline.StatusBarPipelineFlags;
import com.android.systemui.statusbar.pipeline.mobile.ui.MobileUiAdapter;
+import com.android.systemui.statusbar.pipeline.mobile.ui.MobileUiAdapterKairos;
import com.android.systemui.statusbar.pipeline.mobile.ui.MobileViewLogger;
import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.MobileIconsViewModel;
import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.ShadeCarrierGroupMobileIconViewModel;
@@ -63,6 +65,7 @@ import com.android.systemui.util.kotlin.FlowProviderKt;
import com.android.systemui.utils.leaks.LeakCheckedTest;
import com.android.systemui.utils.os.FakeHandler;
+import kotlinx.coroutines.CoroutineScope;
import kotlinx.coroutines.flow.MutableStateFlow;
import org.junit.Before;
@@ -178,8 +181,10 @@ public class ShadeCarrierGroupControllerTest extends LeakCheckedTest {
mSlotIndexResolver,
mMobileUiAdapter,
mMobileContextProvider,
- mStatusBarPipelineFlags
- )
+ mStatusBarPipelineFlags,
+ mock(CoroutineScope.class),
+ mock(KairosNetwork.class),
+ () -> mock(MobileUiAdapterKairos.class))
.setShadeCarrierGroup(mShadeCarrierGroup)
.build();
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/CommandQueueTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/CommandQueueTest.java
index 70df82d95008..c26f18f5ab6d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/CommandQueueTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/CommandQueueTest.java
@@ -36,7 +36,9 @@ import android.hardware.biometrics.IBiometricSysuiReceiver;
import android.hardware.biometrics.PromptInfo;
import android.hardware.fingerprint.IUdfpsRefreshRateRequestCallback;
import android.os.Bundle;
+import android.os.RemoteException;
import android.platform.test.annotations.EnableFlags;
+import android.util.Pair;
import android.view.KeyEvent;
import android.view.WindowInsets;
import android.view.WindowInsets.Type.InsetsType;
@@ -46,6 +48,7 @@ import android.view.WindowInsetsController.Behavior;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
+import com.android.internal.statusbar.DisableStates;
import com.android.internal.statusbar.LetterboxDetails;
import com.android.internal.statusbar.StatusBarIcon;
import com.android.internal.view.AppearanceRegion;
@@ -58,11 +61,14 @@ import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.HashMap;
+import java.util.Map;
+
@SmallTest
@RunWith(AndroidJUnit4.class)
public class CommandQueueTest extends SysuiTestCase {
- private static final LetterboxDetails[] TEST_LETTERBOX_DETAILS = new LetterboxDetails[] {
+ private static final LetterboxDetails[] TEST_LETTERBOX_DETAILS = new LetterboxDetails[]{
new LetterboxDetails(
/* letterboxInnerBounds= */ new Rect(100, 0, 200, 500),
/* letterboxFullBounds= */ new Rect(0, 0, 500, 100),
@@ -119,6 +125,27 @@ public class CommandQueueTest extends SysuiTestCase {
}
@Test
+ public void testDisableForAllDisplays() throws RemoteException {
+ int state1 = 14;
+ int state2 = 42;
+ int secondaryDisplayState1 = 16;
+ int secondaryDisplayState2 = 44;
+ Map<Integer, Pair<Integer, Integer>> displaysWithStates = new HashMap<>();
+ displaysWithStates.put(DEFAULT_DISPLAY, new Pair<>(state1, state2)); // Example values
+ displaysWithStates.put(SECONDARY_DISPLAY,
+ new Pair<>(secondaryDisplayState1, secondaryDisplayState2)); // Example values
+ DisableStates expectedDisableStates = new DisableStates(displaysWithStates, true);
+
+ mCommandQueue.disableForAllDisplays(expectedDisableStates);
+ waitForIdleSync();
+
+ verify(mCallbacks).disable(eq(DEFAULT_DISPLAY), eq(state1), eq(state2), eq(true));
+ verify(mCallbacks).disable(eq(SECONDARY_DISPLAY), eq(secondaryDisplayState1),
+ eq(secondaryDisplayState2), eq(true));
+ }
+
+
+ @Test
public void testExpandNotifications() {
mCommandQueue.animateExpandNotificationsPanel();
waitForIdleSync();
@@ -475,7 +502,8 @@ public class CommandQueueTest extends SysuiTestCase {
final long requestId = 10;
mCommandQueue.showAuthenticationDialog(promptInfo, receiver, sensorIds,
- credentialAllowed, requireConfirmation, userId, operationId, packageName, requestId);
+ credentialAllowed, requireConfirmation, userId, operationId, packageName,
+ requestId);
waitForIdleSync();
verify(mCallbacks).showAuthenticationDialog(eq(promptInfo), eq(receiver), eq(sensorIds),
eq(credentialAllowed), eq(requireConfirmation), eq(userId), eq(operationId),
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationGroupingUtilTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationGroupingUtilTest.kt
index e04162bf990a..18f25cd4838b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationGroupingUtilTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationGroupingUtilTest.kt
@@ -16,24 +16,35 @@
package com.android.systemui.statusbar
+import android.app.Notification
+import android.platform.test.annotations.EnableFlags
import android.platform.test.flag.junit.FlagsParameterization
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
+import com.android.systemui.res.R
+import com.android.systemui.statusbar.notification.collection.BundleEntry
+import com.android.systemui.statusbar.notification.collection.EntryAdapterFactory
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
import com.android.systemui.statusbar.notification.row.NotificationTestHelper
+import com.android.systemui.statusbar.notification.row.entryAdapterFactory
import com.android.systemui.statusbar.notification.shared.NotificationBundleUi
+import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.Mockito.mock
+import org.mockito.Mockito.`when`
import platform.test.runner.parameterized.ParameterizedAndroidJunit4
import platform.test.runner.parameterized.Parameters
@SmallTest
@RunWith(ParameterizedAndroidJunit4::class)
class NotificationGroupingUtilTest(flags: FlagsParameterization) : SysuiTestCase() {
-
+ private val kosmos = testKosmos()
private lateinit var underTest: NotificationGroupingUtil
+ private val factory: EntryAdapterFactory = kosmos.entryAdapterFactory
private lateinit var testHelper: NotificationTestHelper
companion object {
@@ -60,4 +71,55 @@ class NotificationGroupingUtilTest(flags: FlagsParameterization) : SysuiTestCase
underTest = NotificationGroupingUtil(row)
assertThat(underTest.showsTime(row)).isTrue()
}
+
+ @Test
+ fun iconExtractor_extractsSbn_notification() {
+ val row = testHelper.createRow()
+
+ underTest = NotificationGroupingUtil(row)
+
+ assertThat(NotificationGroupingUtil.ICON_EXTRACTOR.extractData(row)).isInstanceOf(
+ Notification::class.java)
+ }
+
+ @Test
+ @EnableFlags(NotificationBundleUi.FLAG_NAME)
+ fun iconExtractor_noException_bundle() {
+ val row = mock(ExpandableNotificationRow::class.java)
+ val be = BundleEntry("promotions")
+ `when`(row.entryAdapter).thenReturn(factory.create(be))
+
+ underTest = NotificationGroupingUtil(row)
+
+ assertThat(NotificationGroupingUtil.ICON_EXTRACTOR.extractData(row)).isNull()
+ }
+
+ @Test
+ fun iconComparator_sameNotificationIcon() {
+ val n1 = NotificationGroupingUtil.ICON_EXTRACTOR.extractData(testHelper.createRow())
+ val n2 = NotificationGroupingUtil.ICON_EXTRACTOR.extractData(testHelper.createRow())
+
+ assertThat(NotificationGroupingUtil.IconComparator().hasSameIcon(n1, n2)).isTrue()
+ }
+
+ @Test
+ fun iconComparator_differentNotificationIcon() {
+ val notif = Notification.Builder(mContext, "").setSmallIcon(R.drawable.ic_menu).build()
+ val n1 = NotificationGroupingUtil.ICON_EXTRACTOR.extractData(testHelper.createRow(notif))
+ val n2 = NotificationGroupingUtil.ICON_EXTRACTOR.extractData(testHelper.createRow())
+
+ assertThat(NotificationGroupingUtil.IconComparator().hasSameIcon(n1, n2)).isFalse()
+ }
+
+ @Test
+ @EnableFlags(NotificationBundleUi.FLAG_NAME)
+ fun iconComparator_bundleNotification() {
+ assertThat(NotificationGroupingUtil.IconComparator().hasSameIcon(null,
+ NotificationGroupingUtil.ICON_EXTRACTOR.extractData(testHelper.createRow()))).isFalse()
+ }
+
+ @Test
+ fun iconComparator_twoBundles() {
+ assertThat(NotificationGroupingUtil.IconComparator().hasSameIcon(null, null)).isFalse()
+ }
} \ No newline at end of file
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt
index 3ecf302204bc..67af7a54988e 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt
@@ -33,6 +33,7 @@ import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.kosmos.testScope
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.shade.ShadeExpansionChangeEvent
+import com.android.systemui.shade.data.repository.fakeShadeDisplaysRepository
import com.android.systemui.shade.domain.interactor.ShadeModeInteractor
import com.android.systemui.statusbar.phone.BiometricUnlockController
import com.android.systemui.statusbar.phone.DozeParameters
@@ -45,6 +46,8 @@ import com.android.systemui.wallpapers.domain.interactor.WallpaperInteractor
import com.android.systemui.window.domain.interactor.WindowRootViewBlurInteractor
import com.android.wm.shell.appzoomout.AppZoomOut
import com.google.common.truth.Truth.assertThat
+import java.util.Optional
+import java.util.function.Consumer
import org.junit.Before
import org.junit.Rule
import org.junit.Test
@@ -65,8 +68,6 @@ import org.mockito.Mockito.reset
import org.mockito.Mockito.verify
import org.mockito.Mockito.`when`
import org.mockito.junit.MockitoJUnit
-import java.util.Optional
-import java.util.function.Consumer
@RunWith(AndroidJUnit4::class)
@RunWithLooper
@@ -75,6 +76,7 @@ class NotificationShadeDepthControllerTest : SysuiTestCase() {
private val kosmos = testKosmos()
private val applicationScope = kosmos.testScope.backgroundScope
+ private val shadeDisplayRepository = kosmos.fakeShadeDisplaysRepository
@Mock private lateinit var statusBarStateController: StatusBarStateController
@Mock private lateinit var blurUtils: BlurUtils
@Mock private lateinit var biometricUnlockController: BiometricUnlockController
@@ -135,7 +137,8 @@ class NotificationShadeDepthControllerTest : SysuiTestCase() {
windowRootViewBlurInteractor,
appZoomOutOptional,
applicationScope,
- dumpManager
+ dumpManager,
+ { shadeDisplayRepository },
)
notificationShadeDepthController.shadeAnimation = shadeAnimation
notificationShadeDepthController.brightnessMirrorSpring = brightnessSpring
@@ -355,6 +358,36 @@ class NotificationShadeDepthControllerTest : SysuiTestCase() {
}
@Test
+ @EnableFlags(Flags.FLAG_SHADE_WINDOW_GOES_AROUND)
+ fun updateBlurCallback_shadeInExternalDisplay_doesSetZeroZoom() {
+ notificationShadeDepthController.onPanelExpansionChanged(
+ ShadeExpansionChangeEvent(fraction = 1f, expanded = true, tracking = false)
+ )
+ notificationShadeDepthController.addListener(listener)
+ shadeDisplayRepository.setDisplayId(1) // not default display.
+
+ notificationShadeDepthController.updateBlurCallback.doFrame(0)
+
+ verify(wallpaperController).setNotificationShadeZoom(eq(0f))
+ verify(listener).onWallpaperZoomOutChanged(eq(0f))
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_SHADE_WINDOW_GOES_AROUND)
+ fun updateBlurCallback_shadeInDefaultDisplay_doesNotSetZeroZoom() {
+ notificationShadeDepthController.onPanelExpansionChanged(
+ ShadeExpansionChangeEvent(fraction = 1f, expanded = true, tracking = false)
+ )
+ notificationShadeDepthController.addListener(listener)
+ shadeDisplayRepository.setDisplayId(0) // shade is in default display
+
+ notificationShadeDepthController.updateBlurCallback.doFrame(0)
+
+ verify(wallpaperController).setNotificationShadeZoom(floatThat { it != 0f })
+ verify(listener).onWallpaperZoomOutChanged(floatThat { it != 0f })
+ }
+
+ @Test
@DisableFlags(Flags.FLAG_NOTIFICATION_SHADE_BLUR)
fun updateBlurCallback_setsOpaque_whenScrim() {
scrimVisibilityCaptor.value.accept(ScrimController.OPAQUE)
@@ -488,10 +521,10 @@ class NotificationShadeDepthControllerTest : SysuiTestCase() {
}
private fun enableSplitShade() {
- `when` (shadeModeInteractor.isSplitShade).thenReturn(true)
+ `when`(shadeModeInteractor.isSplitShade).thenReturn(true)
}
private fun disableSplitShade() {
- `when` (shadeModeInteractor.isSplitShade).thenReturn(false)
+ `when`(shadeModeInteractor.isSplitShade).thenReturn(false)
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModelTest.kt
index 8120c2d9f816..cbef24a2f2d2 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModelTest.kt
@@ -24,6 +24,7 @@ import android.platform.test.annotations.EnableFlags
import android.platform.test.flag.junit.FlagsParameterization
import android.view.View
import androidx.test.filters.SmallTest
+import com.android.internal.logging.InstanceId
import com.android.systemui.SysuiTestCase
import com.android.systemui.activity.data.repository.activityManagerRepository
import com.android.systemui.activity.data.repository.fake
@@ -44,7 +45,6 @@ import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel
import com.android.systemui.statusbar.chips.ui.view.ChipBackgroundContainer
import com.android.systemui.statusbar.core.StatusBarConnectedDisplays
import com.android.systemui.statusbar.core.StatusBarRootModernization
-import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel
import com.android.systemui.statusbar.phone.ongoingcall.DisableChipsModernization
import com.android.systemui.statusbar.phone.ongoingcall.EnableChipsModernization
import com.android.systemui.statusbar.phone.ongoingcall.StatusBarChipsModernization
@@ -98,36 +98,57 @@ class CallChipViewModelTest(flags: FlagsParameterization) : SysuiTestCase() {
}
@Test
- fun chip_inCall_zeroStartTime_isShownAsIconOnly() =
+ fun chip_inCall_hasKeyWithPrefix() =
kosmos.runTest {
val latest by collectLastValue(underTest.chip)
addOngoingCallState(startTimeMs = 0, isAppVisible = false)
+ assertThat(latest).isInstanceOf(OngoingActivityChipModel.Active::class.java)
+ assertThat((latest as OngoingActivityChipModel.Active).key)
+ .startsWith(CallChipViewModel.KEY_PREFIX)
+ }
+
+ @Test
+ fun chip_inCall_zeroStartTime_isShownAsIconOnly_withData() =
+ kosmos.runTest {
+ val latest by collectLastValue(underTest.chip)
+
+ val instanceId = InstanceId.fakeInstanceId(10)
+ addOngoingCallState(startTimeMs = 0, isAppVisible = false, instanceId = instanceId)
+
assertThat(latest).isInstanceOf(OngoingActivityChipModel.Active.IconOnly::class.java)
assertThat((latest as OngoingActivityChipModel.Active).isHidden).isFalse()
+ assertThat((latest as OngoingActivityChipModel.Active).isImportantForPrivacy).isFalse()
+ assertThat((latest as OngoingActivityChipModel.Active).instanceId).isEqualTo(instanceId)
}
@Test
- fun chip_inCall_negativeStartTime_isShownAsIconOnly() =
+ fun chip_inCall_negativeStartTime_isShownAsIconOnly_withData() =
kosmos.runTest {
val latest by collectLastValue(underTest.chip)
- addOngoingCallState(startTimeMs = -2, isAppVisible = false)
+ val instanceId = InstanceId.fakeInstanceId(10)
+ addOngoingCallState(startTimeMs = -2, isAppVisible = false, instanceId = instanceId)
assertThat(latest).isInstanceOf(OngoingActivityChipModel.Active.IconOnly::class.java)
assertThat((latest as OngoingActivityChipModel.Active).isHidden).isFalse()
+ assertThat((latest as OngoingActivityChipModel.Active).isImportantForPrivacy).isFalse()
+ assertThat((latest as OngoingActivityChipModel.Active).instanceId).isEqualTo(instanceId)
}
@Test
- fun chip_inCall_positiveStartTime_isShownAsTimer() =
+ fun chip_inCall_positiveStartTime_isShownAsTimer_withData() =
kosmos.runTest {
val latest by collectLastValue(underTest.chip)
- addOngoingCallState(startTimeMs = 345, isAppVisible = false)
+ val instanceId = InstanceId.fakeInstanceId(10)
+ addOngoingCallState(startTimeMs = 345, isAppVisible = false, instanceId = instanceId)
assertThat(latest).isInstanceOf(OngoingActivityChipModel.Active.Timer::class.java)
assertThat((latest as OngoingActivityChipModel.Active).isHidden).isFalse()
+ assertThat((latest as OngoingActivityChipModel.Active).isImportantForPrivacy).isFalse()
+ assertThat((latest as OngoingActivityChipModel.Active).instanceId).isEqualTo(instanceId)
}
@Test
@@ -152,6 +173,7 @@ class CallChipViewModelTest(flags: FlagsParameterization) : SysuiTestCase() {
assertThat(latest).isInstanceOf(OngoingActivityChipModel.Active.IconOnly::class.java)
assertThat((latest as OngoingActivityChipModel.Active).isHidden).isTrue()
+ assertThat((latest as OngoingActivityChipModel.Active).isImportantForPrivacy).isFalse()
}
@Test
@@ -176,6 +198,7 @@ class CallChipViewModelTest(flags: FlagsParameterization) : SysuiTestCase() {
assertThat(latest).isInstanceOf(OngoingActivityChipModel.Active.IconOnly::class.java)
assertThat((latest as OngoingActivityChipModel.Active).isHidden).isTrue()
+ assertThat((latest as OngoingActivityChipModel.Active).isImportantForPrivacy).isFalse()
}
@Test
@@ -200,6 +223,7 @@ class CallChipViewModelTest(flags: FlagsParameterization) : SysuiTestCase() {
assertThat(latest).isInstanceOf(OngoingActivityChipModel.Active.Timer::class.java)
assertThat((latest as OngoingActivityChipModel.Active).isHidden).isTrue()
+ assertThat((latest as OngoingActivityChipModel.Active).isImportantForPrivacy).isFalse()
}
@Test
@@ -792,17 +816,6 @@ class CallChipViewModelTest(flags: FlagsParameterization) : SysuiTestCase() {
mock<StatusBarIconView>()
}
- private val PROMOTED_CONTENT_WITH_COLOR =
- PromotedNotificationContentModel.Builder("notif")
- .apply {
- this.colors =
- PromotedNotificationContentModel.Colors(
- backgroundColor = PROMOTED_BACKGROUND_COLOR,
- primaryTextColor = PROMOTED_PRIMARY_TEXT_COLOR,
- )
- }
- .build()
-
private const val NOTIFICATION_KEY = "testKey"
private const val NOTIFICATION_UID = 12345
private const val PROMOTED_BACKGROUND_COLOR = 65
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/viewmodel/CastToOtherDeviceChipViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/viewmodel/CastToOtherDeviceChipViewModelTest.kt
index d921ab3b284d..e1b4496606e5 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/viewmodel/CastToOtherDeviceChipViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/viewmodel/CastToOtherDeviceChipViewModelTest.kt
@@ -135,6 +135,9 @@ class CastToOtherDeviceChipViewModelTest : SysuiTestCase() {
)
assertThat(latest).isInstanceOf(OngoingActivityChipModel.Active.Timer::class.java)
+ assertThat((latest as OngoingActivityChipModel.Active).isImportantForPrivacy).isTrue()
+ assertThat((latest as OngoingActivityChipModel.Active).instanceId).isNotNull()
+
val icon =
(((latest as OngoingActivityChipModel.Active).icon)
as OngoingActivityChipModel.ChipIcon.SingleColorIcon)
@@ -157,6 +160,9 @@ class CastToOtherDeviceChipViewModelTest : SysuiTestCase() {
)
assertThat(latest).isInstanceOf(OngoingActivityChipModel.Active.IconOnly::class.java)
+ assertThat((latest as OngoingActivityChipModel.Active).isImportantForPrivacy).isTrue()
+ assertThat((latest as OngoingActivityChipModel.Active).instanceId).isNotNull()
+
val icon =
(((latest as OngoingActivityChipModel.Active).icon)
as OngoingActivityChipModel.ChipIcon.SingleColorIcon)
@@ -177,6 +183,9 @@ class CastToOtherDeviceChipViewModelTest : SysuiTestCase() {
MediaProjectionState.Projecting.EntireScreen(CAST_TO_OTHER_DEVICES_PACKAGE)
assertThat(latest).isInstanceOf(OngoingActivityChipModel.Active.Timer::class.java)
+ assertThat((latest as OngoingActivityChipModel.Active).isImportantForPrivacy).isTrue()
+ assertThat((latest as OngoingActivityChipModel.Active).instanceId).isNotNull()
+
val icon =
(((latest as OngoingActivityChipModel.Active).icon)
as OngoingActivityChipModel.ChipIcon.SingleColorIcon)
@@ -215,6 +224,9 @@ class CastToOtherDeviceChipViewModelTest : SysuiTestCase() {
)
assertThat(latest).isInstanceOf(OngoingActivityChipModel.Active.IconOnly::class.java)
+ assertThat((latest as OngoingActivityChipModel.Active).isImportantForPrivacy).isTrue()
+ assertThat((latest as OngoingActivityChipModel.Active).instanceId).isNotNull()
+
val icon =
(((latest as OngoingActivityChipModel.Active).icon)
as OngoingActivityChipModel.ChipIcon.SingleColorIcon)
@@ -245,6 +257,9 @@ class CastToOtherDeviceChipViewModelTest : SysuiTestCase() {
// Only the projection info will show a timer
assertThat(latest).isInstanceOf(OngoingActivityChipModel.Active.Timer::class.java)
+ assertThat((latest as OngoingActivityChipModel.Active).isImportantForPrivacy).isTrue()
+ assertThat((latest as OngoingActivityChipModel.Active).instanceId).isNotNull()
+
val icon =
(((latest as OngoingActivityChipModel.Active).icon)
as OngoingActivityChipModel.ChipIcon.SingleColorIcon)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/domain/interactor/SingleNotificationChipInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/domain/interactor/SingleNotificationChipInteractorTest.kt
index 7f8f5f43e775..3bed69bac15d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/domain/interactor/SingleNotificationChipInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/domain/interactor/SingleNotificationChipInteractorTest.kt
@@ -20,6 +20,7 @@ import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import com.android.internal.logging.InstanceId
import com.android.systemui.SysuiTestCase
import com.android.systemui.activity.data.repository.activityManagerRepository
import com.android.systemui.activity.data.repository.fake
@@ -30,7 +31,7 @@ import com.android.systemui.statusbar.StatusBarIconView
import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips
import com.android.systemui.statusbar.core.StatusBarConnectedDisplays
import com.android.systemui.statusbar.notification.data.model.activeNotificationModel
-import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel
+import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentBuilder
import com.android.systemui.testKosmos
import com.android.systemui.util.time.fakeSystemClock
import com.google.common.truth.Truth.assertThat
@@ -49,12 +50,14 @@ class SingleNotificationChipInteractorTest : SysuiTestCase() {
fun notificationChip_startsWithStartingModel() =
kosmos.runTest {
val icon = mock<StatusBarIconView>()
+ val instanceId = InstanceId.fakeInstanceId(4)
val startingNotif =
activeNotificationModel(
key = "notif1",
appName = "Fake Name",
statusBarChipIcon = icon,
promotedContent = PROMOTED_CONTENT,
+ instanceId = instanceId,
)
val underTest = factory.create(startingNotif, creationTime = 1)
@@ -65,12 +68,14 @@ class SingleNotificationChipInteractorTest : SysuiTestCase() {
assertThat(latest!!.appName).isEqualTo("Fake Name")
assertThat(latest!!.statusBarChipIconView).isEqualTo(icon)
assertThat(latest!!.promotedContent).isEqualTo(PROMOTED_CONTENT)
+ assertThat(latest!!.instanceId).isEqualTo(instanceId)
}
@Test
fun notificationChip_updatesAfterSet() =
kosmos.runTest {
val originalIconView = mock<StatusBarIconView>()
+ val originalInstanceId = InstanceId.fakeInstanceId(4)
val underTest =
factory.create(
activeNotificationModel(
@@ -78,6 +83,7 @@ class SingleNotificationChipInteractorTest : SysuiTestCase() {
appName = "Fake Name",
statusBarChipIcon = originalIconView,
promotedContent = PROMOTED_CONTENT,
+ instanceId = originalInstanceId,
),
creationTime = 1,
)
@@ -85,18 +91,21 @@ class SingleNotificationChipInteractorTest : SysuiTestCase() {
val latest by collectLastValue(underTest.notificationChip)
val newIconView = mock<StatusBarIconView>()
+ val newInstanceId = InstanceId.fakeInstanceId(4)
underTest.setNotification(
activeNotificationModel(
key = "notif1",
appName = "New Name",
statusBarChipIcon = newIconView,
promotedContent = PROMOTED_CONTENT,
+ instanceId = newInstanceId,
)
)
assertThat(latest!!.key).isEqualTo("notif1")
assertThat(latest!!.appName).isEqualTo("New Name")
assertThat(latest!!.statusBarChipIconView).isEqualTo(newIconView)
+ assertThat(latest!!.instanceId).isEqualTo(newInstanceId)
}
@Test
@@ -420,7 +429,7 @@ class SingleNotificationChipInteractorTest : SysuiTestCase() {
// WHEN the notif gets a new UID that starts as visible
activityManagerRepository.fake.startingIsAppVisibleValue = true
val newPromotedContentBuilder =
- PromotedNotificationContentModel.Builder("notif").apply {
+ PromotedNotificationContentBuilder("notif").applyToShared {
this.shortCriticalText = "Arrived"
}
val newPromotedContent = newPromotedContentBuilder.build()
@@ -452,6 +461,6 @@ class SingleNotificationChipInteractorTest : SysuiTestCase() {
companion object {
private const val UID = 885
- private val PROMOTED_CONTENT = PromotedNotificationContentModel.Builder("notif1").build()
+ private val PROMOTED_CONTENT = PromotedNotificationContentBuilder("notif1").build()
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/domain/interactor/StatusBarNotificationChipsInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/domain/interactor/StatusBarNotificationChipsInteractorTest.kt
index 0b9b297130a2..233efbb97d4e 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/domain/interactor/StatusBarNotificationChipsInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/domain/interactor/StatusBarNotificationChipsInteractorTest.kt
@@ -36,7 +36,7 @@ import com.android.systemui.statusbar.notification.data.repository.ActiveNotific
import com.android.systemui.statusbar.notification.data.repository.activeNotificationListRepository
import com.android.systemui.statusbar.notification.data.repository.addNotif
import com.android.systemui.statusbar.notification.data.repository.removeNotif
-import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel
+import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentBuilder
import com.android.systemui.statusbar.notification.shared.ActiveNotificationModel
import com.android.systemui.statusbar.notification.shared.CallType
import com.android.systemui.testKosmos
@@ -56,16 +56,16 @@ class StatusBarNotificationChipsInteractorTest : SysuiTestCase() {
@Test
@DisableFlags(StatusBarNotifChips.FLAG_NAME)
- fun shownNotificationChips_flagOff_noNotifs() =
+ fun allNotificationChips_flagOff_noNotifs() =
kosmos.runTest {
- val latest by collectLastValue(underTest.shownNotificationChips)
+ val latest by collectLastValue(underTest.allNotificationChips)
setNotifs(
listOf(
activeNotificationModel(
key = "notif",
statusBarChipIcon = mock<StatusBarIconView>(),
- promotedContent = PromotedNotificationContentModel.Builder("notif").build(),
+ promotedContent = PromotedNotificationContentBuilder("notif").build(),
)
)
)
@@ -75,9 +75,9 @@ class StatusBarNotificationChipsInteractorTest : SysuiTestCase() {
@Test
@EnableFlags(StatusBarNotifChips.FLAG_NAME)
- fun shownNotificationChips_noNotifs_empty() =
+ fun allNotificationChips_noNotifs_empty() =
kosmos.runTest {
- val latest by collectLastValue(underTest.shownNotificationChips)
+ val latest by collectLastValue(underTest.allNotificationChips)
setNotifs(emptyList())
@@ -87,16 +87,16 @@ class StatusBarNotificationChipsInteractorTest : SysuiTestCase() {
@Test
@EnableFlags(StatusBarNotifChips.FLAG_NAME)
@DisableFlags(StatusBarConnectedDisplays.FLAG_NAME)
- fun shownNotificationChips_notifMissingStatusBarChipIconView_cdFlagOff_empty() =
+ fun allNotificationChips_notifMissingStatusBarChipIconView_cdFlagOff_empty() =
kosmos.runTest {
- val latest by collectLastValue(underTest.shownNotificationChips)
+ val latest by collectLastValue(underTest.allNotificationChips)
setNotifs(
listOf(
activeNotificationModel(
key = "notif",
statusBarChipIcon = null,
- promotedContent = PromotedNotificationContentModel.Builder("notif").build(),
+ promotedContent = PromotedNotificationContentBuilder("notif").build(),
)
)
)
@@ -106,16 +106,16 @@ class StatusBarNotificationChipsInteractorTest : SysuiTestCase() {
@Test
@EnableFlags(StatusBarNotifChips.FLAG_NAME, StatusBarConnectedDisplays.FLAG_NAME)
- fun shownNotificationChips_notifMissingStatusBarChipIconView_cdFlagOn_notEmpty() =
+ fun allNotificationChips_notifMissingStatusBarChipIconView_cdFlagOn_notEmpty() =
kosmos.runTest {
- val latest by collectLastValue(underTest.shownNotificationChips)
+ val latest by collectLastValue(underTest.allNotificationChips)
setNotifs(
listOf(
activeNotificationModel(
key = "notif",
statusBarChipIcon = null,
- promotedContent = PromotedNotificationContentModel.Builder("notif").build(),
+ promotedContent = PromotedNotificationContentBuilder("notif").build(),
)
)
)
@@ -125,9 +125,9 @@ class StatusBarNotificationChipsInteractorTest : SysuiTestCase() {
@Test
@EnableFlags(StatusBarNotifChips.FLAG_NAME)
- fun shownNotificationChips_onePromotedNotif_statusBarIconViewMatches() =
+ fun allNotificationChips_onePromotedNotif_statusBarIconViewMatches() =
kosmos.runTest {
- val latest by collectLastValue(underTest.shownNotificationChips)
+ val latest by collectLastValue(underTest.allNotificationChips)
val icon = mock<StatusBarIconView>()
setNotifs(
@@ -135,7 +135,7 @@ class StatusBarNotificationChipsInteractorTest : SysuiTestCase() {
activeNotificationModel(
key = "notif",
statusBarChipIcon = icon,
- promotedContent = PromotedNotificationContentModel.Builder("notif").build(),
+ promotedContent = PromotedNotificationContentBuilder("notif").build(),
)
)
)
@@ -147,9 +147,9 @@ class StatusBarNotificationChipsInteractorTest : SysuiTestCase() {
@Test
@EnableFlags(StatusBarNotifChips.FLAG_NAME)
- fun shownNotificationChips_onlyForPromotedNotifs() =
+ fun allNotificationChips_onlyForPromotedNotifs() =
kosmos.runTest {
- val latest by collectLastValue(underTest.shownNotificationChips)
+ val latest by collectLastValue(underTest.allNotificationChips)
val firstIcon = mock<StatusBarIconView>()
val secondIcon = mock<StatusBarIconView>()
@@ -158,12 +158,12 @@ class StatusBarNotificationChipsInteractorTest : SysuiTestCase() {
activeNotificationModel(
key = "notif1",
statusBarChipIcon = firstIcon,
- promotedContent = PromotedNotificationContentModel.Builder("notif1").build(),
+ promotedContent = PromotedNotificationContentBuilder("notif1").build(),
),
activeNotificationModel(
key = "notif2",
statusBarChipIcon = secondIcon,
- promotedContent = PromotedNotificationContentModel.Builder("notif2").build(),
+ promotedContent = PromotedNotificationContentBuilder("notif2").build(),
),
activeNotificationModel(
key = "notif3",
@@ -182,11 +182,11 @@ class StatusBarNotificationChipsInteractorTest : SysuiTestCase() {
@Test
@EnableFlags(StatusBarNotifChips.FLAG_NAME)
- fun shownNotificationChips_onlyForNotVisibleApps() =
+ fun allNotificationChips_appVisibilityInfoCorrect() =
kosmos.runTest {
activityManagerRepository.fake.startingIsAppVisibleValue = false
- val latest by collectLastValue(underTest.shownNotificationChips)
+ val latest by collectLastValue(underTest.allNotificationChips)
val uid = 433
setNotifs(
@@ -195,27 +195,30 @@ class StatusBarNotificationChipsInteractorTest : SysuiTestCase() {
key = "notif",
uid = uid,
statusBarChipIcon = mock<StatusBarIconView>(),
- promotedContent = PromotedNotificationContentModel.Builder("notif1").build(),
+ promotedContent = PromotedNotificationContentBuilder("notif").build(),
)
)
)
activityManagerRepository.fake.setIsAppVisible(uid, isAppVisible = false)
assertThat(latest).hasSize(1)
+ assertThat(latest!![0].isAppVisible).isFalse()
activityManagerRepository.fake.setIsAppVisible(uid, isAppVisible = true)
- assertThat(latest).isEmpty()
+ assertThat(latest).hasSize(1)
+ assertThat(latest!![0].isAppVisible).isTrue()
activityManagerRepository.fake.setIsAppVisible(uid, isAppVisible = false)
assertThat(latest).hasSize(1)
+ assertThat(latest!![0].isAppVisible).isFalse()
}
/** Regression test for b/388521980. */
@Test
@EnableFlags(StatusBarNotifChips.FLAG_NAME)
- fun shownNotificationChips_callNotifIsAlsoPromoted_callNotifExcluded() =
+ fun allNotificationChips_callNotifIsAlsoPromoted_callNotifExcluded() =
kosmos.runTest {
- val latest by collectLastValue(underTest.shownNotificationChips)
+ val latest by collectLastValue(underTest.allNotificationChips)
setNotifs(
listOf(
@@ -223,14 +226,14 @@ class StatusBarNotificationChipsInteractorTest : SysuiTestCase() {
key = "promotedNormal",
statusBarChipIcon = mock(),
promotedContent =
- PromotedNotificationContentModel.Builder("promotedNormal").build(),
+ PromotedNotificationContentBuilder("promotedNormal").build(),
callType = CallType.None,
),
activeNotificationModel(
key = "promotedCall",
statusBarChipIcon = mock(),
promotedContent =
- PromotedNotificationContentModel.Builder("promotedCall").build(),
+ PromotedNotificationContentBuilder("promotedCall").build(),
callType = CallType.Ongoing,
),
)
@@ -243,9 +246,9 @@ class StatusBarNotificationChipsInteractorTest : SysuiTestCase() {
@Test
@EnableFlags(StatusBarNotifChips.FLAG_NAME)
- fun shownNotificationChips_notifUpdatesGoThrough() =
+ fun allNotificationChips_notifUpdatesGoThrough() =
kosmos.runTest {
- val latest by collectLastValue(underTest.shownNotificationChips)
+ val latest by collectLastValue(underTest.allNotificationChips)
val firstIcon = mock<StatusBarIconView>()
val secondIcon = mock<StatusBarIconView>()
@@ -256,7 +259,7 @@ class StatusBarNotificationChipsInteractorTest : SysuiTestCase() {
activeNotificationModel(
key = "notif",
statusBarChipIcon = firstIcon,
- promotedContent = PromotedNotificationContentModel.Builder("notif").build(),
+ promotedContent = PromotedNotificationContentBuilder("notif").build(),
)
)
)
@@ -269,7 +272,7 @@ class StatusBarNotificationChipsInteractorTest : SysuiTestCase() {
activeNotificationModel(
key = "notif",
statusBarChipIcon = secondIcon,
- promotedContent = PromotedNotificationContentModel.Builder("notif").build(),
+ promotedContent = PromotedNotificationContentBuilder("notif").build(),
)
)
)
@@ -282,7 +285,7 @@ class StatusBarNotificationChipsInteractorTest : SysuiTestCase() {
activeNotificationModel(
key = "notif",
statusBarChipIcon = thirdIcon,
- promotedContent = PromotedNotificationContentModel.Builder("notif").build(),
+ promotedContent = PromotedNotificationContentBuilder("notif").build(),
)
)
)
@@ -293,16 +296,16 @@ class StatusBarNotificationChipsInteractorTest : SysuiTestCase() {
@Test
@EnableFlags(StatusBarNotifChips.FLAG_NAME)
- fun shownNotificationChips_promotedNotifDisappearsThenReappears() =
+ fun allNotificationChips_promotedNotifDisappearsThenReappears() =
kosmos.runTest {
- val latest by collectLastValue(underTest.shownNotificationChips)
+ val latest by collectLastValue(underTest.allNotificationChips)
setNotifs(
listOf(
activeNotificationModel(
key = "notif",
statusBarChipIcon = mock(),
- promotedContent = PromotedNotificationContentModel.Builder("notif").build(),
+ promotedContent = PromotedNotificationContentBuilder("notif").build(),
)
)
)
@@ -325,7 +328,7 @@ class StatusBarNotificationChipsInteractorTest : SysuiTestCase() {
activeNotificationModel(
key = "notif",
statusBarChipIcon = mock(),
- promotedContent = PromotedNotificationContentModel.Builder("notif").build(),
+ promotedContent = PromotedNotificationContentBuilder("notif").build(),
)
)
)
@@ -335,9 +338,9 @@ class StatusBarNotificationChipsInteractorTest : SysuiTestCase() {
@Test
@EnableFlags(StatusBarNotifChips.FLAG_NAME)
- fun shownNotificationChips_sortedByFirstAppearanceTime() =
+ fun allNotificationChips_sortedByFirstAppearanceTime() =
kosmos.runTest {
- val latest by collectLastValue(underTest.shownNotificationChips)
+ val latest by collectLastValue(underTest.allNotificationChips)
val firstIcon = mock<StatusBarIconView>()
val secondIcon = mock<StatusBarIconView>()
@@ -348,7 +351,7 @@ class StatusBarNotificationChipsInteractorTest : SysuiTestCase() {
activeNotificationModel(
key = "notif1",
statusBarChipIcon = firstIcon,
- promotedContent = PromotedNotificationContentModel.Builder("notif1").build(),
+ promotedContent = PromotedNotificationContentBuilder("notif1").build(),
)
setNotifs(listOf(notif1))
assertThat(latest!!.map { it.key }).containsExactly("notif1").inOrder()
@@ -359,7 +362,7 @@ class StatusBarNotificationChipsInteractorTest : SysuiTestCase() {
activeNotificationModel(
key = "notif2",
statusBarChipIcon = secondIcon,
- promotedContent = PromotedNotificationContentModel.Builder("notif2").build(),
+ promotedContent = PromotedNotificationContentBuilder("notif2").build(),
)
setNotifs(listOf(notif1, notif2))
@@ -380,7 +383,7 @@ class StatusBarNotificationChipsInteractorTest : SysuiTestCase() {
// WHEN notif1 gets an update
val notif1NewPromotedContent =
- PromotedNotificationContentModel.Builder("notif1").apply {
+ PromotedNotificationContentBuilder("notif1").applyToShared {
this.shortCriticalText = "Arrived"
}
setNotifs(
@@ -411,9 +414,9 @@ class StatusBarNotificationChipsInteractorTest : SysuiTestCase() {
@Test
@EnableFlags(StatusBarNotifChips.FLAG_NAME)
- fun shownNotificationChips_lastAppVisibleTimeMaintainedAcrossNotifAddsAndRemoves() =
+ fun allNotificationChips_lastAppVisibleTimeMaintainedAcrossNotifAddsAndRemoves() =
kosmos.runTest {
- val latest by collectLastValue(underTest.shownNotificationChips)
+ val latest by collectLastValue(underTest.allNotificationChips)
val notif1Info = NotifInfo("notif1", mock<StatusBarIconView>(), uid = 100)
val notif2Info = NotifInfo("notif2", mock<StatusBarIconView>(), uid = 200)
@@ -426,8 +429,7 @@ class StatusBarNotificationChipsInteractorTest : SysuiTestCase() {
key = notif1Info.key,
uid = notif1Info.uid,
statusBarChipIcon = notif1Info.icon,
- promotedContent =
- PromotedNotificationContentModel.Builder(notif1Info.key).build(),
+ promotedContent = PromotedNotificationContentBuilder(notif1Info.key).build(),
)
)
activityManagerRepository.fake.setIsAppVisible(notif1Info.uid, isAppVisible = false)
@@ -443,8 +445,7 @@ class StatusBarNotificationChipsInteractorTest : SysuiTestCase() {
key = notif2Info.key,
uid = notif2Info.uid,
statusBarChipIcon = notif2Info.icon,
- promotedContent =
- PromotedNotificationContentModel.Builder(notif2Info.key).build(),
+ promotedContent = PromotedNotificationContentBuilder(notif2Info.key).build(),
)
)
activityManagerRepository.fake.setIsAppVisible(notif2Info.uid, isAppVisible = false)
@@ -468,9 +469,9 @@ class StatusBarNotificationChipsInteractorTest : SysuiTestCase() {
@Test
@EnableFlags(StatusBarNotifChips.FLAG_NAME)
- fun shownNotificationChips_sortedByLastAppVisibleTime() =
+ fun allNotificationChips_sortedByLastAppVisibleTime() =
kosmos.runTest {
- val latest by collectLastValue(underTest.shownNotificationChips)
+ val latest by collectLastValue(underTest.allNotificationChips)
val notif1Info = NotifInfo("notif1", mock<StatusBarIconView>(), uid = 100)
val notif2Info = NotifInfo("notif2", mock<StatusBarIconView>(), uid = 200)
@@ -482,16 +483,14 @@ class StatusBarNotificationChipsInteractorTest : SysuiTestCase() {
key = notif1Info.key,
uid = notif1Info.uid,
statusBarChipIcon = notif1Info.icon,
- promotedContent =
- PromotedNotificationContentModel.Builder(notif1Info.key).build(),
+ promotedContent = PromotedNotificationContentBuilder(notif1Info.key).build(),
)
val notif2 =
activeNotificationModel(
key = notif2Info.key,
uid = notif2Info.uid,
statusBarChipIcon = notif2Info.icon,
- promotedContent =
- PromotedNotificationContentModel.Builder(notif2Info.key).build(),
+ promotedContent = PromotedNotificationContentBuilder(notif2Info.key).build(),
)
setNotifs(listOf(notif1, notif2))
assertThat(latest!!.map { it.key }).containsExactly("notif1", "notif2").inOrder()
@@ -500,15 +499,19 @@ class StatusBarNotificationChipsInteractorTest : SysuiTestCase() {
fakeSystemClock.advanceTime(1000)
activityManagerRepository.fake.setIsAppVisible(notif2Info.uid, isAppVisible = true)
- // THEN notif2 is no longer shown
- assertThat(latest!!.map { it.key }).containsExactly("notif1").inOrder()
+ // THEN notif2 is ranked above notif1 because it was more recently visible
+ assertThat(latest!!.map { it.key }).containsExactly("notif2", "notif1").inOrder()
+ assertThat(latest!![0].isAppVisible).isTrue() // notif2
+ assertThat(latest!![1].isAppVisible).isFalse() // notif1
// WHEN notif2's app is no longer visible
fakeSystemClock.advanceTime(1000)
activityManagerRepository.fake.setIsAppVisible(notif2Info.uid, isAppVisible = false)
- // THEN notif2 is ranked above notif1 because it was more recently visible
+ // THEN notif2 is still ranked above notif1
assertThat(latest!!.map { it.key }).containsExactly("notif2", "notif1").inOrder()
+ assertThat(latest!![0].isAppVisible).isFalse() // notif2
+ assertThat(latest!![1].isAppVisible).isFalse() // notif1
// WHEN the app associated with notif1 becomes visible then un-visible
fakeSystemClock.advanceTime(1000)
@@ -522,9 +525,9 @@ class StatusBarNotificationChipsInteractorTest : SysuiTestCase() {
@Test
@EnableFlags(StatusBarNotifChips.FLAG_NAME)
- fun shownNotificationChips_newNotificationTakesPriorityOverLastAppVisible() =
+ fun allNotificationChips_newNotificationTakesPriorityOverLastAppVisible() =
kosmos.runTest {
- val latest by collectLastValue(underTest.shownNotificationChips)
+ val latest by collectLastValue(underTest.allNotificationChips)
val notif1Info = NotifInfo("notif1", mock<StatusBarIconView>(), uid = 100)
val notif2Info = NotifInfo("notif2", mock<StatusBarIconView>(), uid = 200)
@@ -537,16 +540,14 @@ class StatusBarNotificationChipsInteractorTest : SysuiTestCase() {
key = notif1Info.key,
uid = notif1Info.uid,
statusBarChipIcon = notif1Info.icon,
- promotedContent =
- PromotedNotificationContentModel.Builder(notif1Info.key).build(),
+ promotedContent = PromotedNotificationContentBuilder(notif1Info.key).build(),
)
val notif2 =
activeNotificationModel(
key = notif2Info.key,
uid = notif2Info.uid,
statusBarChipIcon = notif2Info.icon,
- promotedContent =
- PromotedNotificationContentModel.Builder(notif2Info.key).build(),
+ promotedContent = PromotedNotificationContentBuilder(notif2Info.key).build(),
)
setNotifs(listOf(notif1, notif2))
assertThat(latest!!.map { it.key }).containsExactly("notif1", "notif2").inOrder()
@@ -567,8 +568,7 @@ class StatusBarNotificationChipsInteractorTest : SysuiTestCase() {
key = notif3Info.key,
uid = notif3Info.uid,
statusBarChipIcon = notif3Info.icon,
- promotedContent =
- PromotedNotificationContentModel.Builder(notif3Info.key).build(),
+ promotedContent = PromotedNotificationContentBuilder(notif3Info.key).build(),
)
setNotifs(listOf(notif1, notif2, notif3))
@@ -581,9 +581,9 @@ class StatusBarNotificationChipsInteractorTest : SysuiTestCase() {
@Test
@EnableFlags(StatusBarNotifChips.FLAG_NAME)
- fun shownNotificationChips_fullSort() =
+ fun allNotificationChips_fullSort() =
kosmos.runTest {
- val latest by collectLastValue(underTest.shownNotificationChips)
+ val latest by collectLastValue(underTest.allNotificationChips)
val notif1Info = NotifInfo("notif1", mock<StatusBarIconView>(), uid = 100)
val notif2Info = NotifInfo("notif2", mock<StatusBarIconView>(), uid = 200)
@@ -597,8 +597,7 @@ class StatusBarNotificationChipsInteractorTest : SysuiTestCase() {
key = notif1Info.key,
uid = notif1Info.uid,
statusBarChipIcon = notif1Info.icon,
- promotedContent =
- PromotedNotificationContentModel.Builder(notif1Info.key).build(),
+ promotedContent = PromotedNotificationContentBuilder(notif1Info.key).build(),
)
setNotifs(listOf(notif1))
@@ -609,8 +608,7 @@ class StatusBarNotificationChipsInteractorTest : SysuiTestCase() {
key = notif2Info.key,
uid = notif2Info.uid,
statusBarChipIcon = notif2Info.icon,
- promotedContent =
- PromotedNotificationContentModel.Builder(notif2Info.key).build(),
+ promotedContent = PromotedNotificationContentBuilder(notif2Info.key).build(),
)
setNotifs(listOf(notif1, notif2))
@@ -637,7 +635,7 @@ class StatusBarNotificationChipsInteractorTest : SysuiTestCase() {
// WHEN notif2 gets an update
val notif2NewPromotedContent =
- PromotedNotificationContentModel.Builder("notif2").apply {
+ PromotedNotificationContentBuilder("notif2").applyToShared {
this.shortCriticalText = "Arrived"
}
setNotifs(
@@ -662,8 +660,7 @@ class StatusBarNotificationChipsInteractorTest : SysuiTestCase() {
key = notif3Info.key,
uid = notif3Info.uid,
statusBarChipIcon = notif3Info.icon,
- promotedContent =
- PromotedNotificationContentModel.Builder(notif3Info.key).build(),
+ promotedContent = PromotedNotificationContentBuilder(notif3Info.key).build(),
)
setNotifs(listOf(notif1, notif2, notif3))
@@ -699,9 +696,9 @@ class StatusBarNotificationChipsInteractorTest : SysuiTestCase() {
@Test
@EnableFlags(StatusBarNotifChips.FLAG_NAME)
- fun shownNotificationChips_notifChangesKey() =
+ fun allNotificationChips_notifChangesKey() =
kosmos.runTest {
- val latest by collectLastValue(underTest.shownNotificationChips)
+ val latest by collectLastValue(underTest.allNotificationChips)
val firstIcon = mock<StatusBarIconView>()
val secondIcon = mock<StatusBarIconView>()
@@ -710,8 +707,7 @@ class StatusBarNotificationChipsInteractorTest : SysuiTestCase() {
activeNotificationModel(
key = "notif|uid1",
statusBarChipIcon = firstIcon,
- promotedContent =
- PromotedNotificationContentModel.Builder("notif|uid1").build(),
+ promotedContent = PromotedNotificationContentBuilder("notif|uid1").build(),
)
)
)
@@ -725,8 +721,7 @@ class StatusBarNotificationChipsInteractorTest : SysuiTestCase() {
activeNotificationModel(
key = "notif|uid2",
statusBarChipIcon = secondIcon,
- promotedContent =
- PromotedNotificationContentModel.Builder("notif|uid2").build(),
+ promotedContent = PromotedNotificationContentBuilder("notif|uid2").build(),
)
)
)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModelTest.kt
index b5cfc7e9080d..1ee4b0f4d96f 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModelTest.kt
@@ -22,8 +22,11 @@ import android.platform.test.annotations.EnableFlags
import android.view.View
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import com.android.internal.logging.InstanceId
import com.android.systemui.Flags.FLAG_PROMOTE_NOTIFICATIONS_AUTOMATICALLY
import com.android.systemui.SysuiTestCase
+import com.android.systemui.activity.data.repository.activityManagerRepository
+import com.android.systemui.activity.data.repository.fake
import com.android.systemui.common.shared.model.ContentDescription.Companion.loadContentDescription
import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
@@ -43,6 +46,7 @@ import com.android.systemui.statusbar.notification.data.repository.ActiveNotific
import com.android.systemui.statusbar.notification.data.repository.UnconfinedFakeHeadsUpRowRepository
import com.android.systemui.statusbar.notification.data.repository.activeNotificationListRepository
import com.android.systemui.statusbar.notification.headsup.PinnedStatus
+import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentBuilder
import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel
import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel.When
import com.android.systemui.statusbar.notification.shared.ActiveNotificationModel
@@ -101,7 +105,7 @@ class NotifChipsViewModelTest : SysuiTestCase() {
activeNotificationModel(
key = "notif",
statusBarChipIcon = null,
- promotedContent = PromotedNotificationContentModel.Builder("notif").build(),
+ promotedContent = PromotedNotificationContentBuilder("notif").build(),
)
)
)
@@ -121,7 +125,7 @@ class NotifChipsViewModelTest : SysuiTestCase() {
activeNotificationModel(
key = "notif",
statusBarChipIcon = null,
- promotedContent = PromotedNotificationContentModel.Builder("notif").build(),
+ promotedContent = PromotedNotificationContentBuilder("notif").build(),
)
)
)
@@ -142,7 +146,7 @@ class NotifChipsViewModelTest : SysuiTestCase() {
key = "notif",
appName = "Fake App Name",
statusBarChipIcon = icon,
- promotedContent = PromotedNotificationContentModel.Builder("notif").build(),
+ promotedContent = PromotedNotificationContentBuilder("notif").build(),
)
)
)
@@ -172,7 +176,7 @@ class NotifChipsViewModelTest : SysuiTestCase() {
key = notifKey,
appName = "Fake App Name",
statusBarChipIcon = null,
- promotedContent = PromotedNotificationContentModel.Builder(notifKey).build(),
+ promotedContent = PromotedNotificationContentBuilder(notifKey).build(),
)
)
)
@@ -195,7 +199,7 @@ class NotifChipsViewModelTest : SysuiTestCase() {
val latest by collectLastValue(underTest.chips)
val promotedContentBuilder =
- PromotedNotificationContentModel.Builder("notif").apply {
+ PromotedNotificationContentBuilder("notif").applyToShared {
this.colors =
PromotedNotificationContentModel.Colors(
backgroundColor = 56,
@@ -229,12 +233,12 @@ class NotifChipsViewModelTest : SysuiTestCase() {
activeNotificationModel(
key = "notif1",
statusBarChipIcon = firstIcon,
- promotedContent = PromotedNotificationContentModel.Builder("notif1").build(),
+ promotedContent = PromotedNotificationContentBuilder("notif1").build(),
),
activeNotificationModel(
key = "notif2",
statusBarChipIcon = secondIcon,
- promotedContent = PromotedNotificationContentModel.Builder("notif2").build(),
+ promotedContent = PromotedNotificationContentBuilder("notif2").build(),
),
activeNotificationModel(
key = "notif3",
@@ -264,13 +268,12 @@ class NotifChipsViewModelTest : SysuiTestCase() {
activeNotificationModel(
key = firstKey,
statusBarChipIcon = null,
- promotedContent = PromotedNotificationContentModel.Builder(firstKey).build(),
+ promotedContent = PromotedNotificationContentBuilder(firstKey).build(),
),
activeNotificationModel(
key = secondKey,
statusBarChipIcon = null,
- promotedContent =
- PromotedNotificationContentModel.Builder(secondKey).build(),
+ promotedContent = PromotedNotificationContentBuilder(secondKey).build(),
),
activeNotificationModel(
key = thirdKey,
@@ -286,6 +289,84 @@ class NotifChipsViewModelTest : SysuiTestCase() {
}
@Test
+ fun chips_appStartsAsVisible_isHiddenTrue() =
+ kosmos.runTest {
+ activityManagerRepository.fake.startingIsAppVisibleValue = true
+
+ val latest by collectLastValue(underTest.chips)
+
+ val uid = 433
+ setNotifs(
+ listOf(
+ activeNotificationModel(
+ key = "notif",
+ uid = uid,
+ statusBarChipIcon = createStatusBarIconViewOrNull(),
+ promotedContent = PromotedNotificationContentBuilder("notif").build(),
+ )
+ )
+ )
+
+ assertThat(latest).hasSize(1)
+ assertThat(latest!![0].isHidden).isTrue()
+ }
+
+ @Test
+ fun chips_appStartsAsNotVisible_isHiddenFalse() =
+ kosmos.runTest {
+ activityManagerRepository.fake.startingIsAppVisibleValue = false
+
+ val latest by collectLastValue(underTest.chips)
+
+ val uid = 433
+ setNotifs(
+ listOf(
+ activeNotificationModel(
+ key = "notif",
+ uid = uid,
+ statusBarChipIcon = createStatusBarIconViewOrNull(),
+ promotedContent = PromotedNotificationContentBuilder("notif").build(),
+ )
+ )
+ )
+
+ assertThat(latest).hasSize(1)
+ assertThat(latest!![0].isHidden).isFalse()
+ }
+
+ @Test
+ fun chips_isHidden_changesBasedOnAppVisibility() =
+ kosmos.runTest {
+ activityManagerRepository.fake.startingIsAppVisibleValue = false
+
+ val latest by collectLastValue(underTest.chips)
+
+ val uid = 433
+ setNotifs(
+ listOf(
+ activeNotificationModel(
+ key = "notif",
+ uid = uid,
+ statusBarChipIcon = createStatusBarIconViewOrNull(),
+ promotedContent = PromotedNotificationContentBuilder("notif").build(),
+ )
+ )
+ )
+
+ activityManagerRepository.fake.setIsAppVisible(uid, isAppVisible = false)
+ assertThat(latest).hasSize(1)
+ assertThat(latest!![0].isHidden).isFalse()
+
+ activityManagerRepository.fake.setIsAppVisible(uid, isAppVisible = true)
+ assertThat(latest).hasSize(1)
+ assertThat(latest!![0].isHidden).isTrue()
+
+ activityManagerRepository.fake.setIsAppVisible(uid, isAppVisible = false)
+ assertThat(latest).hasSize(1)
+ assertThat(latest!![0].isHidden).isFalse()
+ }
+
+ @Test
@DisableFlags(FLAG_PROMOTE_NOTIFICATIONS_AUTOMATICALLY)
fun chips_hasShortCriticalText_usesTextInsteadOfTime() =
kosmos.runTest {
@@ -294,7 +375,7 @@ class NotifChipsViewModelTest : SysuiTestCase() {
fakeSystemClock.setCurrentTimeMillis(currentTime)
val promotedContentBuilder =
- PromotedNotificationContentModel.Builder("notif").apply {
+ PromotedNotificationContentBuilder("notif").applyToShared {
this.shortCriticalText = "Arrived"
this.time = When.Time(currentTime + 30.minutes.inWholeMilliseconds)
}
@@ -316,12 +397,39 @@ class NotifChipsViewModelTest : SysuiTestCase() {
@Test
@DisableFlags(FLAG_PROMOTE_NOTIFICATIONS_AUTOMATICALLY)
+ fun chips_shortCriticalText_usesInstanceId() =
+ kosmos.runTest {
+ val latest by collectLastValue(underTest.chips)
+
+ val promotedContentBuilder =
+ PromotedNotificationContentBuilder("notif").applyToShared {
+ this.shortCriticalText = "Arrived"
+ }
+ val instanceId = InstanceId.fakeInstanceId(30)
+ setNotifs(
+ listOf(
+ activeNotificationModel(
+ key = "notif",
+ statusBarChipIcon = createStatusBarIconViewOrNull(),
+ promotedContent = promotedContentBuilder.build(),
+ instanceId = instanceId,
+ )
+ )
+ )
+
+ assertThat(latest).hasSize(1)
+ assertThat(latest!![0]).isInstanceOf(OngoingActivityChipModel.Active::class.java)
+ assertThat(latest!![0].instanceId).isEqualTo(instanceId)
+ }
+
+ @Test
+ @DisableFlags(FLAG_PROMOTE_NOTIFICATIONS_AUTOMATICALLY)
fun chips_noTime_isIconOnly() =
kosmos.runTest {
val latest by collectLastValue(underTest.chips)
val promotedContentBuilder =
- PromotedNotificationContentModel.Builder("notif").apply { this.time = null }
+ PromotedNotificationContentBuilder("notif").applyToShared { this.time = null }
setNotifs(
listOf(
activeNotificationModel(
@@ -346,7 +454,7 @@ class NotifChipsViewModelTest : SysuiTestCase() {
fakeSystemClock.setCurrentTimeMillis(currentTime)
val promotedContentBuilder =
- PromotedNotificationContentModel.Builder("notif").apply {
+ PromotedNotificationContentBuilder("notif").applyToShared {
this.wasPromotedAutomatically = true
this.time = When.Time(currentTime + 30.minutes.inWholeMilliseconds)
}
@@ -374,7 +482,7 @@ class NotifChipsViewModelTest : SysuiTestCase() {
fakeSystemClock.setCurrentTimeMillis(currentTime)
val promotedContentBuilder =
- PromotedNotificationContentModel.Builder("notif").apply {
+ PromotedNotificationContentBuilder("notif").applyToShared {
this.wasPromotedAutomatically = false
this.time = When.Time(currentTime + 30.minutes.inWholeMilliseconds)
}
@@ -402,7 +510,7 @@ class NotifChipsViewModelTest : SysuiTestCase() {
fakeSystemClock.setCurrentTimeMillis(currentTime)
val promotedContentBuilder =
- PromotedNotificationContentModel.Builder("notif").apply {
+ PromotedNotificationContentBuilder("notif").applyToShared {
this.time = When.Time(currentTime + 13.minutes.inWholeMilliseconds)
}
@@ -422,6 +530,42 @@ class NotifChipsViewModelTest : SysuiTestCase() {
}
@Test
+ fun chips_basicTime_respectsIsAppVisible() =
+ kosmos.runTest {
+ activityManagerRepository.fake.startingIsAppVisibleValue = false
+
+ val latest by collectLastValue(underTest.chips)
+ val currentTime = 3.minutes.inWholeMilliseconds
+ fakeSystemClock.setCurrentTimeMillis(currentTime)
+
+ val promotedContentBuilder =
+ PromotedNotificationContentBuilder("notif").applyToShared {
+ this.time = When.Time(currentTime + 13.minutes.inWholeMilliseconds)
+ }
+ val uid = 3
+
+ setNotifs(
+ listOf(
+ activeNotificationModel(
+ key = "notif",
+ uid = 3,
+ statusBarChipIcon = createStatusBarIconViewOrNull(),
+ promotedContent = promotedContentBuilder.build(),
+ )
+ )
+ )
+
+ assertThat(latest).hasSize(1)
+ assertThat(latest!![0])
+ .isInstanceOf(OngoingActivityChipModel.Active.ShortTimeDelta::class.java)
+ assertThat(latest!![0].isHidden).isFalse()
+
+ activityManagerRepository.fake.setIsAppVisible(uid = uid, isAppVisible = true)
+
+ assertThat(latest!![0].isHidden).isTrue()
+ }
+
+ @Test
@DisableFlags(FLAG_PROMOTE_NOTIFICATIONS_AUTOMATICALLY)
fun chips_basicTime_timeLessThanOneMinInFuture_isIconOnly() =
kosmos.runTest {
@@ -430,7 +574,7 @@ class NotifChipsViewModelTest : SysuiTestCase() {
fakeSystemClock.setCurrentTimeMillis(currentTime)
val promotedContentBuilder =
- PromotedNotificationContentModel.Builder("notif").apply {
+ PromotedNotificationContentBuilder("notif").applyToShared {
this.time = When.Time(currentTime + 500)
}
@@ -458,7 +602,7 @@ class NotifChipsViewModelTest : SysuiTestCase() {
fakeSystemClock.setCurrentTimeMillis(currentTime)
val promotedContentBuilder =
- PromotedNotificationContentModel.Builder("notif").apply {
+ PromotedNotificationContentBuilder("notif").applyToShared {
this.time = When.Time(currentTime)
}
@@ -486,7 +630,7 @@ class NotifChipsViewModelTest : SysuiTestCase() {
fakeSystemClock.setCurrentTimeMillis(currentTime)
val promotedContentBuilder =
- PromotedNotificationContentModel.Builder("notif").apply {
+ PromotedNotificationContentBuilder("notif").applyToShared {
this.time = When.Time(currentTime - 2.minutes.inWholeMilliseconds)
}
@@ -515,7 +659,7 @@ class NotifChipsViewModelTest : SysuiTestCase() {
fakeSystemClock.setCurrentTimeMillis(currentTime)
val promotedContentBuilder =
- PromotedNotificationContentModel.Builder("notif").apply {
+ PromotedNotificationContentBuilder("notif").applyToShared {
this.time = When.Time(currentTime + 3.minutes.inWholeMilliseconds)
}
@@ -555,7 +699,7 @@ class NotifChipsViewModelTest : SysuiTestCase() {
val whenElapsed = currentElapsed - 1.minutes.inWholeMilliseconds
val promotedContentBuilder =
- PromotedNotificationContentModel.Builder("notif").apply {
+ PromotedNotificationContentBuilder("notif").applyToShared {
this.time =
When.Chronometer(elapsedRealtimeMillis = whenElapsed, isCountDown = false)
}
@@ -579,6 +723,48 @@ class NotifChipsViewModelTest : SysuiTestCase() {
@Test
@DisableFlags(FLAG_PROMOTE_NOTIFICATIONS_AUTOMATICALLY)
+ fun chips_countUpTime_respectsIsAppVisible() =
+ kosmos.runTest {
+ activityManagerRepository.fake.startingIsAppVisibleValue = true
+
+ val latest by collectLastValue(underTest.chips)
+ val currentTime = 30.minutes.inWholeMilliseconds
+ fakeSystemClock.setCurrentTimeMillis(currentTime)
+
+ val currentElapsed =
+ currentTime + fakeSystemClock.elapsedRealtime() -
+ fakeSystemClock.currentTimeMillis()
+
+ val whenElapsed = currentElapsed - 1.minutes.inWholeMilliseconds
+
+ val promotedContentBuilder =
+ PromotedNotificationContentBuilder("notif").applyToShared {
+ this.time =
+ When.Chronometer(elapsedRealtimeMillis = whenElapsed, isCountDown = false)
+ }
+ val uid = 6
+ setNotifs(
+ listOf(
+ activeNotificationModel(
+ key = "notif",
+ uid = uid,
+ statusBarChipIcon = createStatusBarIconViewOrNull(),
+ promotedContent = promotedContentBuilder.build(),
+ )
+ )
+ )
+
+ assertThat(latest).hasSize(1)
+ assertThat(latest!![0]).isInstanceOf(OngoingActivityChipModel.Active.Timer::class.java)
+ assertThat(latest!![0].isHidden).isTrue()
+
+ activityManagerRepository.fake.setIsAppVisible(uid, isAppVisible = false)
+
+ assertThat(latest!![0].isHidden).isFalse()
+ }
+
+ @Test
+ @DisableFlags(FLAG_PROMOTE_NOTIFICATIONS_AUTOMATICALLY)
fun chips_countDownTime_isTimer() =
kosmos.runTest {
val latest by collectLastValue(underTest.chips)
@@ -592,7 +778,7 @@ class NotifChipsViewModelTest : SysuiTestCase() {
val whenElapsed = currentElapsed + 10.minutes.inWholeMilliseconds
val promotedContentBuilder =
- PromotedNotificationContentModel.Builder("notif").apply {
+ PromotedNotificationContentBuilder("notif").applyToShared {
this.time =
When.Chronometer(elapsedRealtimeMillis = whenElapsed, isCountDown = true)
}
@@ -616,6 +802,41 @@ class NotifChipsViewModelTest : SysuiTestCase() {
@Test
@DisableFlags(FLAG_PROMOTE_NOTIFICATIONS_AUTOMATICALLY)
+ fun chips_countDownTime_usesInstanceId() =
+ kosmos.runTest {
+ val latest by collectLastValue(underTest.chips)
+
+ val instanceId = InstanceId.fakeInstanceId(20)
+ val currentTime = 30.minutes.inWholeMilliseconds
+ fakeSystemClock.setCurrentTimeMillis(currentTime)
+
+ val currentElapsed =
+ currentTime + fakeSystemClock.elapsedRealtime() -
+ fakeSystemClock.currentTimeMillis()
+ val whenElapsed = currentElapsed + 10.minutes.inWholeMilliseconds
+ val promotedContentBuilder =
+ PromotedNotificationContentBuilder("notif").applyToShared {
+ this.time =
+ When.Chronometer(elapsedRealtimeMillis = whenElapsed, isCountDown = true)
+ }
+ setNotifs(
+ listOf(
+ activeNotificationModel(
+ key = "notif",
+ statusBarChipIcon = createStatusBarIconViewOrNull(),
+ promotedContent = promotedContentBuilder.build(),
+ instanceId = instanceId,
+ )
+ )
+ )
+
+ assertThat(latest).hasSize(1)
+ assertThat(latest!![0]).isInstanceOf(OngoingActivityChipModel.Active::class.java)
+ assertThat((latest!![0]).instanceId).isEqualTo(instanceId)
+ }
+
+ @Test
+ @DisableFlags(FLAG_PROMOTE_NOTIFICATIONS_AUTOMATICALLY)
fun chips_noHeadsUp_showsTime() =
kosmos.runTest {
val latest by collectLastValue(underTest.chips)
@@ -623,7 +844,7 @@ class NotifChipsViewModelTest : SysuiTestCase() {
fakeSystemClock.setCurrentTimeMillis(currentTime)
val promotedContentBuilder =
- PromotedNotificationContentModel.Builder("notif").apply {
+ PromotedNotificationContentBuilder("notif").applyToShared {
this.time = When.Time(currentTime + 10.minutes.inWholeMilliseconds)
}
setNotifs(
@@ -653,7 +874,7 @@ class NotifChipsViewModelTest : SysuiTestCase() {
fakeSystemClock.setCurrentTimeMillis(currentTime)
val promotedContentBuilder =
- PromotedNotificationContentModel.Builder("notif").apply {
+ PromotedNotificationContentBuilder("notif").applyToShared {
this.time = When.Time(currentTime + 10.minutes.inWholeMilliseconds)
}
setNotifs(
@@ -690,11 +911,11 @@ class NotifChipsViewModelTest : SysuiTestCase() {
fakeSystemClock.setCurrentTimeMillis(currentTime)
val promotedContentBuilder =
- PromotedNotificationContentModel.Builder("notif").apply {
+ PromotedNotificationContentBuilder("notif").applyToShared {
this.time = When.Time(currentTime + 10.minutes.inWholeMilliseconds)
}
val otherPromotedContentBuilder =
- PromotedNotificationContentModel.Builder("other notif").apply {
+ PromotedNotificationContentBuilder("other notif").applyToShared {
this.time = When.Time(currentTime + 10.minutes.inWholeMilliseconds)
}
val icon = createStatusBarIconViewOrNull()
@@ -738,7 +959,7 @@ class NotifChipsViewModelTest : SysuiTestCase() {
fakeSystemClock.setCurrentTimeMillis(currentTime)
val promotedContentBuilder =
- PromotedNotificationContentModel.Builder("notif").apply {
+ PromotedNotificationContentBuilder("notif").applyToShared {
this.time = When.Time(currentTime + 10.minutes.inWholeMilliseconds)
}
setNotifs(
@@ -781,7 +1002,7 @@ class NotifChipsViewModelTest : SysuiTestCase() {
activeNotificationModel(
key,
statusBarChipIcon = createStatusBarIconViewOrNull(),
- promotedContent = PromotedNotificationContentModel.Builder(key).build(),
+ promotedContent = PromotedNotificationContentBuilder(key).build(),
)
)
)
@@ -809,7 +1030,7 @@ class NotifChipsViewModelTest : SysuiTestCase() {
activeNotificationModel(
key,
statusBarChipIcon = createStatusBarIconViewOrNull(),
- promotedContent = PromotedNotificationContentModel.Builder(key).build(),
+ promotedContent = PromotedNotificationContentBuilder(key).build(),
)
)
)
@@ -842,6 +1063,7 @@ class NotifChipsViewModelTest : SysuiTestCase() {
expectedContentDescriptionSubstrings: List<String> = emptyList(),
) {
val active = latest as OngoingActivityChipModel.Active
+ assertThat(active.isImportantForPrivacy).isFalse()
if (StatusBarConnectedDisplays.isEnabled) {
assertThat(active.icon)
.isInstanceOf(
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/screenrecord/domain/interactor/ScreenRecordChipInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/screenrecord/domain/interactor/ScreenRecordChipInteractorTest.kt
index 538247e26a6b..f8c57655005e 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/screenrecord/domain/interactor/ScreenRecordChipInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/screenrecord/domain/interactor/ScreenRecordChipInteractorTest.kt
@@ -87,7 +87,7 @@ class ScreenRecordChipInteractorTest : SysuiTestCase() {
screenRecordRepo.screenRecordState.value = ScreenRecordModel.Recording
mediaProjectionRepo.mediaProjectionState.value = MediaProjectionState.NotProjecting
- assertThat(latest).isEqualTo(ScreenRecordChipModel.Recording(recordedTask = null))
+ assertThat((latest as ScreenRecordChipModel.Recording).recordedTask).isNull()
}
@Test
@@ -99,7 +99,7 @@ class ScreenRecordChipInteractorTest : SysuiTestCase() {
mediaProjectionRepo.mediaProjectionState.value =
MediaProjectionState.Projecting.EntireScreen("host.package")
- assertThat(latest).isEqualTo(ScreenRecordChipModel.Recording(recordedTask = null))
+ assertThat((latest as ScreenRecordChipModel.Recording).recordedTask).isNull()
}
@Test
@@ -116,7 +116,48 @@ class ScreenRecordChipInteractorTest : SysuiTestCase() {
task,
)
- assertThat(latest).isEqualTo(ScreenRecordChipModel.Recording(recordedTask = task))
+ assertThat((latest as ScreenRecordChipModel.Recording).recordedTask).isEqualTo(task)
+ }
+
+ @Test
+ fun screenRecordState_projectionIsNotProjecting_hostPackageNull() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.screenRecordState)
+
+ screenRecordRepo.screenRecordState.value = ScreenRecordModel.Recording
+ mediaProjectionRepo.mediaProjectionState.value = MediaProjectionState.NotProjecting
+
+ assertThat((latest as ScreenRecordChipModel.Recording).hostPackage).isNull()
+ }
+
+ @Test
+ fun screenRecordState_projectionIsEntireScreen_hostPackageMatches() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.screenRecordState)
+
+ screenRecordRepo.screenRecordState.value = ScreenRecordModel.Recording
+ mediaProjectionRepo.mediaProjectionState.value =
+ MediaProjectionState.Projecting.EntireScreen(hostPackage = "host.package")
+
+ assertThat((latest as ScreenRecordChipModel.Recording).hostPackage)
+ .isEqualTo("host.package")
+ }
+
+ @Test
+ fun screenRecordState_projectionIsSingleTask_hostPackageMatches() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.screenRecordState)
+
+ screenRecordRepo.screenRecordState.value = ScreenRecordModel.Recording
+ mediaProjectionRepo.mediaProjectionState.value =
+ MediaProjectionState.Projecting.SingleTask(
+ hostPackage = "host.package",
+ hostDeviceName = null,
+ task = createTask(taskId = 1),
+ )
+
+ assertThat((latest as ScreenRecordChipModel.Recording).hostPackage)
+ .isEqualTo("host.package")
}
@Test
@@ -150,7 +191,7 @@ class ScreenRecordChipInteractorTest : SysuiTestCase() {
advanceTimeBy(901)
// THEN we automatically update to the recording state
- assertThat(latest).isEqualTo(ScreenRecordChipModel.Recording(recordedTask = null))
+ assertThat(latest).isInstanceOf(ScreenRecordChipModel.Recording::class.java)
}
@Test
@@ -175,13 +216,14 @@ class ScreenRecordChipInteractorTest : SysuiTestCase() {
)
// THEN we immediately switch to Recording, and we have the task
- assertThat(latest).isEqualTo(ScreenRecordChipModel.Recording(recordedTask = task))
+ assertThat(latest).isInstanceOf(ScreenRecordChipModel.Recording::class.java)
+ assertThat((latest as ScreenRecordChipModel.Recording).recordedTask).isEqualTo(task)
// WHEN more than 900ms has elapsed
advanceTimeBy(200)
// THEN we still stay in the Recording state and we have the task
- assertThat(latest).isEqualTo(ScreenRecordChipModel.Recording(recordedTask = task))
+ assertThat((latest as ScreenRecordChipModel.Recording).recordedTask).isEqualTo(task)
}
@Test
@@ -247,7 +289,7 @@ class ScreenRecordChipInteractorTest : SysuiTestCase() {
// THEN we *do* auto-start 400ms later
advanceTimeBy(401)
- assertThat(latest).isEqualTo(ScreenRecordChipModel.Recording(recordedTask = null))
+ assertThat(latest).isInstanceOf(ScreenRecordChipModel.Recording::class.java)
}
@Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/screenrecord/ui/viewmodel/ScreenRecordChipViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/screenrecord/ui/viewmodel/ScreenRecordChipViewModelTest.kt
index 005af366a6c0..3e67e983a680 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/screenrecord/ui/viewmodel/ScreenRecordChipViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/screenrecord/ui/viewmodel/ScreenRecordChipViewModelTest.kt
@@ -110,6 +110,8 @@ class ScreenRecordChipViewModelTest : SysuiTestCase() {
screenRecordRepo.screenRecordState.value = ScreenRecordModel.Starting(400)
assertThat(latest).isInstanceOf(OngoingActivityChipModel.Active.Countdown::class.java)
+ assertThat((latest as OngoingActivityChipModel.Active).isImportantForPrivacy).isTrue()
+ assertThat((latest as OngoingActivityChipModel.Active).instanceId).isNotNull()
assertThat((latest as OngoingActivityChipModel.Active).icon).isNull()
assertThat((latest as OngoingActivityChipModel.Active).onClickListenerLegacy).isNull()
}
@@ -156,6 +158,8 @@ class ScreenRecordChipViewModelTest : SysuiTestCase() {
screenRecordRepo.screenRecordState.value = ScreenRecordModel.Recording
assertThat(latest).isInstanceOf(OngoingActivityChipModel.Active.Timer::class.java)
+ assertThat((latest as OngoingActivityChipModel.Active).isImportantForPrivacy).isTrue()
+ assertThat((latest as OngoingActivityChipModel.Active).instanceId).isNotNull()
val icon =
(((latest as OngoingActivityChipModel.Active).icon)
as OngoingActivityChipModel.ChipIcon.SingleColorIcon)
@@ -261,6 +265,7 @@ class ScreenRecordChipViewModelTest : SysuiTestCase() {
mediaProjectionRepo.mediaProjectionState.value = MediaProjectionState.NotProjecting
assertThat(latest).isInstanceOf(OngoingActivityChipModel.Active::class.java)
+ assertThat((latest as OngoingActivityChipModel.Active).isImportantForPrivacy).isTrue()
assertThat((latest as OngoingActivityChipModel.Active.Timer).startTimeMs)
.isEqualTo(1234)
@@ -275,6 +280,7 @@ class ScreenRecordChipViewModelTest : SysuiTestCase() {
// THEN the start time is still the old start time
assertThat(latest).isInstanceOf(OngoingActivityChipModel.Active::class.java)
+ assertThat((latest as OngoingActivityChipModel.Active).isImportantForPrivacy).isTrue()
assertThat((latest as OngoingActivityChipModel.Active.Timer).startTimeMs)
.isEqualTo(1234)
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/sharetoapp/ui/viewmodel/ShareToAppChipViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/sharetoapp/ui/viewmodel/ShareToAppChipViewModelTest.kt
index d6b10a89726e..ad556b7d4675 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/sharetoapp/ui/viewmodel/ShareToAppChipViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/sharetoapp/ui/viewmodel/ShareToAppChipViewModelTest.kt
@@ -384,6 +384,9 @@ class ShareToAppChipViewModelTest : SysuiTestCase() {
MediaProjectionState.Projecting.NoScreen(NORMAL_PACKAGE, hostDeviceName = null)
assertThat(latest).isInstanceOf(OngoingActivityChipModel.Active.IconOnly::class.java)
+ assertThat((latest as OngoingActivityChipModel.Active).isImportantForPrivacy).isTrue()
+ assertThat((latest as OngoingActivityChipModel.Active).instanceId).isNotNull()
+
val icon =
(((latest as OngoingActivityChipModel.Active).icon)
as OngoingActivityChipModel.ChipIcon.SingleColorIcon)
@@ -407,6 +410,9 @@ class ShareToAppChipViewModelTest : SysuiTestCase() {
)
assertThat(latest).isInstanceOf(OngoingActivityChipModel.Active.Timer::class.java)
+ assertThat((latest as OngoingActivityChipModel.Active).isImportantForPrivacy).isTrue()
+ assertThat((latest as OngoingActivityChipModel.Active).instanceId).isNotNull()
+
val icon =
(((latest as OngoingActivityChipModel.Active).icon)
as OngoingActivityChipModel.ChipIcon.SingleColorIcon)
@@ -424,6 +430,9 @@ class ShareToAppChipViewModelTest : SysuiTestCase() {
MediaProjectionState.Projecting.EntireScreen(NORMAL_PACKAGE)
assertThat(latest).isInstanceOf(OngoingActivityChipModel.Active.Timer::class.java)
+ assertThat((latest as OngoingActivityChipModel.Active).isImportantForPrivacy).isTrue()
+ assertThat((latest as OngoingActivityChipModel.Active).instanceId).isNotNull()
+
val icon =
(((latest as OngoingActivityChipModel.Active).icon)
as OngoingActivityChipModel.ChipIcon.SingleColorIcon)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipViewModelTest.kt
index 39b19d3c4191..0eddcd563663 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipViewModelTest.kt
@@ -20,6 +20,7 @@ import android.view.View
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.internal.jank.Cuj
+import com.android.internal.logging.InstanceId
import com.android.systemui.SysuiTestCase
import com.android.systemui.animation.DialogCuj
import com.android.systemui.animation.DialogTransitionAnimator
@@ -29,9 +30,11 @@ import com.android.systemui.res.R
import com.android.systemui.statusbar.chips.ui.view.ChipBackgroundContainer
import com.android.systemui.statusbar.chips.ui.viewmodel.OngoingActivityChipViewModel.Companion.createDialogLaunchOnClickCallback
import com.android.systemui.statusbar.chips.ui.viewmodel.OngoingActivityChipViewModel.Companion.createDialogLaunchOnClickListener
+import com.android.systemui.statusbar.chips.uievents.statusBarChipsUiEventLogger
import com.android.systemui.statusbar.phone.SystemUIDialog
import com.android.systemui.statusbar.phone.ongoingcall.DisableChipsModernization
import com.android.systemui.statusbar.phone.ongoingcall.EnableChipsModernization
+import com.android.systemui.testKosmos
import kotlin.test.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentMatchers.anyBoolean
@@ -44,6 +47,7 @@ import org.mockito.kotlin.whenever
@SmallTest
@RunWith(AndroidJUnit4::class)
class OngoingActivityChipViewModelTest : SysuiTestCase() {
+ private val kosmos = testKosmos()
private val mockSystemUIDialog = mock<SystemUIDialog>()
private val dialogDelegate = SystemUIDialog.Delegate { mockSystemUIDialog }
private val dialogTransitionAnimator = mock<DialogTransitionAnimator>()
@@ -70,8 +74,10 @@ class OngoingActivityChipViewModelTest : SysuiTestCase() {
dialogDelegate,
dialogTransitionAnimator,
cuj,
- logcatLogBuffer("OngoingActivityChipViewModelTest"),
- "tag",
+ instanceId = InstanceId.fakeInstanceId(0),
+ uiEventLogger = kosmos.statusBarChipsUiEventLogger,
+ logger = logcatLogBuffer("OngoingActivityChipViewModelTest"),
+ tag = "tag",
)
clickListener.onClick(chipView)
@@ -88,8 +94,10 @@ class OngoingActivityChipViewModelTest : SysuiTestCase() {
dialogDelegate,
dialogTransitionAnimator,
cuj,
- logcatLogBuffer("OngoingActivityChipViewModelTest"),
- "tag",
+ instanceId = InstanceId.fakeInstanceId(0),
+ uiEventLogger = kosmos.statusBarChipsUiEventLogger,
+ logger = logcatLogBuffer("OngoingActivityChipViewModelTest"),
+ tag = "tag",
)
clickCallback.invoke(mockExpandable)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelTest.kt
index e39fa7099953..4f0dfd373116 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelTest.kt
@@ -41,6 +41,7 @@ import com.android.systemui.mediaprojection.taskswitcher.FakeActivityTaskManager
import com.android.systemui.res.R
import com.android.systemui.screenrecord.data.model.ScreenRecordModel
import com.android.systemui.screenrecord.data.repository.screenRecordRepository
+import com.android.systemui.statusbar.chips.call.ui.viewmodel.CallChipViewModel
import com.android.systemui.statusbar.chips.mediaprojection.domain.interactor.MediaProjectionChipInteractorTest.Companion.NORMAL_PACKAGE
import com.android.systemui.statusbar.chips.mediaprojection.domain.interactor.MediaProjectionChipInteractorTest.Companion.setUpPackageManagerForMediaProjection
import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips
@@ -402,7 +403,8 @@ class OngoingActivityChipsViewModelTest : SysuiTestCase() {
context: Context,
) {
assertThat(latest).isInstanceOf(OngoingActivityChipModel.Active::class.java)
- assertThat((latest as OngoingActivityChipModel.Active).key).isEqualTo(notificationKey)
+ assertThat((latest as OngoingActivityChipModel.Active).key)
+ .isEqualTo("${CallChipViewModel.KEY_PREFIX}$notificationKey")
if (StatusBarConnectedDisplays.isEnabled) {
assertNotificationIcon(latest, notificationKey)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsWithNotifsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsWithNotifsViewModelTest.kt
index 7135cf01394a..826f94408715 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsWithNotifsViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsWithNotifsViewModelTest.kt
@@ -42,6 +42,7 @@ import com.android.systemui.mediaprojection.taskswitcher.FakeActivityTaskManager
import com.android.systemui.res.R
import com.android.systemui.screenrecord.data.model.ScreenRecordModel
import com.android.systemui.screenrecord.data.repository.screenRecordRepository
+import com.android.systemui.statusbar.chips.call.ui.viewmodel.CallChipViewModel
import com.android.systemui.statusbar.chips.call.ui.viewmodel.CallChipViewModelTest.Companion.createStatusBarIconViewOrNull
import com.android.systemui.statusbar.chips.mediaprojection.domain.interactor.MediaProjectionChipInteractorTest.Companion.NORMAL_PACKAGE
import com.android.systemui.statusbar.chips.mediaprojection.domain.interactor.MediaProjectionChipInteractorTest.Companion.setUpPackageManagerForMediaProjection
@@ -63,7 +64,7 @@ import com.android.systemui.statusbar.notification.data.repository.activeNotific
import com.android.systemui.statusbar.notification.data.repository.addNotif
import com.android.systemui.statusbar.notification.data.repository.addNotifs
import com.android.systemui.statusbar.notification.data.repository.removeNotif
-import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel
+import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentBuilder
import com.android.systemui.statusbar.notification.shared.ActiveNotificationModel
import com.android.systemui.statusbar.phone.SystemUIDialog
import com.android.systemui.statusbar.phone.ongoingcall.DisableChipsModernization
@@ -269,7 +270,10 @@ class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() {
addOngoingCallState(callNotificationKey)
assertThat(latest)
- .containsExactly(ScreenRecordChipViewModel.KEY, callNotificationKey)
+ .containsExactly(
+ ScreenRecordChipViewModel.KEY,
+ "${CallChipViewModel.KEY_PREFIX}$callNotificationKey",
+ )
.inOrder()
}
@@ -358,7 +362,7 @@ class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() {
addOngoingCallState(key = "call")
val promotedContentBuilder =
- PromotedNotificationContentModel.Builder("notif").apply {
+ PromotedNotificationContentBuilder("notif").applyToShared {
this.shortCriticalText = "Some text here"
}
activeNotificationListRepository.addNotif(
@@ -741,7 +745,7 @@ class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() {
activeNotificationModel(
key = "notif",
statusBarChipIcon = icon,
- promotedContent = PromotedNotificationContentModel.Builder("notif").build(),
+ promotedContent = PromotedNotificationContentBuilder("notif").build(),
)
)
)
@@ -765,7 +769,7 @@ class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() {
activeNotificationModel(
key = "notif",
statusBarChipIcon = icon,
- promotedContent = PromotedNotificationContentModel.Builder("notif").build(),
+ promotedContent = PromotedNotificationContentBuilder("notif").build(),
)
)
)
@@ -791,14 +795,12 @@ class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() {
activeNotificationModel(
key = "firstNotif",
statusBarChipIcon = firstIcon,
- promotedContent =
- PromotedNotificationContentModel.Builder("firstNotif").build(),
+ promotedContent = PromotedNotificationContentBuilder("firstNotif").build(),
),
activeNotificationModel(
key = "secondNotif",
statusBarChipIcon = secondIcon,
- promotedContent =
- PromotedNotificationContentModel.Builder("secondNotif").build(),
+ promotedContent = PromotedNotificationContentBuilder("secondNotif").build(),
),
)
)
@@ -822,14 +824,12 @@ class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() {
activeNotificationModel(
key = "firstNotif",
statusBarChipIcon = firstIcon,
- promotedContent =
- PromotedNotificationContentModel.Builder("firstNotif").build(),
+ promotedContent = PromotedNotificationContentBuilder("firstNotif").build(),
),
activeNotificationModel(
key = "secondNotif",
statusBarChipIcon = secondIcon,
- promotedContent =
- PromotedNotificationContentModel.Builder("secondNotif").build(),
+ promotedContent = PromotedNotificationContentBuilder("secondNotif").build(),
),
)
)
@@ -857,20 +857,17 @@ class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() {
activeNotificationModel(
key = "firstNotif",
statusBarChipIcon = firstIcon,
- promotedContent =
- PromotedNotificationContentModel.Builder("firstNotif").build(),
+ promotedContent = PromotedNotificationContentBuilder("firstNotif").build(),
),
activeNotificationModel(
key = "secondNotif",
statusBarChipIcon = secondIcon,
- promotedContent =
- PromotedNotificationContentModel.Builder("secondNotif").build(),
+ promotedContent = PromotedNotificationContentBuilder("secondNotif").build(),
),
activeNotificationModel(
key = "thirdNotif",
statusBarChipIcon = thirdIcon,
- promotedContent =
- PromotedNotificationContentModel.Builder("thirdNotif").build(),
+ promotedContent = PromotedNotificationContentBuilder("thirdNotif").build(),
),
)
)
@@ -896,26 +893,22 @@ class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() {
activeNotificationModel(
key = "firstNotif",
statusBarChipIcon = firstIcon,
- promotedContent =
- PromotedNotificationContentModel.Builder("firstNotif").build(),
+ promotedContent = PromotedNotificationContentBuilder("firstNotif").build(),
),
activeNotificationModel(
key = "secondNotif",
statusBarChipIcon = secondIcon,
- promotedContent =
- PromotedNotificationContentModel.Builder("secondNotif").build(),
+ promotedContent = PromotedNotificationContentBuilder("secondNotif").build(),
),
activeNotificationModel(
key = "thirdNotif",
statusBarChipIcon = thirdIcon,
- promotedContent =
- PromotedNotificationContentModel.Builder("thirdNotif").build(),
+ promotedContent = PromotedNotificationContentBuilder("thirdNotif").build(),
),
activeNotificationModel(
key = "fourthNotif",
statusBarChipIcon = fourthIcon,
- promotedContent =
- PromotedNotificationContentModel.Builder("fourthNotif").build(),
+ promotedContent = PromotedNotificationContentBuilder("fourthNotif").build(),
),
)
)
@@ -941,20 +934,17 @@ class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() {
activeNotificationModel(
key = "firstNotif",
statusBarChipIcon = createStatusBarIconViewOrNull(),
- promotedContent =
- PromotedNotificationContentModel.Builder("firstNotif").build(),
+ promotedContent = PromotedNotificationContentBuilder("firstNotif").build(),
),
activeNotificationModel(
key = "secondNotif",
statusBarChipIcon = createStatusBarIconViewOrNull(),
- promotedContent =
- PromotedNotificationContentModel.Builder("secondNotif").build(),
+ promotedContent = PromotedNotificationContentBuilder("secondNotif").build(),
),
activeNotificationModel(
key = "thirdNotif",
statusBarChipIcon = createStatusBarIconViewOrNull(),
- promotedContent =
- PromotedNotificationContentModel.Builder("thirdNotif").build(),
+ promotedContent = PromotedNotificationContentBuilder("thirdNotif").build(),
),
)
)
@@ -973,26 +963,22 @@ class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() {
activeNotificationModel(
key = "firstNotif",
statusBarChipIcon = createStatusBarIconViewOrNull(),
- promotedContent =
- PromotedNotificationContentModel.Builder("firstNotif").build(),
+ promotedContent = PromotedNotificationContentBuilder("firstNotif").build(),
),
activeNotificationModel(
key = "secondNotif",
statusBarChipIcon = createStatusBarIconViewOrNull(),
- promotedContent =
- PromotedNotificationContentModel.Builder("secondNotif").build(),
+ promotedContent = PromotedNotificationContentBuilder("secondNotif").build(),
),
activeNotificationModel(
key = "thirdNotif",
statusBarChipIcon = createStatusBarIconViewOrNull(),
- promotedContent =
- PromotedNotificationContentModel.Builder("thirdNotif").build(),
+ promotedContent = PromotedNotificationContentBuilder("thirdNotif").build(),
),
activeNotificationModel(
key = "fourthNotif",
statusBarChipIcon = createStatusBarIconViewOrNull(),
- promotedContent =
- PromotedNotificationContentModel.Builder("fourthNotif").build(),
+ promotedContent = PromotedNotificationContentBuilder("fourthNotif").build(),
),
)
)
@@ -1016,14 +1002,12 @@ class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() {
activeNotificationModel(
key = "firstNotif",
statusBarChipIcon = firstIcon,
- promotedContent =
- PromotedNotificationContentModel.Builder("firstNotif").build(),
+ promotedContent = PromotedNotificationContentBuilder("firstNotif").build(),
),
activeNotificationModel(
key = "secondNotif",
statusBarChipIcon = createStatusBarIconViewOrNull(),
- promotedContent =
- PromotedNotificationContentModel.Builder("secondNotif").build(),
+ promotedContent = PromotedNotificationContentBuilder("secondNotif").build(),
),
)
)
@@ -1050,20 +1034,17 @@ class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() {
activeNotificationModel(
key = "firstNotif",
statusBarChipIcon = firstIcon,
- promotedContent =
- PromotedNotificationContentModel.Builder("firstNotif").build(),
+ promotedContent = PromotedNotificationContentBuilder("firstNotif").build(),
),
activeNotificationModel(
key = "secondNotif",
statusBarChipIcon = secondIcon,
- promotedContent =
- PromotedNotificationContentModel.Builder("secondNotif").build(),
+ promotedContent = PromotedNotificationContentBuilder("secondNotif").build(),
),
activeNotificationModel(
key = "thirdNotif",
statusBarChipIcon = thirdIcon,
- promotedContent =
- PromotedNotificationContentModel.Builder("thirdNotif").build(),
+ promotedContent = PromotedNotificationContentBuilder("thirdNotif").build(),
),
)
)
@@ -1092,7 +1073,7 @@ class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() {
activeNotificationModel(
key = "notif",
statusBarChipIcon = createStatusBarIconViewOrNull(),
- promotedContent = PromotedNotificationContentModel.Builder("notif").build(),
+ promotedContent = PromotedNotificationContentBuilder("notif").build(),
)
)
@@ -1114,19 +1095,22 @@ class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() {
activeNotificationModel(
key = "notif1",
statusBarChipIcon = createStatusBarIconViewOrNull(),
- promotedContent = PromotedNotificationContentModel.Builder("notif1").build(),
+ promotedContent = PromotedNotificationContentBuilder("notif1").build(),
)
)
activeNotificationListRepository.addNotif(
activeNotificationModel(
key = "notif2",
statusBarChipIcon = createStatusBarIconViewOrNull(),
- promotedContent = PromotedNotificationContentModel.Builder("notif2").build(),
+ promotedContent = PromotedNotificationContentBuilder("notif2").build(),
)
)
assertThat(latest)
- .containsExactly(ScreenRecordChipViewModel.KEY, callNotificationKey)
+ .containsExactly(
+ ScreenRecordChipViewModel.KEY,
+ "${CallChipViewModel.KEY_PREFIX}$callNotificationKey",
+ )
.inOrder()
}
@@ -1143,22 +1127,31 @@ class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() {
activeNotificationModel(
key = "notif1",
statusBarChipIcon = createStatusBarIconViewOrNull(),
- promotedContent = PromotedNotificationContentModel.Builder("notif1").build(),
+ promotedContent = PromotedNotificationContentBuilder("notif1").build(),
)
)
activeNotificationListRepository.addNotif(
activeNotificationModel(
key = "notif2",
statusBarChipIcon = createStatusBarIconViewOrNull(),
- promotedContent = PromotedNotificationContentModel.Builder("notif2").build(),
+ promotedContent = PromotedNotificationContentBuilder("notif2").build(),
)
)
assertThat(latest)
- .containsExactly(ScreenRecordChipViewModel.KEY, callNotificationKey, "notif1")
+ .containsExactly(
+ ScreenRecordChipViewModel.KEY,
+ "${CallChipViewModel.KEY_PREFIX}$callNotificationKey",
+ "notif1",
+ )
.inOrder()
}
+ // The ranking between different chips should stay consistent between
+ // OngoingActivityChipsViewModel and PromotedNotificationsInteractor.
+ // Make sure to also change
+ // PromotedNotificationsInteractorTest#orderedChipNotificationKeys_rankingIsCorrect
+ // if you change this test.
@EnableChipsModernization
@Test
fun chips_screenRecordAndCallAndPromotedNotifs_secondNotifInOverflow() =
@@ -1173,7 +1166,7 @@ class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() {
activeNotificationModel(
key = "notif",
statusBarChipIcon = notifIcon,
- promotedContent = PromotedNotificationContentModel.Builder("notif").build(),
+ promotedContent = PromotedNotificationContentBuilder("notif").build(),
)
)
addOngoingCallState(key = callNotificationKey)
@@ -1184,7 +1177,7 @@ class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() {
activeNotificationModel(
key = "notif2",
statusBarChipIcon = notifIcon2,
- promotedContent = PromotedNotificationContentModel.Builder("notif2").build(),
+ promotedContent = PromotedNotificationContentBuilder("notif2").build(),
)
)
@@ -1209,7 +1202,7 @@ class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() {
activeNotificationModel(
key = "notif",
statusBarChipIcon = notifIcon,
- promotedContent = PromotedNotificationContentModel.Builder("notif").build(),
+ promotedContent = PromotedNotificationContentBuilder("notif").build(),
)
)
)
@@ -1260,7 +1253,7 @@ class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() {
activeNotificationModel(
key = "notif",
statusBarChipIcon = notifIcon,
- promotedContent = PromotedNotificationContentModel.Builder("notif").build(),
+ promotedContent = PromotedNotificationContentBuilder("notif").build(),
)
)
@@ -1299,7 +1292,7 @@ class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() {
activeNotificationModel(
key = "notif",
statusBarChipIcon = notifIcon,
- promotedContent = PromotedNotificationContentModel.Builder("notif").build(),
+ promotedContent = PromotedNotificationContentBuilder("notif").build(),
)
)
// And everything else hidden
@@ -1377,7 +1370,7 @@ class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() {
activeNotificationModel(
key = "notif1",
statusBarChipIcon = notif1Icon,
- promotedContent = PromotedNotificationContentModel.Builder("notif1").build(),
+ promotedContent = PromotedNotificationContentBuilder("notif1").build(),
)
)
)
@@ -1431,7 +1424,7 @@ class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() {
activeNotificationModel(
key = "notif2",
statusBarChipIcon = notif2Icon,
- promotedContent = PromotedNotificationContentModel.Builder("notif2").build(),
+ promotedContent = PromotedNotificationContentBuilder("notif2").build(),
)
)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/AssistantFeedbackControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/AssistantFeedbackControllerTest.java
index a58f7f72f08a..ae5cd0196e72 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/AssistantFeedbackControllerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/AssistantFeedbackControllerTest.java
@@ -41,6 +41,7 @@ import android.app.NotificationChannel;
import android.os.Handler;
import android.os.UserHandle;
import android.provider.DeviceConfig;
+import android.service.notification.NotificationListenerService;
import android.service.notification.StatusBarNotification;
import android.testing.TestableLooper;
@@ -101,52 +102,53 @@ public class AssistantFeedbackControllerTest extends SysuiTestCase {
switchFlag("false");
// test flag disables logic with default values
assertEquals(STATUS_UNCHANGED, mAssistantFeedbackController.getFeedbackStatus(
- getEntry(IMPORTANCE_DEFAULT, IMPORTANCE_DEFAULT, RANKING_UNCHANGED)));
+ getEntry(IMPORTANCE_DEFAULT, IMPORTANCE_DEFAULT, RANKING_UNCHANGED).getRanking()));
assertNull(mAssistantFeedbackController.getFeedbackIcon(
- getEntry(IMPORTANCE_DEFAULT, IMPORTANCE_DEFAULT, RANKING_UNCHANGED)));
+ getEntry(IMPORTANCE_DEFAULT, IMPORTANCE_DEFAULT, RANKING_UNCHANGED).getRanking()));
// test that the flag disables logic with values that otherwise would return a value
assertEquals(STATUS_UNCHANGED, mAssistantFeedbackController.getFeedbackStatus(
- getEntry(IMPORTANCE_DEFAULT, IMPORTANCE_HIGH, RANKING_PROMOTED)));
+ getEntry(IMPORTANCE_DEFAULT, IMPORTANCE_HIGH, RANKING_PROMOTED).getRanking()));
assertNull(mAssistantFeedbackController.getFeedbackIcon(
- getEntry(IMPORTANCE_DEFAULT, IMPORTANCE_HIGH, RANKING_PROMOTED)));
+ getEntry(IMPORTANCE_DEFAULT, IMPORTANCE_HIGH, RANKING_PROMOTED).getRanking()));
}
@Test
public void testFeedback_noChange() {
switchFlag("true");
assertEquals(STATUS_UNCHANGED, mAssistantFeedbackController.getFeedbackStatus(
- getEntry(IMPORTANCE_DEFAULT, IMPORTANCE_DEFAULT, RANKING_UNCHANGED)));
+ getEntry(IMPORTANCE_DEFAULT, IMPORTANCE_DEFAULT, RANKING_UNCHANGED).getRanking()));
assertNull(mAssistantFeedbackController.getFeedbackIcon(
- getEntry(IMPORTANCE_DEFAULT, IMPORTANCE_DEFAULT, RANKING_UNCHANGED)));
+ getEntry(IMPORTANCE_DEFAULT, IMPORTANCE_DEFAULT, RANKING_UNCHANGED).getRanking()));
}
@Test
public void testFeedback_changedImportance() {
switchFlag("true");
- NotificationEntry entry = getEntry(IMPORTANCE_DEFAULT, IMPORTANCE_HIGH, RANKING_UNCHANGED);
- assertEquals(STATUS_PROMOTED, mAssistantFeedbackController.getFeedbackStatus(entry));
- assertNotNull(mAssistantFeedbackController.getFeedbackIcon(entry));
-
- entry = getEntry(IMPORTANCE_DEFAULT, IMPORTANCE_LOW, RANKING_UNCHANGED);
- assertEquals(STATUS_SILENCED, mAssistantFeedbackController.getFeedbackStatus(entry));
- assertNotNull(mAssistantFeedbackController.getFeedbackIcon(entry));
-
- entry = getEntry(IMPORTANCE_LOW, IMPORTANCE_MIN, RANKING_UNCHANGED);
- assertEquals(STATUS_DEMOTED, mAssistantFeedbackController.getFeedbackStatus(entry));
- assertNotNull(mAssistantFeedbackController.getFeedbackIcon(entry));
+ NotificationListenerService.Ranking ranking =
+ getEntry(IMPORTANCE_DEFAULT, IMPORTANCE_HIGH, RANKING_UNCHANGED).getRanking();
+ assertEquals(STATUS_PROMOTED, mAssistantFeedbackController.getFeedbackStatus(ranking));
+ assertNotNull(mAssistantFeedbackController.getFeedbackIcon(ranking));
+
+ ranking = getEntry(IMPORTANCE_DEFAULT, IMPORTANCE_LOW, RANKING_UNCHANGED).getRanking();
+ assertEquals(STATUS_SILENCED, mAssistantFeedbackController.getFeedbackStatus(ranking));
+ assertNotNull(mAssistantFeedbackController.getFeedbackIcon(ranking));
+
+ ranking = getEntry(IMPORTANCE_LOW, IMPORTANCE_MIN, RANKING_UNCHANGED).getRanking();
+ assertEquals(STATUS_DEMOTED, mAssistantFeedbackController.getFeedbackStatus(ranking));
+ assertNotNull(mAssistantFeedbackController.getFeedbackIcon(ranking));
}
@Test
public void testFeedback_changedRanking() {
switchFlag("true");
- NotificationEntry entry =
- getEntry(IMPORTANCE_DEFAULT, IMPORTANCE_DEFAULT, RANKING_PROMOTED);
- assertEquals(STATUS_PROMOTED, mAssistantFeedbackController.getFeedbackStatus(entry));
- assertNotNull(mAssistantFeedbackController.getFeedbackIcon(entry));
-
- entry = getEntry(IMPORTANCE_DEFAULT, IMPORTANCE_DEFAULT, RANKING_DEMOTED);
- assertEquals(STATUS_DEMOTED, mAssistantFeedbackController.getFeedbackStatus(entry));
- assertNotNull(mAssistantFeedbackController.getFeedbackIcon(entry));
+ NotificationListenerService.Ranking ranking =
+ getEntry(IMPORTANCE_DEFAULT, IMPORTANCE_DEFAULT, RANKING_PROMOTED).getRanking();
+ assertEquals(STATUS_PROMOTED, mAssistantFeedbackController.getFeedbackStatus(ranking));
+ assertNotNull(mAssistantFeedbackController.getFeedbackIcon(ranking));
+
+ ranking = getEntry(IMPORTANCE_DEFAULT, IMPORTANCE_DEFAULT, RANKING_DEMOTED).getRanking();
+ assertEquals(STATUS_DEMOTED, mAssistantFeedbackController.getFeedbackStatus(ranking));
+ assertNotNull(mAssistantFeedbackController.getFeedbackIcon(ranking));
}
@Test
@@ -155,7 +157,7 @@ public class AssistantFeedbackControllerTest extends SysuiTestCase {
FeedbackIcon expected = new FeedbackIcon(com.android.internal.R.drawable.ic_feedback_uprank,
com.android.internal.R.string.notification_feedback_indicator_promoted);
assertEquals(expected, mAssistantFeedbackController.getFeedbackIcon(
- getEntry(IMPORTANCE_DEFAULT, IMPORTANCE_HIGH, RANKING_PROMOTED)));
+ getEntry(IMPORTANCE_DEFAULT, IMPORTANCE_HIGH, RANKING_PROMOTED).getRanking()));
}
private NotificationEntry getEntry(int oldImportance, int newImportance,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/BundleEntryAdapterTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/BundleEntryAdapterTest.kt
index a13b8647e170..52f68bf4d729 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/BundleEntryAdapterTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/BundleEntryAdapterTest.kt
@@ -24,7 +24,10 @@ import android.testing.TestableLooper.RunWithLooper
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
+import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier.Companion.TYPE_NON_PERSON
+import com.android.systemui.statusbar.notification.row.entryAdapterFactory
import com.android.systemui.statusbar.notification.shared.NotificationBundleUi
+import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Rule
@@ -35,13 +38,15 @@ import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
@RunWithLooper
class BundleEntryAdapterTest : SysuiTestCase() {
+ private val kosmos = testKosmos()
private lateinit var underTest: BundleEntryAdapter
@get:Rule val setFlagsRule = SetFlagsRule()
+ private val factory: EntryAdapterFactory = kosmos.entryAdapterFactory
@Before
fun setUp() {
- underTest = BundleEntryAdapter(BundleEntry("key"))
+ underTest = factory.create(BundleEntry("key")) as BundleEntryAdapter
}
@Test
@@ -126,7 +131,7 @@ class BundleEntryAdapterTest : SysuiTestCase() {
@Test
@EnableFlags(NotificationBundleUi.FLAG_NAME)
fun isBubble() {
- assertThat(underTest.isBubbleCapable).isFalse()
+ assertThat(underTest.isBubble).isFalse()
}
@Test
@@ -152,4 +157,10 @@ class BundleEntryAdapterTest : SysuiTestCase() {
fun canShowFullScreen() {
assertThat(underTest.isFullScreenCapable()).isFalse()
}
+
+ @Test
+ @EnableFlags(NotificationBundleUi.FLAG_NAME)
+ fun getPeopleNotificationType() {
+ assertThat(underTest.getPeopleNotificationType()).isEqualTo(TYPE_NON_PERSON)
+ }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryAdapterTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryAdapterTest.kt
index faafa073be4c..02c6a484bd43 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryAdapterTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/NotificationEntryAdapterTest.kt
@@ -29,7 +29,11 @@ import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.res.R
import com.android.systemui.statusbar.RankingBuilder
+import com.android.systemui.statusbar.notification.collection.coordinator.mockVisualStabilityCoordinator
+import com.android.systemui.statusbar.notification.collection.notifcollection.NotifLifetimeExtender
+import com.android.systemui.statusbar.notification.collection.provider.mockHighPriorityProvider
import com.android.systemui.statusbar.notification.mockNotificationActivityStarter
+import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier.Companion.TYPE_FULL_PERSON
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
import com.android.systemui.statusbar.notification.row.entryAdapterFactory
import com.android.systemui.statusbar.notification.row.mockNotificationActionClickManager
@@ -40,8 +44,12 @@ import com.google.common.truth.Truth.assertThat
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.anyLong
import org.mockito.Mockito
+import org.mockito.Mockito.mock
import org.mockito.Mockito.verify
+import org.mockito.kotlin.any
+import org.mockito.kotlin.eq
@SmallTest
@RunWith(AndroidJUnit4::class)
@@ -108,7 +116,7 @@ class NotificationEntryAdapterTest : SysuiTestCase() {
@Test
@EnableFlags(NotificationBundleUi.FLAG_NAME)
fun getRow_adapter() {
- val row = Mockito.mock(ExpandableNotificationRow::class.java)
+ val row = mock(ExpandableNotificationRow::class.java)
val notification: Notification =
Notification.Builder(mContext, "").setSmallIcon(R.drawable.ic_person).build()
@@ -126,7 +134,7 @@ class NotificationEntryAdapterTest : SysuiTestCase() {
@Test
@EnableFlags(NotificationBundleUi.FLAG_NAME)
fun isGroupRoot_adapter_groupSummary() {
- val row = Mockito.mock(ExpandableNotificationRow::class.java)
+ val row = mock(ExpandableNotificationRow::class.java)
val notification: Notification =
Notification.Builder(mContext, "")
.setSmallIcon(R.drawable.ic_person)
@@ -173,7 +181,7 @@ class NotificationEntryAdapterTest : SysuiTestCase() {
@Test
@EnableFlags(NotificationBundleUi.FLAG_NAME)
fun isClearable_adapter() {
- val row = Mockito.mock(ExpandableNotificationRow::class.java)
+ val row = mock(ExpandableNotificationRow::class.java)
val notification: Notification =
Notification.Builder(mContext, "").setSmallIcon(R.drawable.ic_person).build()
@@ -191,7 +199,7 @@ class NotificationEntryAdapterTest : SysuiTestCase() {
@Test
@EnableFlags(NotificationBundleUi.FLAG_NAME)
fun getSummarization_adapter() {
- val row = Mockito.mock(ExpandableNotificationRow::class.java)
+ val row = mock(ExpandableNotificationRow::class.java)
val notification: Notification =
Notification.Builder(mContext, "").setSmallIcon(R.drawable.ic_person).build()
@@ -211,7 +219,7 @@ class NotificationEntryAdapterTest : SysuiTestCase() {
@Test
@EnableFlags(NotificationBundleUi.FLAG_NAME)
fun getIcons_adapter() {
- val row = Mockito.mock(ExpandableNotificationRow::class.java)
+ val row = mock(ExpandableNotificationRow::class.java)
val notification: Notification =
Notification.Builder(mContext, "").setSmallIcon(R.drawable.ic_person).build()
@@ -255,8 +263,111 @@ class NotificationEntryAdapterTest : SysuiTestCase() {
@Test
@EnableFlags(NotificationBundleUi.FLAG_NAME)
+ fun getRanking() {
+ val notification: Notification =
+ Notification.Builder(mContext, "")
+ .setSmallIcon(R.drawable.ic_person)
+ .addAction(Mockito.mock(Notification.Action::class.java))
+ .build()
+
+ val entry = NotificationEntryBuilder().setNotification(notification).build()
+
+ underTest = factory.create(entry) as NotificationEntryAdapter
+ assertThat(underTest.ranking).isEqualTo(entry.ranking)
+ }
+
+ @Test
+ @EnableFlags(NotificationBundleUi.FLAG_NAME)
+ fun endLifetimeExtension() {
+ val notification: Notification =
+ Notification.Builder(mContext, "")
+ .setSmallIcon(R.drawable.ic_person)
+ .addAction(Mockito.mock(Notification.Action::class.java))
+ .build()
+
+ val entry = NotificationEntryBuilder().setNotification(notification).build()
+ val callback =
+ Mockito.mock(NotifLifetimeExtender.OnEndLifetimeExtensionCallback::class.java)
+
+ underTest = factory.create(entry) as NotificationEntryAdapter
+ underTest.endLifetimeExtension(callback, Mockito.mock(NotifLifetimeExtender::class.java))
+ verify(callback).onEndLifetimeExtension(any(), eq(entry))
+ }
+
+ @Test
+ @EnableFlags(NotificationBundleUi.FLAG_NAME)
+ fun onImportanceChanged() {
+ val notification: Notification =
+ Notification.Builder(mContext, "")
+ .setSmallIcon(R.drawable.ic_person)
+ .addAction(Mockito.mock(Notification.Action::class.java))
+ .build()
+
+ val entry = NotificationEntryBuilder().setNotification(notification).build()
+
+ underTest = factory.create(entry) as NotificationEntryAdapter
+
+ underTest.onImportanceChanged()
+ verify(kosmos.mockVisualStabilityCoordinator)
+ .temporarilyAllowSectionChanges(eq(entry), anyLong())
+ }
+
+ @Test
+ @EnableFlags(NotificationBundleUi.FLAG_NAME)
+ fun markForUserTriggeredMovement() {
+ val notification: Notification =
+ Notification.Builder(mContext, "")
+ .setSmallIcon(R.drawable.ic_person)
+ .addAction(Mockito.mock(Notification.Action::class.java))
+ .build()
+
+ val entry = NotificationEntryBuilder().setNotification(notification).build()
+ underTest = factory.create(entry) as NotificationEntryAdapter
+
+ assertThat(underTest.isMarkedForUserTriggeredMovement)
+ .isEqualTo(entry.isMarkedForUserTriggeredMovement)
+
+ underTest.markForUserTriggeredMovement()
+ assertThat(underTest.isMarkedForUserTriggeredMovement)
+ .isEqualTo(entry.isMarkedForUserTriggeredMovement)
+ }
+
+ @Test
+ @EnableFlags(NotificationBundleUi.FLAG_NAME)
+ fun isHighPriority() {
+ val notification: Notification =
+ Notification.Builder(mContext, "")
+ .setSmallIcon(R.drawable.ic_person)
+ .addAction(Mockito.mock(Notification.Action::class.java))
+ .build()
+
+ val entry = NotificationEntryBuilder().setNotification(notification).build()
+ underTest = factory.create(entry) as NotificationEntryAdapter
+
+ underTest.isHighPriority
+
+ verify(kosmos.mockHighPriorityProvider).isHighPriority(entry)
+ }
+
+ @Test
+ @EnableFlags(NotificationBundleUi.FLAG_NAME)
+ fun isBlockable() {
+ val notification: Notification =
+ Notification.Builder(mContext, "")
+ .setSmallIcon(R.drawable.ic_person)
+ .addAction(Mockito.mock(Notification.Action::class.java))
+ .build()
+
+ val entry = NotificationEntryBuilder().setNotification(notification).build()
+ underTest = factory.create(entry) as NotificationEntryAdapter
+
+ assertThat(underTest.isBlockable).isEqualTo(entry.isBlockable)
+ }
+
+ @Test
+ @EnableFlags(NotificationBundleUi.FLAG_NAME)
fun canDragAndDrop() {
- val pi = Mockito.mock(PendingIntent::class.java)
+ val pi = mock(PendingIntent::class.java)
Mockito.`when`(pi.isActivity).thenReturn(true)
val notification: Notification =
Notification.Builder(mContext, "")
@@ -282,7 +393,7 @@ class NotificationEntryAdapterTest : SysuiTestCase() {
val entry = NotificationEntryBuilder().setNotification(notification).build()
underTest = factory.create(entry) as NotificationEntryAdapter
- assertThat(underTest.isBubbleCapable).isEqualTo(entry.isBubble)
+ assertThat(underTest.isBubble).isEqualTo(entry.isBubble)
}
@Test
@@ -334,11 +445,21 @@ class NotificationEntryAdapterTest : SysuiTestCase() {
@Test
@EnableFlags(NotificationBundleUi.FLAG_NAME)
+ fun getPeopleNotificationType() {
+ val entry = kosmos.msgStyleBubbleableFullPerson
+
+ underTest = factory.create(entry) as NotificationEntryAdapter
+
+ assertThat(underTest.peopleNotificationType).isEqualTo(TYPE_FULL_PERSON)
+ }
+
+ @Test
+ @EnableFlags(NotificationBundleUi.FLAG_NAME)
fun canShowFullScreen() {
val notification: Notification =
Notification.Builder(mContext, "")
.setSmallIcon(R.drawable.ic_person)
- .setFullScreenIntent(Mockito.mock(PendingIntent::class.java), true)
+ .setFullScreenIntent(mock(PendingIntent::class.java), true)
.build()
val entry =
@@ -353,6 +474,22 @@ class NotificationEntryAdapterTest : SysuiTestCase() {
@Test
@EnableFlags(NotificationBundleUi.FLAG_NAME)
+ fun onDragSuccess() {
+ val notification: Notification =
+ Notification.Builder(mContext, "")
+ .setSmallIcon(R.drawable.ic_person)
+ .addAction(mock(Notification.Action::class.java))
+ .build()
+ val entry = NotificationEntryBuilder().setNotification(notification).build()
+
+ underTest = factory.create(entry) as NotificationEntryAdapter
+
+ underTest.onDragSuccess()
+ verify(kosmos.mockNotificationActivityStarter).onDragSuccess(entry)
+ }
+
+ @Test
+ @EnableFlags(NotificationBundleUi.FLAG_NAME)
fun onNotificationBubbleIconClicked() {
val notification: Notification =
Notification.Builder(mContext, "").setSmallIcon(R.drawable.ic_person).build()
@@ -371,7 +508,7 @@ class NotificationEntryAdapterTest : SysuiTestCase() {
val notification: Notification =
Notification.Builder(mContext, "")
.setSmallIcon(R.drawable.ic_person)
- .addAction(Mockito.mock(Notification.Action::class.java))
+ .addAction(mock(Notification.Action::class.java))
.build()
val entry = NotificationEntryBuilder().setNotification(notification).build()
@@ -380,4 +517,35 @@ class NotificationEntryAdapterTest : SysuiTestCase() {
underTest.onNotificationActionClicked()
verify(kosmos.mockNotificationActionClickManager).onNotificationActionClicked(entry)
}
+
+ @Test
+ @EnableFlags(NotificationBundleUi.FLAG_NAME)
+ fun getDismissState() {
+ val notification: Notification =
+ Notification.Builder(mContext, "").setSmallIcon(R.drawable.ic_person).build()
+
+ val entry = NotificationEntryBuilder().setNotification(notification).build()
+ entry.dismissState = NotificationEntry.DismissState.PARENT_DISMISSED
+
+ underTest = factory.create(entry) as NotificationEntryAdapter
+
+ assertThat(underTest.dismissState).isEqualTo(entry.dismissState)
+ }
+
+ @Test
+ @EnableFlags(NotificationBundleUi.FLAG_NAME)
+ fun onEntryClicked() {
+ val notification: Notification =
+ Notification.Builder(mContext, "")
+ .setSmallIcon(R.drawable.ic_person)
+ .addAction(mock(Notification.Action::class.java))
+ .build()
+ val entry = NotificationEntryBuilder().setNotification(notification).build()
+ val row = mock(ExpandableNotificationRow::class.java)
+
+ underTest = factory.create(entry) as NotificationEntryAdapter
+
+ underTest.onEntryClicked(row)
+ verify(kosmos.mockNotificationActivityStarter).onNotificationClicked(entry, row)
+ }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/ColorizedFgsCoordinatorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/ColorizedFgsCoordinatorTest.kt
index 7fa157fa7cb3..3d7ced747450 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/ColorizedFgsCoordinatorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/ColorizedFgsCoordinatorTest.kt
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.notification.collection.coordinator
import android.app.Notification
+import android.app.Notification.FLAG_FOREGROUND_SERVICE
import android.app.NotificationManager
import android.app.PendingIntent
import android.app.Person
@@ -27,18 +28,21 @@ import android.testing.TestableLooper.RunWithLooper
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
-import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.kosmos.applicationCoroutineScope
import com.android.systemui.kosmos.collectLastValue
import com.android.systemui.kosmos.runTest
import com.android.systemui.kosmos.useUnconfinedTestDispatcher
+import com.android.systemui.mediaprojection.data.model.MediaProjectionState
+import com.android.systemui.mediaprojection.data.repository.fakeMediaProjectionRepository
+import com.android.systemui.screenrecord.data.model.ScreenRecordModel
+import com.android.systemui.screenrecord.data.repository.screenRecordRepository
import com.android.systemui.statusbar.chips.notification.domain.interactor.statusBarNotificationChipsInteractor
import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips
import com.android.systemui.statusbar.core.StatusBarRootModernization
-import com.android.systemui.statusbar.notification.buildNotificationEntry
-import com.android.systemui.statusbar.notification.buildOngoingCallEntry
-import com.android.systemui.statusbar.notification.buildPromotedOngoingEntry
import com.android.systemui.statusbar.notification.collection.buildEntry
+import com.android.systemui.statusbar.notification.collection.buildNotificationEntry
+import com.android.systemui.statusbar.notification.collection.buildOngoingCallEntry
+import com.android.systemui.statusbar.notification.collection.buildPromotedOngoingEntry
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifPromoter
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner
import com.android.systemui.statusbar.notification.collection.notifPipeline
@@ -49,7 +53,6 @@ import com.android.systemui.statusbar.phone.ongoingcall.StatusBarChipsModernizat
import com.android.systemui.testKosmos
import com.android.systemui.util.mockito.withArgCaptor
import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.test.runTest
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
import org.junit.Before
@@ -171,6 +174,87 @@ class ColorizedFgsCoordinatorTest : SysuiTestCase() {
@Test
@EnableFlags(PromotedNotificationUi.FLAG_NAME)
+ fun testIncludeScreenRecordNotifInSection_importanceDefault() =
+ kosmos.runTest {
+ // GIVEN a screen record event + screen record notif that has a status bar chip
+ screenRecordRepository.screenRecordState.value = ScreenRecordModel.Recording
+ fakeMediaProjectionRepository.mediaProjectionState.value =
+ MediaProjectionState.Projecting.EntireScreen(hostPackage = "test_pkg")
+ val screenRecordEntry =
+ buildNotificationEntry(tag = "screenRecord", promoted = false) {
+ setImportance(NotificationManager.IMPORTANCE_DEFAULT)
+ setFlag(context, FLAG_FOREGROUND_SERVICE, true)
+ }
+
+ renderNotificationListInteractor.setRenderedList(listOf(screenRecordEntry))
+
+ val orderedChipNotificationKeys by
+ collectLastValue(promotedNotificationsInteractor.orderedChipNotificationKeys)
+
+ assertThat(orderedChipNotificationKeys)
+ .containsExactly("0|test_pkg|0|screenRecord|0")
+ .inOrder()
+
+ // THEN the entry is in the fgs section
+ assertTrue(sectioner.isInSection(screenRecordEntry))
+ }
+
+ @Test
+ @EnableFlags(PromotedNotificationUi.FLAG_NAME)
+ fun testDiscludeScreenRecordNotifInSection_importanceMin() =
+ kosmos.runTest {
+ // GIVEN a screen record event + screen record notif that has a status bar chip
+ screenRecordRepository.screenRecordState.value = ScreenRecordModel.Recording
+ fakeMediaProjectionRepository.mediaProjectionState.value =
+ MediaProjectionState.Projecting.EntireScreen(hostPackage = "test_pkg")
+ val screenRecordEntry =
+ buildNotificationEntry(tag = "screenRecord", promoted = false) {
+ setImportance(NotificationManager.IMPORTANCE_MIN)
+ setFlag(context, FLAG_FOREGROUND_SERVICE, true)
+ }
+
+ renderNotificationListInteractor.setRenderedList(listOf(screenRecordEntry))
+
+ val orderedChipNotificationKeys by
+ collectLastValue(promotedNotificationsInteractor.orderedChipNotificationKeys)
+
+ assertThat(orderedChipNotificationKeys)
+ .containsExactly("0|test_pkg|0|screenRecord|0")
+ .inOrder()
+
+ // THEN the entry is NOT in the fgs section
+ assertFalse(sectioner.isInSection(screenRecordEntry))
+ }
+
+ @Test
+ @DisableFlags(PromotedNotificationUi.FLAG_NAME)
+ fun testDiscludeScreenRecordNotifInSection_flagDisabled() =
+ kosmos.runTest {
+ // GIVEN a screen record event + screen record notif that has a status bar chip
+ screenRecordRepository.screenRecordState.value = ScreenRecordModel.Recording
+ fakeMediaProjectionRepository.mediaProjectionState.value =
+ MediaProjectionState.Projecting.EntireScreen(hostPackage = "test_pkg")
+ val screenRecordEntry =
+ buildNotificationEntry(tag = "screenRecord", promoted = false) {
+ setImportance(NotificationManager.IMPORTANCE_DEFAULT)
+ setFlag(context, FLAG_FOREGROUND_SERVICE, true)
+ }
+
+ renderNotificationListInteractor.setRenderedList(listOf(screenRecordEntry))
+
+ val orderedChipNotificationKeys by
+ collectLastValue(promotedNotificationsInteractor.orderedChipNotificationKeys)
+
+ assertThat(orderedChipNotificationKeys)
+ .containsExactly("0|test_pkg|0|screenRecord|0")
+ .inOrder()
+
+ // THEN the entry is NOT in the fgs section
+ assertFalse(sectioner.isInSection(screenRecordEntry))
+ }
+
+ @Test
+ @EnableFlags(PromotedNotificationUi.FLAG_NAME)
fun promoterSelectsPromotedOngoing_flagEnabled() {
val promoter: NotifPromoter = withArgCaptor { verify(notifPipeline).addPromoter(capture()) }
@@ -236,8 +320,4 @@ class ColorizedFgsCoordinatorTest : SysuiTestCase() {
val person = Person.Builder().setName("person").build()
return Notification.CallStyle.forOngoingCall(person, pendingIntent)
}
-
- companion object {
- private const val NOTIF_USER_ID = 0
- }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/GutsCoordinatorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/GutsCoordinatorTest.kt
index 7fe97d27f273..928b0014dc52 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/GutsCoordinatorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/GutsCoordinatorTest.kt
@@ -15,12 +15,15 @@
*/
package com.android.systemui.statusbar.notification.collection.coordinator
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
import android.testing.TestableLooper.RunWithLooper
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.dump.DumpManager
import com.android.systemui.log.logcatLogBuffer
+import com.android.systemui.statusbar.notification.collection.EntryAdapter
import com.android.systemui.statusbar.notification.collection.NotifPipeline
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder
@@ -30,6 +33,9 @@ import com.android.systemui.statusbar.notification.collection.render.NotifGutsVi
import com.android.systemui.statusbar.notification.collection.render.NotifGutsViewManager
import com.android.systemui.statusbar.notification.row.NotificationGuts
import com.android.systemui.statusbar.notification.row.NotificationGuts.GutsContent
+import com.android.systemui.statusbar.notification.row.entryAdapterFactory
+import com.android.systemui.statusbar.notification.shared.NotificationBundleUi
+import com.android.systemui.testKosmos
import com.android.systemui.util.mockito.withArgCaptor
import com.google.common.truth.Truth.assertThat
import org.junit.Before
@@ -45,12 +51,16 @@ import org.mockito.MockitoAnnotations.initMocks
@RunWith(AndroidJUnit4::class)
@RunWithLooper
class GutsCoordinatorTest : SysuiTestCase() {
+
+ private val kosmos = testKosmos()
private lateinit var coordinator: GutsCoordinator
private lateinit var notifLifetimeExtender: NotifLifetimeExtender
private lateinit var notifGutsViewListener: NotifGutsViewListener
private lateinit var entry1: NotificationEntry
private lateinit var entry2: NotificationEntry
+ private lateinit var entryAdapter1: EntryAdapter
+ private lateinit var entryAdapter2: EntryAdapter
@Mock private lateinit var notifGutsViewManager: NotifGutsViewManager
@Mock private lateinit var pipeline: NotifPipeline
@@ -73,12 +83,60 @@ class GutsCoordinatorTest : SysuiTestCase() {
notifLifetimeExtender.setCallback(lifetimeExtenderCallback)
entry1 = NotificationEntryBuilder().setId(1).build()
entry2 = NotificationEntryBuilder().setId(2).build()
+ entryAdapter1 = kosmos.entryAdapterFactory.create(entry1)
+ entryAdapter2 = kosmos.entryAdapterFactory.create(entry2)
whenever(notificationGuts.gutsContent).thenReturn(mock(GutsContent::class.java))
}
@Test
+ @EnableFlags(NotificationBundleUi.FLAG_NAME)
fun testSimpleLifetimeExtension() {
assertThat(notifLifetimeExtender.maybeExtendLifetime(entry1, 0)).isFalse()
+ notifGutsViewListener.onGutsOpen(entryAdapter1, notificationGuts)
+ assertThat(notifLifetimeExtender.maybeExtendLifetime(entry1, 0)).isTrue()
+ notifGutsViewListener.onGutsClose(entryAdapter1)
+ verify(lifetimeExtenderCallback).onEndLifetimeExtension(notifLifetimeExtender, entry1)
+ assertThat(notifLifetimeExtender.maybeExtendLifetime(entry1, 0)).isFalse()
+ }
+
+ @Test
+ @EnableFlags(NotificationBundleUi.FLAG_NAME)
+ fun testDoubleOpenLifetimeExtension() {
+ assertThat(notifLifetimeExtender.maybeExtendLifetime(entry1, 0)).isFalse()
+ notifGutsViewListener.onGutsOpen(entryAdapter1, notificationGuts)
+ assertThat(notifLifetimeExtender.maybeExtendLifetime(entry1, 0)).isTrue()
+ notifGutsViewListener.onGutsOpen(entryAdapter1, notificationGuts)
+ assertThat(notifLifetimeExtender.maybeExtendLifetime(entry1, 0)).isTrue()
+ notifGutsViewListener.onGutsClose(entryAdapter1)
+ verify(lifetimeExtenderCallback).onEndLifetimeExtension(notifLifetimeExtender, entry1)
+ assertThat(notifLifetimeExtender.maybeExtendLifetime(entry1, 0)).isFalse()
+ }
+
+ @Test
+ @EnableFlags(NotificationBundleUi.FLAG_NAME)
+ fun testTwoEntryLifetimeExtension() {
+ assertThat(notifLifetimeExtender.maybeExtendLifetime(entry1, 0)).isFalse()
+ assertThat(notifLifetimeExtender.maybeExtendLifetime(entry2, 0)).isFalse()
+ notifGutsViewListener.onGutsOpen(entryAdapter1, notificationGuts)
+ assertThat(notifLifetimeExtender.maybeExtendLifetime(entry1, 0)).isTrue()
+ assertThat(notifLifetimeExtender.maybeExtendLifetime(entry2, 0)).isFalse()
+ notifGutsViewListener.onGutsOpen(entryAdapter2, notificationGuts)
+ assertThat(notifLifetimeExtender.maybeExtendLifetime(entry1, 0)).isTrue()
+ assertThat(notifLifetimeExtender.maybeExtendLifetime(entry2, 0)).isTrue()
+ notifGutsViewListener.onGutsClose(entryAdapter1)
+ verify(lifetimeExtenderCallback).onEndLifetimeExtension(notifLifetimeExtender, entry1)
+ assertThat(notifLifetimeExtender.maybeExtendLifetime(entry1, 0)).isFalse()
+ assertThat(notifLifetimeExtender.maybeExtendLifetime(entry2, 0)).isTrue()
+ notifGutsViewListener.onGutsClose(entryAdapter2)
+ verify(lifetimeExtenderCallback).onEndLifetimeExtension(notifLifetimeExtender, entry2)
+ assertThat(notifLifetimeExtender.maybeExtendLifetime(entry1, 0)).isFalse()
+ assertThat(notifLifetimeExtender.maybeExtendLifetime(entry2, 0)).isFalse()
+ }
+
+ @Test
+ @DisableFlags(NotificationBundleUi.FLAG_NAME)
+ fun testSimpleLifetimeExtension_entry() {
+ assertThat(notifLifetimeExtender.maybeExtendLifetime(entry1, 0)).isFalse()
notifGutsViewListener.onGutsOpen(entry1, notificationGuts)
assertThat(notifLifetimeExtender.maybeExtendLifetime(entry1, 0)).isTrue()
notifGutsViewListener.onGutsClose(entry1)
@@ -87,7 +145,8 @@ class GutsCoordinatorTest : SysuiTestCase() {
}
@Test
- fun testDoubleOpenLifetimeExtension() {
+ @DisableFlags(NotificationBundleUi.FLAG_NAME)
+ fun testDoubleOpenLifetimeExtension_entry() {
assertThat(notifLifetimeExtender.maybeExtendLifetime(entry1, 0)).isFalse()
notifGutsViewListener.onGutsOpen(entry1, notificationGuts)
assertThat(notifLifetimeExtender.maybeExtendLifetime(entry1, 0)).isTrue()
@@ -99,7 +158,8 @@ class GutsCoordinatorTest : SysuiTestCase() {
}
@Test
- fun testTwoEntryLifetimeExtension() {
+ @DisableFlags(NotificationBundleUi.FLAG_NAME)
+ fun testTwoEntryLifetimeExtension_entry() {
assertThat(notifLifetimeExtender.maybeExtendLifetime(entry1, 0)).isFalse()
assertThat(notifLifetimeExtender.maybeExtendLifetime(entry2, 0)).isFalse()
notifGutsViewListener.onGutsOpen(entry1, notificationGuts)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.kt
index 44d88c31c5f1..b3d678b1fda6 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.kt
@@ -28,6 +28,7 @@ import com.android.systemui.log.logcatLogBuffer
import com.android.systemui.statusbar.NotificationRemoteInputManager
import com.android.systemui.statusbar.chips.notification.domain.interactor.statusBarNotificationChipsInteractor
import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips
+import com.android.systemui.statusbar.chips.uievents.statusBarChipsUiEventLogger
import com.android.systemui.statusbar.notification.NotifPipelineFlags
import com.android.systemui.statusbar.notification.collection.GroupEntryBuilder
import com.android.systemui.statusbar.notification.collection.NotifPipeline
@@ -144,6 +145,7 @@ class HeadsUpCoordinatorTest : SysuiTestCase() {
launchFullScreenIntentProvider,
flags,
statusBarNotificationChipsInteractor,
+ kosmos.statusBarChipsUiEventLogger,
headerController,
executor,
)
@@ -164,15 +166,15 @@ class HeadsUpCoordinatorTest : SysuiTestCase() {
verify(notifPipeline).addOnBeforeFinalizeFilterListener(capture())
}
onHeadsUpChangedListener = withArgCaptor { verify(headsUpManager).addListener(capture()) }
- actionPressListener = if (NotificationBundleUi.isEnabled) {
- withArgCaptor {
- verify(kosmos.mockNotificationActionClickManager).addActionClickListener(capture())
+ actionPressListener =
+ if (NotificationBundleUi.isEnabled) {
+ withArgCaptor {
+ verify(kosmos.mockNotificationActionClickManager)
+ .addActionClickListener(capture())
+ }
+ } else {
+ withArgCaptor { verify(remoteInputManager).addActionPressListener(capture()) }
}
- } else {
- withArgCaptor {
- verify(remoteInputManager).addActionPressListener(capture())
- }
- }
given(headsUpManager.allEntries).willAnswer { huns.stream() }
given(headsUpManager.isHeadsUpEntry(anyString())).willAnswer { invocation ->
val key = invocation.getArgument<String>(0)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/domain/interactor/ActiveNotificationsInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/domain/interactor/ActiveNotificationsInteractorTest.kt
index d3befa921e9e..29bb29f25797 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/domain/interactor/ActiveNotificationsInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/domain/interactor/ActiveNotificationsInteractorTest.kt
@@ -29,7 +29,7 @@ import com.android.systemui.statusbar.notification.data.model.activeNotification
import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationsStore
import com.android.systemui.statusbar.notification.data.repository.activeNotificationListRepository
import com.android.systemui.statusbar.notification.data.repository.setActiveNotifs
-import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel
+import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentBuilder
import com.android.systemui.statusbar.notification.shared.CallType
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
@@ -170,7 +170,7 @@ class ActiveNotificationsInteractorTest : SysuiTestCase() {
val promoted1 =
activeNotificationModel(
key = "notif1",
- promotedContent = PromotedNotificationContentModel.Builder("notif1").build(),
+ promotedContent = PromotedNotificationContentBuilder("notif1").build(),
)
val notPromoted2 = activeNotificationModel(key = "notif2", promotedContent = null)
@@ -208,14 +208,14 @@ class ActiveNotificationsInteractorTest : SysuiTestCase() {
val promoted1 =
activeNotificationModel(
key = "notif1",
- promotedContent = PromotedNotificationContentModel.Builder("notif1").build(),
+ promotedContent = PromotedNotificationContentBuilder("notif1").build(),
)
val notPromoted2 = activeNotificationModel(key = "notif2", promotedContent = null)
val notPromoted3 = activeNotificationModel(key = "notif3", promotedContent = null)
val promoted4 =
activeNotificationModel(
key = "notif4",
- promotedContent = PromotedNotificationContentModel.Builder("notif4").build(),
+ promotedContent = PromotedNotificationContentBuilder("notif4").build(),
)
activeNotificationListRepository.activeNotifications.value =
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/domain/interactor/RenderNotificationsListInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/domain/interactor/RenderNotificationsListInteractorTest.kt
index 35b19c19d5ce..873357c2201e 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/domain/interactor/RenderNotificationsListInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/domain/interactor/RenderNotificationsListInteractorTest.kt
@@ -21,20 +21,21 @@ import android.service.notification.StatusBarNotification
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
-import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.kosmos.testScope
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.collectLastValue
+import com.android.systemui.kosmos.runTest
import com.android.systemui.statusbar.RankingBuilder
import com.android.systemui.statusbar.notification.collection.GroupEntry
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.data.repository.activeNotificationListRepository
import com.android.systemui.statusbar.notification.promoted.PromotedNotificationUi
-import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel
+import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentBuilder
+import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModels
import com.android.systemui.statusbar.notification.shared.byKey
import com.android.systemui.testKosmos
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.test.runTest
import org.junit.Test
import org.junit.runner.RunWith
@@ -42,17 +43,14 @@ import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class RenderNotificationsListInteractorTest : SysuiTestCase() {
private val kosmos = testKosmos()
- private val testScope = kosmos.testScope
- private val notifsRepository = kosmos.activeNotificationListRepository
- private val notifsInteractor = kosmos.activeNotificationsInteractor
- private val underTest =
- RenderNotificationListInteractor(notifsRepository, sectionStyleProvider = mock(), context)
+ private val Kosmos.outputInteractor by Kosmos.Fixture { activeNotificationsInteractor }
+ private val Kosmos.underTest by Kosmos.Fixture { renderNotificationListInteractor }
@Test
fun setRenderedList_preservesOrdering() =
- testScope.runTest {
- val notifs by collectLastValue(notifsInteractor.topLevelRepresentativeNotifications)
+ kosmos.runTest {
+ val notifs by collectLastValue(outputInteractor.topLevelRepresentativeNotifications)
val keys = (1..50).shuffled().map { "$it" }
val entries = keys.map { mockNotificationEntry(key = it) }
underTest.setRenderedList(entries)
@@ -64,8 +62,8 @@ class RenderNotificationsListInteractorTest : SysuiTestCase() {
@Test
fun setRenderList_flatMapsRankings() =
- testScope.runTest {
- val ranks by collectLastValue(notifsInteractor.activeNotificationRanks)
+ kosmos.runTest {
+ val ranks by collectLastValue(outputInteractor.activeNotificationRanks)
val single = mockNotificationEntry("single", 0)
val group =
@@ -89,8 +87,8 @@ class RenderNotificationsListInteractorTest : SysuiTestCase() {
@Test
fun setRenderList_singleItems_mapsRankings() =
- testScope.runTest {
- val actual by collectLastValue(notifsInteractor.activeNotificationRanks)
+ kosmos.runTest {
+ val actual by collectLastValue(outputInteractor.activeNotificationRanks)
val expected =
(0..10).shuffled().mapIndexed { index, value -> "$value" to index }.toMap()
@@ -103,8 +101,8 @@ class RenderNotificationsListInteractorTest : SysuiTestCase() {
@Test
fun setRenderList_groupWithNoSummary_flatMapsRankings() =
- testScope.runTest {
- val actual by collectLastValue(notifsInteractor.activeNotificationRanks)
+ kosmos.runTest {
+ val actual by collectLastValue(outputInteractor.activeNotificationRanks)
val expected =
(0..10).shuffled().mapIndexed { index, value -> "$value" to index }.toMap()
@@ -123,14 +121,14 @@ class RenderNotificationsListInteractorTest : SysuiTestCase() {
@Test
@EnableFlags(PromotedNotificationUi.FLAG_NAME)
fun setRenderList_setsPromotionContent() =
- testScope.runTest {
- val actual by collectLastValue(notifsInteractor.topLevelRepresentativeNotifications)
+ kosmos.runTest {
+ val actual by collectLastValue(outputInteractor.topLevelRepresentativeNotifications)
val notPromoted1 = mockNotificationEntry("key1", promotedContent = null)
val promoted2 =
mockNotificationEntry(
"key2",
- promotedContent = PromotedNotificationContentModel.Builder("key2").build(),
+ promotedContent = PromotedNotificationContentBuilder("key2").build(),
)
underTest.setRenderedList(listOf(notPromoted1, promoted2))
@@ -149,7 +147,7 @@ class RenderNotificationsListInteractorTest : SysuiTestCase() {
private fun mockNotificationEntry(
key: String,
rank: Int = 0,
- promotedContent: PromotedNotificationContentModel? = null,
+ promotedContent: PromotedNotificationContentModels? = null,
): NotificationEntry {
val nBuilder = Notification.Builder(context, "a")
val notification = nBuilder.build()
@@ -165,7 +163,7 @@ class RenderNotificationsListInteractorTest : SysuiTestCase() {
whenever(this.representativeEntry).thenReturn(this)
whenever(this.ranking).thenReturn(RankingBuilder().setRank(rank).build())
whenever(this.sbn).thenReturn(mockSbn)
- whenever(this.promotedNotificationContentModel).thenReturn(promotedContent)
+ whenever(this.promotedNotificationContentModels).thenReturn(promotedContent)
}
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/icon/domain/interactor/NotificationIconsInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/icon/domain/interactor/NotificationIconsInteractorTest.kt
index ee698ae20adb..41120a16e4ea 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/icon/domain/interactor/NotificationIconsInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/icon/domain/interactor/NotificationIconsInteractorTest.kt
@@ -33,8 +33,10 @@ import com.android.systemui.statusbar.notification.data.repository.notifications
import com.android.systemui.statusbar.notification.domain.interactor.activeNotificationsInteractor
import com.android.systemui.statusbar.notification.domain.interactor.headsUpNotificationIconInteractor
import com.android.systemui.statusbar.notification.promoted.domain.interactor.aodPromotedNotificationInteractor
+import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentBuilder
import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel
import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel.Style.Base
+import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModels
import com.android.systemui.statusbar.notification.shared.byIsAmbient
import com.android.systemui.statusbar.notification.shared.byIsLastMessageFromReply
import com.android.systemui.statusbar.notification.shared.byIsPromoted
@@ -354,6 +356,6 @@ private val testIcons =
private fun promotedContent(
key: String,
style: PromotedNotificationContentModel.Style,
-): PromotedNotificationContentModel {
- return PromotedNotificationContentModel.Builder(key).apply { this.style = style }.build()
+): PromotedNotificationContentModels {
+ return PromotedNotificationContentBuilder(key).applyToShared { this.style = style }.build()
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractorImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractorImplTest.kt
index 893c17998a17..cc016b9768b7 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractorImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractorImplTest.kt
@@ -33,18 +33,21 @@ import android.platform.test.annotations.EnableFlags
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
+import com.android.systemui.statusbar.NotificationLockscreenUserManager.REDACTION_TYPE_NONE
+import com.android.systemui.statusbar.NotificationLockscreenUserManager.REDACTION_TYPE_PUBLIC
import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder
import com.android.systemui.statusbar.notification.promoted.AutomaticPromotionCoordinator.Companion.EXTRA_WAS_AUTOMATICALLY_PROMOTED
-import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel
import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel.Style
import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel.When
+import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModels
import com.android.systemui.statusbar.notification.row.RowImageInflater
import com.android.systemui.testKosmos
import com.android.systemui.util.time.fakeSystemClock
import com.android.systemui.util.time.systemClock
import com.google.common.truth.Truth.assertThat
+import kotlin.test.assertNotNull
import kotlin.time.Duration
import kotlin.time.Duration.Companion.minutes
import org.junit.Test
@@ -112,12 +115,43 @@ class PromotedNotificationContentExtractorImplTest : SysuiTestCase() {
setContentText(TEST_CONTENT_TEXT)
}
- val content = extractContent(entry)
+ val content = requireContent(entry)
- assertThat(content).isNotNull()
- assertThat(content?.subText).isEqualTo(TEST_SUB_TEXT)
- assertThat(content?.title).isEqualTo(TEST_CONTENT_TITLE)
- assertThat(content?.text).isEqualTo(TEST_CONTENT_TEXT)
+ content.privateVersion.apply {
+ assertThat(subText).isEqualTo(TEST_SUB_TEXT)
+ assertThat(title).isEqualTo(TEST_CONTENT_TITLE)
+ assertThat(text).isEqualTo(TEST_CONTENT_TEXT)
+ }
+
+ content.publicVersion.apply {
+ assertThat(subText).isNull()
+ assertThat(title).isNull()
+ assertThat(text).isNull()
+ }
+ }
+
+ @Test
+ @EnableFlags(PromotedNotificationUi.FLAG_NAME, StatusBarNotifChips.FLAG_NAME)
+ fun extractsContent_commonFields_noRedaction() {
+ val entry = createEntry {
+ setSubText(TEST_SUB_TEXT)
+ setContentTitle(TEST_CONTENT_TITLE)
+ setContentText(TEST_CONTENT_TEXT)
+ }
+
+ val content = requireContent(entry, redactionType = REDACTION_TYPE_NONE)
+
+ content.privateVersion.apply {
+ assertThat(subText).isEqualTo(TEST_SUB_TEXT)
+ assertThat(title).isEqualTo(TEST_CONTENT_TITLE)
+ assertThat(text).isEqualTo(TEST_CONTENT_TEXT)
+ }
+
+ content.publicVersion.apply {
+ assertThat(subText).isEqualTo(TEST_SUB_TEXT)
+ assertThat(title).isEqualTo(TEST_CONTENT_TITLE)
+ assertThat(text).isEqualTo(TEST_CONTENT_TEXT)
+ }
}
@Test
@@ -125,9 +159,9 @@ class PromotedNotificationContentExtractorImplTest : SysuiTestCase() {
fun extractContent_wasPromotedAutomatically_false() {
val entry = createEntry { extras.putBoolean(EXTRA_WAS_AUTOMATICALLY_PROMOTED, false) }
- val content = extractContent(entry)
+ val content = requireContent(entry).privateVersion
- assertThat(content!!.wasPromotedAutomatically).isFalse()
+ assertThat(content.wasPromotedAutomatically).isFalse()
}
@Test
@@ -135,9 +169,9 @@ class PromotedNotificationContentExtractorImplTest : SysuiTestCase() {
fun extractContent_wasPromotedAutomatically_true() {
val entry = createEntry { extras.putBoolean(EXTRA_WAS_AUTOMATICALLY_PROMOTED, true) }
- val content = extractContent(entry)
+ val content = requireContent(entry).privateVersion
- assertThat(content!!.wasPromotedAutomatically).isTrue()
+ assertThat(content.wasPromotedAutomatically).isTrue()
}
@Test
@@ -146,10 +180,9 @@ class PromotedNotificationContentExtractorImplTest : SysuiTestCase() {
fun extractContent_apiFlagOff_shortCriticalTextNotExtracted() {
val entry = createEntry { setShortCriticalText(TEST_SHORT_CRITICAL_TEXT) }
- val content = extractContent(entry)
+ val content = requireContent(entry).privateVersion
- assertThat(content).isNotNull()
- assertThat(content?.text).isNull()
+ assertThat(content.text).isNull()
}
@Test
@@ -161,10 +194,9 @@ class PromotedNotificationContentExtractorImplTest : SysuiTestCase() {
fun extractContent_apiFlagOn_shortCriticalTextExtracted() {
val entry = createEntry { setShortCriticalText(TEST_SHORT_CRITICAL_TEXT) }
- val content = extractContent(entry)
+ val content = requireContent(entry).privateVersion
- assertThat(content).isNotNull()
- assertThat(content?.shortCriticalText).isEqualTo(TEST_SHORT_CRITICAL_TEXT)
+ assertThat(content.shortCriticalText).isEqualTo(TEST_SHORT_CRITICAL_TEXT)
}
@Test
@@ -176,10 +208,9 @@ class PromotedNotificationContentExtractorImplTest : SysuiTestCase() {
fun extractContent_noShortCriticalTextSet_textIsNull() {
val entry = createEntry { setShortCriticalText(null) }
- val content = extractContent(entry)
+ val content = requireContent(entry).privateVersion
- assertThat(content).isNotNull()
- assertThat(content?.shortCriticalText).isNull()
+ assertThat(content.shortCriticalText).isNull()
}
@Test
@@ -190,11 +221,22 @@ class PromotedNotificationContentExtractorImplTest : SysuiTestCase() {
@Test
@EnableFlags(PromotedNotificationUi.FLAG_NAME, StatusBarNotifChips.FLAG_NAME)
+ fun extractTime_basicTimeZero() {
+ assertExtractedTime(
+ hasTime = true,
+ hasChronometer = false,
+ provided = ProvidedTime.Value(0L),
+ expected = ExpectedTime.Time,
+ )
+ }
+
+ @Test
+ @EnableFlags(PromotedNotificationUi.FLAG_NAME, StatusBarNotifChips.FLAG_NAME)
fun extractTime_basicTimeNow() {
assertExtractedTime(
hasTime = true,
hasChronometer = false,
- whenOffset = Duration.ZERO,
+ provided = ProvidedTime.Offset(Duration.ZERO),
expected = ExpectedTime.Time,
)
}
@@ -205,7 +247,7 @@ class PromotedNotificationContentExtractorImplTest : SysuiTestCase() {
assertExtractedTime(
hasTime = true,
hasChronometer = false,
- whenOffset = (-5).minutes,
+ provided = ProvidedTime.Offset((-5).minutes),
expected = ExpectedTime.Time,
)
}
@@ -216,19 +258,31 @@ class PromotedNotificationContentExtractorImplTest : SysuiTestCase() {
assertExtractedTime(
hasTime = true,
hasChronometer = false,
- whenOffset = 5.minutes,
+ provided = ProvidedTime.Offset(5.minutes),
expected = ExpectedTime.Time,
)
}
@Test
@EnableFlags(PromotedNotificationUi.FLAG_NAME, StatusBarNotifChips.FLAG_NAME)
+ fun extractTime_countUpZero() {
+ assertExtractedTime(
+ hasTime = false,
+ hasChronometer = true,
+ isCountDown = false,
+ provided = ProvidedTime.Value(0L),
+ expected = ExpectedTime.CountUp,
+ )
+ }
+
+ @Test
+ @EnableFlags(PromotedNotificationUi.FLAG_NAME, StatusBarNotifChips.FLAG_NAME)
fun extractTime_countUpNow() {
assertExtractedTime(
hasTime = false,
hasChronometer = true,
isCountDown = false,
- whenOffset = Duration.ZERO,
+ provided = ProvidedTime.Offset(Duration.ZERO),
expected = ExpectedTime.CountUp,
)
}
@@ -240,7 +294,7 @@ class PromotedNotificationContentExtractorImplTest : SysuiTestCase() {
hasTime = false,
hasChronometer = true,
isCountDown = false,
- whenOffset = (-5).minutes,
+ provided = ProvidedTime.Offset((-5).minutes),
expected = ExpectedTime.CountUp,
)
}
@@ -252,19 +306,31 @@ class PromotedNotificationContentExtractorImplTest : SysuiTestCase() {
hasTime = false,
hasChronometer = true,
isCountDown = false,
- whenOffset = 5.minutes,
+ provided = ProvidedTime.Offset(5.minutes),
expected = ExpectedTime.CountUp,
)
}
@Test
@EnableFlags(PromotedNotificationUi.FLAG_NAME, StatusBarNotifChips.FLAG_NAME)
+ fun extractTime_countDownZero() {
+ assertExtractedTime(
+ hasTime = false,
+ hasChronometer = true,
+ isCountDown = true,
+ provided = ProvidedTime.Value(0L),
+ expected = ExpectedTime.CountDown,
+ )
+ }
+
+ @Test
+ @EnableFlags(PromotedNotificationUi.FLAG_NAME, StatusBarNotifChips.FLAG_NAME)
fun extractTime_countDownNow() {
assertExtractedTime(
hasTime = false,
hasChronometer = true,
isCountDown = true,
- whenOffset = Duration.ZERO,
+ provided = ProvidedTime.Offset(Duration.ZERO),
expected = ExpectedTime.CountDown,
)
}
@@ -276,7 +342,7 @@ class PromotedNotificationContentExtractorImplTest : SysuiTestCase() {
hasTime = false,
hasChronometer = true,
isCountDown = true,
- whenOffset = (-5).minutes,
+ provided = ProvidedTime.Offset((-5).minutes),
expected = ExpectedTime.CountDown,
)
}
@@ -288,7 +354,7 @@ class PromotedNotificationContentExtractorImplTest : SysuiTestCase() {
hasTime = false,
hasChronometer = true,
isCountDown = true,
- whenOffset = 5.minutes,
+ provided = ProvidedTime.Offset(5.minutes),
expected = ExpectedTime.CountDown,
)
}
@@ -299,6 +365,12 @@ class PromotedNotificationContentExtractorImplTest : SysuiTestCase() {
assertExtractedTime(hasTime = true, hasChronometer = true, expected = ExpectedTime.CountUp)
}
+ private sealed class ProvidedTime {
+ data class Value(val value: Long) : ProvidedTime()
+
+ data class Offset(val offset: Duration = Duration.ZERO) : ProvidedTime()
+ }
+
private enum class ExpectedTime {
Null,
Time,
@@ -310,7 +382,7 @@ class PromotedNotificationContentExtractorImplTest : SysuiTestCase() {
hasTime: Boolean = false,
hasChronometer: Boolean = false,
isCountDown: Boolean = false,
- whenOffset: Duration = Duration.ZERO,
+ provided: ProvidedTime = ProvidedTime.Offset(),
expected: ExpectedTime,
) {
// Set the two timebases to different (arbitrary) numbers, so we can verify whether the
@@ -318,48 +390,60 @@ class PromotedNotificationContentExtractorImplTest : SysuiTestCase() {
systemClock.setCurrentTimeMillis(1_739_570_992_579L)
systemClock.setElapsedRealtime(1_380_967_080L)
- val whenCurrentTime = systemClock.currentTimeMillis() + whenOffset.inWholeMilliseconds
- val whenElapsedRealtime = systemClock.elapsedRealtime() + whenOffset.inWholeMilliseconds
+ val providedCurrentTime =
+ when (provided) {
+ is ProvidedTime.Value -> provided.value
+ is ProvidedTime.Offset ->
+ systemClock.currentTimeMillis() + provided.offset.inWholeMilliseconds
+ }
+
+ val expectedCurrentTime =
+ when (providedCurrentTime) {
+ 0L -> systemClock.currentTimeMillis()
+ else -> providedCurrentTime
+ }
val entry = createEntry {
setShowWhen(hasTime)
setUsesChronometer(hasChronometer)
setChronometerCountDown(isCountDown)
- setWhen(whenCurrentTime)
+ setWhen(providedCurrentTime)
}
- val content = extractContent(entry)
-
- assertThat(content).isNotNull()
+ val content = requireContent(entry).privateVersion
when (expected) {
- ExpectedTime.Null -> assertThat(content?.time).isNull()
+ ExpectedTime.Null -> assertThat(content.time).isNull()
ExpectedTime.Time -> {
- val actual = content?.time as? When.Time
- assertThat(actual).isNotNull()
- assertThat(actual?.currentTimeMillis).isEqualTo(whenCurrentTime)
+ val actual = assertNotNull(content.time as? When.Time)
+ assertThat(actual.currentTimeMillis).isEqualTo(expectedCurrentTime)
}
ExpectedTime.CountDown,
ExpectedTime.CountUp -> {
- val actual = content?.time as? When.Chronometer
- assertThat(actual).isNotNull()
- assertThat(actual?.elapsedRealtimeMillis).isEqualTo(whenElapsedRealtime)
- assertThat(actual?.isCountDown).isEqualTo(expected == ExpectedTime.CountDown)
+ val expectedElapsedRealtime =
+ expectedCurrentTime + systemClock.elapsedRealtime() -
+ systemClock.currentTimeMillis()
+
+ val actual = assertNotNull(content.time as? When.Chronometer)
+ assertThat(actual.elapsedRealtimeMillis).isEqualTo(expectedElapsedRealtime)
+ assertThat(actual.isCountDown).isEqualTo(expected == ExpectedTime.CountDown)
}
}
}
+ // TODO: Add tests for the style of the publicVersion once we implement that
+
@Test
@EnableFlags(PromotedNotificationUi.FLAG_NAME, StatusBarNotifChips.FLAG_NAME)
fun extractContent_fromBaseStyle() {
val entry = createEntry { setStyle(null) }
- val content = extractContent(entry)
+ val content = requireContent(entry)
- assertThat(content).isNotNull()
- assertThat(content?.style).isEqualTo(Style.Base)
+ assertThat(content.privateVersion.style).isEqualTo(Style.Base)
+ assertThat(content.publicVersion.style).isEqualTo(Style.Base)
}
@Test
@@ -367,10 +451,10 @@ class PromotedNotificationContentExtractorImplTest : SysuiTestCase() {
fun extractContent_fromBigPictureStyle() {
val entry = createEntry { setStyle(BigPictureStyle()) }
- val content = extractContent(entry)
+ val content = requireContent(entry)
- assertThat(content).isNotNull()
- assertThat(content?.style).isEqualTo(Style.BigPicture)
+ assertThat(content.privateVersion.style).isEqualTo(Style.BigPicture)
+ assertThat(content.publicVersion.style).isEqualTo(Style.Base)
}
@Test
@@ -387,12 +471,15 @@ class PromotedNotificationContentExtractorImplTest : SysuiTestCase() {
)
}
- val content = extractContent(entry)
+ val content = requireContent(entry)
- assertThat(content).isNotNull()
- assertThat(content?.style).isEqualTo(Style.BigText)
- assertThat(content?.title).isEqualTo(TEST_BIG_CONTENT_TITLE)
- assertThat(content?.text).isEqualTo(TEST_BIG_TEXT)
+ assertThat(content.privateVersion.style).isEqualTo(Style.BigText)
+ assertThat(content.privateVersion.title).isEqualTo(TEST_BIG_CONTENT_TITLE)
+ assertThat(content.privateVersion.text).isEqualTo(TEST_BIG_TEXT)
+
+ assertThat(content.publicVersion.style).isEqualTo(Style.Base)
+ assertThat(content.publicVersion.title).isNull()
+ assertThat(content.publicVersion.text).isNull()
}
@Test
@@ -409,12 +496,15 @@ class PromotedNotificationContentExtractorImplTest : SysuiTestCase() {
)
}
- val content = extractContent(entry)
+ val content = requireContent(entry)
- assertThat(content).isNotNull()
- assertThat(content?.style).isEqualTo(Style.BigText)
- assertThat(content?.title).isEqualTo(TEST_CONTENT_TITLE)
- assertThat(content?.text).isEqualTo(TEST_BIG_TEXT)
+ assertThat(content.privateVersion.style).isEqualTo(Style.BigText)
+ assertThat(content.privateVersion.title).isEqualTo(TEST_CONTENT_TITLE)
+ assertThat(content.privateVersion.text).isEqualTo(TEST_BIG_TEXT)
+
+ assertThat(content.publicVersion.style).isEqualTo(Style.Base)
+ assertThat(content.publicVersion.title).isNull()
+ assertThat(content.publicVersion.text).isNull()
}
@Test
@@ -431,12 +521,15 @@ class PromotedNotificationContentExtractorImplTest : SysuiTestCase() {
)
}
- val content = extractContent(entry)
+ val content = requireContent(entry)
- assertThat(content).isNotNull()
- assertThat(content?.style).isEqualTo(Style.BigText)
- assertThat(content?.title).isEqualTo(TEST_BIG_CONTENT_TITLE)
- assertThat(content?.text).isEqualTo(TEST_CONTENT_TEXT)
+ assertThat(content.privateVersion.style).isEqualTo(Style.BigText)
+ assertThat(content.privateVersion.title).isEqualTo(TEST_BIG_CONTENT_TITLE)
+ assertThat(content.privateVersion.text).isEqualTo(TEST_CONTENT_TEXT)
+
+ assertThat(content.publicVersion.style).isEqualTo(Style.Base)
+ assertThat(content.publicVersion.title).isNull()
+ assertThat(content.publicVersion.text).isNull()
}
@Test
@@ -451,10 +544,14 @@ class PromotedNotificationContentExtractorImplTest : SysuiTestCase() {
)
val entry = createEntry { setStyle(CallStyle.forOngoingCall(TEST_PERSON, hangUpIntent)) }
- val content = extractContent(entry)
+ val content = requireContent(entry)
- assertThat(content).isNotNull()
- assertThat(content?.style).isEqualTo(Style.Call)
+ assertThat(content.privateVersion.style).isEqualTo(Style.Call)
+ assertThat(content.privateVersion.title).isEqualTo(TEST_PERSON_NAME)
+
+ assertThat(content.publicVersion.style).isEqualTo(Style.Base)
+ assertThat(content.publicVersion.title).isNull()
+ assertThat(content.publicVersion.text).isNull()
}
@Test
@@ -468,13 +565,17 @@ class PromotedNotificationContentExtractorImplTest : SysuiTestCase() {
setStyle(ProgressStyle().addProgressSegment(Segment(100)).setProgress(75))
}
- val content = extractContent(entry)
+ val content = requireContent(entry)
- assertThat(content).isNotNull()
- assertThat(content?.style).isEqualTo(Style.Progress)
- assertThat(content?.newProgress).isNotNull()
- assertThat(content?.newProgress?.progress).isEqualTo(75)
- assertThat(content?.newProgress?.progressMax).isEqualTo(100)
+ assertThat(content.privateVersion.style).isEqualTo(Style.Progress)
+ val newProgress = assertNotNull(content.privateVersion.newProgress)
+ assertThat(newProgress.progress).isEqualTo(75)
+ assertThat(newProgress.progressMax).isEqualTo(100)
+
+ assertThat(content.publicVersion.style).isEqualTo(Style.Base)
+ assertThat(content.publicVersion.title).isNull()
+ assertThat(content.publicVersion.text).isNull()
+ assertThat(content.publicVersion.newProgress).isNull()
}
@Test
@@ -484,10 +585,11 @@ class PromotedNotificationContentExtractorImplTest : SysuiTestCase() {
setStyle(MessagingStyle(TEST_PERSON).addMessage("message text", 0L, TEST_PERSON))
}
- val content = extractContent(entry)
+ val content = requireContent(entry)
- assertThat(content).isNotNull()
- assertThat(content?.style).isEqualTo(Style.Ineligible)
+ assertThat(content.privateVersion.style).isEqualTo(Style.Ineligible)
+
+ assertThat(content.publicVersion.style).isEqualTo(Style.Ineligible)
}
@Test
@@ -497,18 +599,13 @@ class PromotedNotificationContentExtractorImplTest : SysuiTestCase() {
setProgress(TEST_PROGRESS_MAX, TEST_PROGRESS, /* indeterminate= */ false)
}
- val content = extractContent(entry)
-
- assertThat(content).isNotNull()
+ val content = requireContent(entry)
- val oldProgress = content?.oldProgress
- assertThat(oldProgress).isNotNull()
+ val oldProgress = assertNotNull(content.privateVersion.oldProgress)
- assertThat(content).isNotNull()
- assertThat(content?.oldProgress).isNotNull()
- assertThat(content?.oldProgress?.progress).isEqualTo(TEST_PROGRESS)
- assertThat(content?.oldProgress?.max).isEqualTo(TEST_PROGRESS_MAX)
- assertThat(content?.oldProgress?.isIndeterminate).isFalse()
+ assertThat(oldProgress.progress).isEqualTo(TEST_PROGRESS)
+ assertThat(oldProgress.max).isEqualTo(TEST_PROGRESS_MAX)
+ assertThat(oldProgress.isIndeterminate).isFalse()
}
@Test
@@ -518,18 +615,25 @@ class PromotedNotificationContentExtractorImplTest : SysuiTestCase() {
setProgress(TEST_PROGRESS_MAX, TEST_PROGRESS, /* indeterminate= */ true)
}
- val content = extractContent(entry)
+ val content = requireContent(entry)
+ val oldProgress = assertNotNull(content.privateVersion.oldProgress)
- assertThat(content).isNotNull()
- assertThat(content?.oldProgress).isNotNull()
- assertThat(content?.oldProgress?.progress).isEqualTo(TEST_PROGRESS)
- assertThat(content?.oldProgress?.max).isEqualTo(TEST_PROGRESS_MAX)
- assertThat(content?.oldProgress?.isIndeterminate).isTrue()
+ assertThat(oldProgress.progress).isEqualTo(TEST_PROGRESS)
+ assertThat(oldProgress.max).isEqualTo(TEST_PROGRESS_MAX)
+ assertThat(oldProgress.isIndeterminate).isTrue()
}
- private fun extractContent(entry: NotificationEntry): PromotedNotificationContentModel? {
+ private fun requireContent(
+ entry: NotificationEntry,
+ redactionType: Int = REDACTION_TYPE_PUBLIC,
+ ): PromotedNotificationContentModels = assertNotNull(extractContent(entry, redactionType))
+
+ private fun extractContent(
+ entry: NotificationEntry,
+ redactionType: Int = REDACTION_TYPE_PUBLIC,
+ ): PromotedNotificationContentModels? {
val recoveredBuilder = Notification.Builder(context, entry.sbn.notification)
- return underTest.extractContent(entry, recoveredBuilder, imageModelProvider)
+ return underTest.extractContent(entry, recoveredBuilder, redactionType, imageModelProvider)
}
private fun createEntry(
@@ -544,6 +648,11 @@ class PromotedNotificationContentExtractorImplTest : SysuiTestCase() {
if (promoted) {
notif.flags = FLAG_PROMOTED_ONGOING
}
+ // Notification uses System.currentTimeMillis() to initialize creationTime; overwrite that
+ // with the value from our mock clock.
+ if (notif.creationTime != 0L) {
+ notif.creationTime = systemClock.currentTimeMillis()
+ }
return NotificationEntryBuilder().setNotification(notif).build()
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/promoted/domain/interactor/PromotedNotificationsInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/promoted/domain/interactor/PromotedNotificationsInteractorTest.kt
index 6926677feda0..42c3f6603ad8 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/promoted/domain/interactor/PromotedNotificationsInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/promoted/domain/interactor/PromotedNotificationsInteractorTest.kt
@@ -16,6 +16,8 @@
package com.android.systemui.statusbar.notification.promoted.domain.interactor
+import android.app.Notification.FLAG_FOREGROUND_SERVICE
+import android.app.Notification.FLAG_ONGOING_EVENT
import android.platform.test.annotations.EnableFlags
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
@@ -25,15 +27,26 @@ import com.android.systemui.kosmos.Kosmos.Fixture
import com.android.systemui.kosmos.collectLastValue
import com.android.systemui.kosmos.runTest
import com.android.systemui.kosmos.useUnconfinedTestDispatcher
+import com.android.systemui.mediaprojection.data.model.MediaProjectionState
+import com.android.systemui.mediaprojection.data.repository.fakeMediaProjectionRepository
+import com.android.systemui.mediaprojection.taskswitcher.FakeActivityTaskManager.Companion.createTask
+import com.android.systemui.screenrecord.data.model.ScreenRecordModel
+import com.android.systemui.screenrecord.data.repository.screenRecordRepository
+import com.android.systemui.statusbar.chips.call.ui.viewmodel.CallChipViewModelTest.Companion.createStatusBarIconViewOrNull
import com.android.systemui.statusbar.chips.notification.domain.interactor.statusBarNotificationChipsInteractor
import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips
import com.android.systemui.statusbar.core.StatusBarRootModernization
-import com.android.systemui.statusbar.notification.buildNotificationEntry
-import com.android.systemui.statusbar.notification.buildOngoingCallEntry
-import com.android.systemui.statusbar.notification.buildPromotedOngoingEntry
+import com.android.systemui.statusbar.notification.collection.buildNotificationEntry
+import com.android.systemui.statusbar.notification.collection.buildOngoingCallEntry
+import com.android.systemui.statusbar.notification.collection.buildPromotedOngoingEntry
+import com.android.systemui.statusbar.notification.data.model.activeNotificationModel
+import com.android.systemui.statusbar.notification.data.repository.activeNotificationListRepository
+import com.android.systemui.statusbar.notification.data.repository.addNotif
import com.android.systemui.statusbar.notification.domain.interactor.renderNotificationListInteractor
import com.android.systemui.statusbar.notification.promoted.PromotedNotificationUi
+import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentBuilder
import com.android.systemui.statusbar.phone.ongoingcall.StatusBarChipsModernization
+import com.android.systemui.statusbar.phone.ongoingcall.shared.model.OngoingCallTestHelper.addOngoingCallState
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import org.junit.Before
@@ -76,6 +89,7 @@ class PromotedNotificationsInteractorTest : SysuiTestCase() {
// THEN the order of the notification keys should be the call then the RON
assertThat(orderedChipNotificationKeys)
.containsExactly("0|test_pkg|0|call|0", "0|test_pkg|0|ron|0")
+ .inOrder()
}
@Test
@@ -96,6 +110,521 @@ class PromotedNotificationsInteractorTest : SysuiTestCase() {
// THEN the order of the notification keys should be the call then the RON
assertThat(orderedChipNotificationKeys)
.containsExactly("0|test_pkg|0|call|0", "0|test_pkg|0|ron|0")
+ .inOrder()
+ }
+
+ @Test
+ fun orderedChipNotificationKeys_noScreenRecordNotif_isEmpty() =
+ kosmos.runTest {
+ screenRecordRepository.screenRecordState.value = ScreenRecordModel.Recording
+ fakeMediaProjectionRepository.mediaProjectionState.value =
+ MediaProjectionState.Projecting.EntireScreen(hostPackage = "test_pkg")
+
+ renderNotificationListInteractor.setRenderedList(emptyList())
+
+ val orderedChipNotificationKeys by
+ collectLastValue(underTest.orderedChipNotificationKeys)
+
+ assertThat(orderedChipNotificationKeys).isEmpty()
+ }
+
+ @Test
+ fun orderedChipNotificationKeys_nullHostPackageForScreenRecord_isEmpty() =
+ kosmos.runTest {
+ screenRecordRepository.screenRecordState.value = ScreenRecordModel.Recording
+ // hostPackage would be provided through mediaProjectionState
+ fakeMediaProjectionRepository.mediaProjectionState.value =
+ MediaProjectionState.NotProjecting
+
+ val entry = buildNotificationEntry(tag = "record", promoted = false)
+ renderNotificationListInteractor.setRenderedList(listOf(entry))
+
+ val orderedChipNotificationKeys by
+ collectLastValue(underTest.orderedChipNotificationKeys)
+
+ assertThat(orderedChipNotificationKeys).isEmpty()
+ }
+
+ @Test
+ fun orderedChipNotificationKeys_containsPromotedScreenRecordNotif() =
+ kosmos.runTest {
+ screenRecordRepository.screenRecordState.value = ScreenRecordModel.Recording
+ fakeMediaProjectionRepository.mediaProjectionState.value =
+ MediaProjectionState.Projecting.EntireScreen(hostPackage = "test_pkg")
+
+ val screenRecordEntry = buildNotificationEntry(tag = "record", promoted = true)
+ renderNotificationListInteractor.setRenderedList(listOf(screenRecordEntry))
+
+ val orderedChipNotificationKeys by
+ collectLastValue(underTest.orderedChipNotificationKeys)
+
+ assertThat(orderedChipNotificationKeys)
+ .containsExactly("0|test_pkg|0|record|0")
+ .inOrder()
+ }
+
+ @Test
+ fun orderedChipNotificationKeys_containsNotPromotedScreenRecordNotif_ifOngoing() =
+ kosmos.runTest {
+ screenRecordRepository.screenRecordState.value = ScreenRecordModel.Recording
+ fakeMediaProjectionRepository.mediaProjectionState.value =
+ MediaProjectionState.Projecting.EntireScreen(hostPackage = "test_pkg")
+
+ val screenRecordEntry =
+ buildNotificationEntry(tag = "record", promoted = false) {
+ setFlag(context, FLAG_ONGOING_EVENT, true)
+ }
+ renderNotificationListInteractor.setRenderedList(listOf(screenRecordEntry))
+
+ val orderedChipNotificationKeys by
+ collectLastValue(underTest.orderedChipNotificationKeys)
+
+ assertThat(orderedChipNotificationKeys)
+ .containsExactly("0|test_pkg|0|record|0")
+ .inOrder()
+ }
+
+ @Test
+ fun orderedChipNotificationKeys_containsNotPromotedScreenRecordNotif_ifFgs() =
+ kosmos.runTest {
+ screenRecordRepository.screenRecordState.value = ScreenRecordModel.Recording
+ fakeMediaProjectionRepository.mediaProjectionState.value =
+ MediaProjectionState.Projecting.EntireScreen(hostPackage = "test_pkg")
+
+ val screenRecordEntry =
+ buildNotificationEntry(tag = "record", promoted = false) {
+ setFlag(context, FLAG_FOREGROUND_SERVICE, true)
+ }
+ renderNotificationListInteractor.setRenderedList(listOf(screenRecordEntry))
+
+ val orderedChipNotificationKeys by
+ collectLastValue(underTest.orderedChipNotificationKeys)
+
+ assertThat(orderedChipNotificationKeys)
+ .containsExactly("0|test_pkg|0|record|0")
+ .inOrder()
+ }
+
+ @Test
+ fun orderedChipNotificationKeys_doesNotContainScreenRecordNotif_ifNotOngoingOrFgs() =
+ kosmos.runTest {
+ screenRecordRepository.screenRecordState.value = ScreenRecordModel.Recording
+ fakeMediaProjectionRepository.mediaProjectionState.value =
+ MediaProjectionState.Projecting.EntireScreen(hostPackage = "test_pkg")
+
+ val screenRecordEntry =
+ buildNotificationEntry(tag = "record", promoted = false) {
+ setFlag(context, FLAG_ONGOING_EVENT, false)
+ setFlag(context, FLAG_FOREGROUND_SERVICE, false)
+ }
+ renderNotificationListInteractor.setRenderedList(listOf(screenRecordEntry))
+
+ val orderedChipNotificationKeys by
+ collectLastValue(underTest.orderedChipNotificationKeys)
+
+ assertThat(orderedChipNotificationKeys).isEmpty()
+ }
+
+ @Test
+ fun orderedChipNotificationKeys_containsFgsScreenRecordNotif_whenNonFgsNotifExists() =
+ kosmos.runTest {
+ screenRecordRepository.screenRecordState.value = ScreenRecordModel.Recording
+ fakeMediaProjectionRepository.mediaProjectionState.value =
+ MediaProjectionState.Projecting.EntireScreen(hostPackage = "test_pkg")
+
+ val fgsEntry =
+ buildNotificationEntry(tag = "recordFgs", promoted = false) {
+ setFlag(context, FLAG_FOREGROUND_SERVICE, true)
+ }
+ val notFgsEntry =
+ buildNotificationEntry(tag = "recordNotFgs", promoted = false) {
+ setFlag(context, FLAG_FOREGROUND_SERVICE, false)
+ }
+ renderNotificationListInteractor.setRenderedList(listOf(fgsEntry, notFgsEntry))
+
+ val orderedChipNotificationKeys by
+ collectLastValue(underTest.orderedChipNotificationKeys)
+
+ assertThat(orderedChipNotificationKeys)
+ .containsExactly("0|test_pkg|0|recordFgs|0")
+ .inOrder()
+ }
+
+ @Test
+ fun orderedChipNotificationKeys_containsOngoingScreenRecordNotif_whenNonOngoingNotifExists() =
+ kosmos.runTest {
+ screenRecordRepository.screenRecordState.value = ScreenRecordModel.Recording
+ fakeMediaProjectionRepository.mediaProjectionState.value =
+ MediaProjectionState.Projecting.EntireScreen(hostPackage = "test_pkg")
+
+ val ongoingEntry =
+ buildNotificationEntry(tag = "recordOngoing", promoted = false) {
+ setFlag(context, FLAG_ONGOING_EVENT, true)
+ }
+ val notOngoingEntry =
+ buildNotificationEntry(tag = "recordNotOngoing", promoted = false) {
+ setFlag(context, FLAG_ONGOING_EVENT, false)
+ }
+ renderNotificationListInteractor.setRenderedList(listOf(notOngoingEntry, ongoingEntry))
+
+ val orderedChipNotificationKeys by
+ collectLastValue(underTest.orderedChipNotificationKeys)
+
+ assertThat(orderedChipNotificationKeys)
+ .containsExactly("0|test_pkg|0|recordOngoing|0")
+ .inOrder()
+ }
+
+ @Test
+ fun orderedChipNotificationKeys_containsFgsOngoingScreenRecordNotif_whenNonFgsOngoingNotifExists() =
+ kosmos.runTest {
+ screenRecordRepository.screenRecordState.value = ScreenRecordModel.Recording
+ fakeMediaProjectionRepository.mediaProjectionState.value =
+ MediaProjectionState.Projecting.EntireScreen(hostPackage = "test_pkg")
+
+ val ongoingAndFgsEntry =
+ buildNotificationEntry(tag = "recordBoth", promoted = false) {
+ setFlag(context, FLAG_FOREGROUND_SERVICE, true)
+ setFlag(context, FLAG_ONGOING_EVENT, true)
+ }
+ val ongoingButNotFgsEntry =
+ buildNotificationEntry(tag = "recordOngoing", promoted = false) {
+ setFlag(context, FLAG_ONGOING_EVENT, true)
+ setFlag(context, FLAG_FOREGROUND_SERVICE, false)
+ }
+ val fgsButNotOngoingEntry =
+ buildNotificationEntry(tag = "recordFgs", promoted = false) {
+ setFlag(context, FLAG_FOREGROUND_SERVICE, true)
+ setFlag(context, FLAG_ONGOING_EVENT, false)
+ }
+ renderNotificationListInteractor.setRenderedList(
+ listOf(fgsButNotOngoingEntry, ongoingButNotFgsEntry, ongoingAndFgsEntry)
+ )
+
+ val orderedChipNotificationKeys by
+ collectLastValue(underTest.orderedChipNotificationKeys)
+
+ assertThat(orderedChipNotificationKeys)
+ .containsExactly("0|test_pkg|0|recordBoth|0")
+ .inOrder()
+ }
+
+ @Test
+ fun orderedChipNotificationKeys_twoEquivalentNotifsForScreenRecord_isEmpty() =
+ kosmos.runTest {
+ screenRecordRepository.screenRecordState.value = ScreenRecordModel.Recording
+ fakeMediaProjectionRepository.mediaProjectionState.value =
+ MediaProjectionState.Projecting.EntireScreen(hostPackage = "test_pkg")
+
+ val entry1 =
+ buildNotificationEntry(tag = "entry1", promoted = false) {
+ setFlag(context, FLAG_FOREGROUND_SERVICE, true)
+ }
+ val entry2 =
+ buildNotificationEntry(tag = "entry2", promoted = false) {
+ setFlag(context, FLAG_FOREGROUND_SERVICE, true)
+ }
+ renderNotificationListInteractor.setRenderedList(listOf(entry1, entry2))
+
+ val orderedChipNotificationKeys by
+ collectLastValue(underTest.orderedChipNotificationKeys)
+
+ assertThat(orderedChipNotificationKeys).isEmpty()
+ }
+
+ @Test
+ fun orderedChipNotificationKeys_noMediProjNotif_isEmpty() =
+ kosmos.runTest {
+ fakeMediaProjectionRepository.mediaProjectionState.value =
+ MediaProjectionState.Projecting.SingleTask(
+ hostPackage = "test_pkg",
+ hostDeviceName = null,
+ createTask(taskId = 1),
+ )
+
+ renderNotificationListInteractor.setRenderedList(emptyList())
+
+ val orderedChipNotificationKeys by
+ collectLastValue(underTest.orderedChipNotificationKeys)
+
+ assertThat(orderedChipNotificationKeys).isEmpty()
+ }
+
+ @Test
+ fun orderedChipNotificationKeys_containsPromotedMediaProjNotif() =
+ kosmos.runTest {
+ fakeMediaProjectionRepository.mediaProjectionState.value =
+ MediaProjectionState.Projecting.SingleTask(
+ hostPackage = "test_pkg",
+ hostDeviceName = null,
+ createTask(taskId = 1),
+ )
+
+ val mediaProjEntry = buildNotificationEntry(tag = "proj", promoted = true)
+ renderNotificationListInteractor.setRenderedList(listOf(mediaProjEntry))
+
+ val orderedChipNotificationKeys by
+ collectLastValue(underTest.orderedChipNotificationKeys)
+
+ assertThat(orderedChipNotificationKeys).containsExactly("0|test_pkg|0|proj|0").inOrder()
+ }
+
+ @Test
+ fun orderedChipNotificationKeys_containsNotPromotedMediaProjNotif_ifOngoing() =
+ kosmos.runTest {
+ fakeMediaProjectionRepository.mediaProjectionState.value =
+ MediaProjectionState.Projecting.SingleTask(
+ hostPackage = "test_pkg",
+ hostDeviceName = null,
+ createTask(taskId = 1),
+ )
+
+ val mediaProjEntry =
+ buildNotificationEntry(tag = "proj", promoted = false) {
+ setFlag(context, FLAG_ONGOING_EVENT, true)
+ }
+ renderNotificationListInteractor.setRenderedList(listOf(mediaProjEntry))
+
+ val orderedChipNotificationKeys by
+ collectLastValue(underTest.orderedChipNotificationKeys)
+
+ assertThat(orderedChipNotificationKeys).containsExactly("0|test_pkg|0|proj|0").inOrder()
+ }
+
+ @Test
+ fun orderedChipNotificationKeys_containsNotPromotedMediaProjNotif_ifFgs() =
+ kosmos.runTest {
+ fakeMediaProjectionRepository.mediaProjectionState.value =
+ MediaProjectionState.Projecting.SingleTask(
+ hostPackage = "test_pkg",
+ hostDeviceName = null,
+ createTask(taskId = 1),
+ )
+
+ val mediaProjEntry =
+ buildNotificationEntry(tag = "proj", promoted = false) {
+ setFlag(context, FLAG_FOREGROUND_SERVICE, true)
+ }
+ renderNotificationListInteractor.setRenderedList(listOf(mediaProjEntry))
+
+ val orderedChipNotificationKeys by
+ collectLastValue(underTest.orderedChipNotificationKeys)
+
+ assertThat(orderedChipNotificationKeys).containsExactly("0|test_pkg|0|proj|0").inOrder()
+ }
+
+ @Test
+ fun orderedChipNotificationKeys_doesNotContainMediaProjNotif_ifNotOngoingOrFgs() =
+ kosmos.runTest {
+ fakeMediaProjectionRepository.mediaProjectionState.value =
+ MediaProjectionState.Projecting.SingleTask(
+ hostPackage = "test_pkg",
+ hostDeviceName = null,
+ createTask(taskId = 1),
+ )
+
+ val mediaProjEntry =
+ buildNotificationEntry(tag = "proj", promoted = false) {
+ setFlag(context, FLAG_ONGOING_EVENT, false)
+ setFlag(context, FLAG_FOREGROUND_SERVICE, false)
+ }
+ renderNotificationListInteractor.setRenderedList(listOf(mediaProjEntry))
+
+ val orderedChipNotificationKeys by
+ collectLastValue(underTest.orderedChipNotificationKeys)
+
+ assertThat(orderedChipNotificationKeys).isEmpty()
+ }
+
+ @Test
+ fun orderedChipNotificationKeys_containsFgsMediaProjNotif_whenNonFgsNotifExists() =
+ kosmos.runTest {
+ fakeMediaProjectionRepository.mediaProjectionState.value =
+ MediaProjectionState.Projecting.SingleTask(
+ hostPackage = "test_pkg",
+ hostDeviceName = null,
+ createTask(taskId = 1),
+ )
+
+ val fgsEntry =
+ buildNotificationEntry(tag = "projFgs", promoted = false) {
+ setFlag(context, FLAG_FOREGROUND_SERVICE, true)
+ }
+ val notFgsEntry =
+ buildNotificationEntry(tag = "projNotFgs", promoted = false) {
+ setFlag(context, FLAG_FOREGROUND_SERVICE, false)
+ }
+ renderNotificationListInteractor.setRenderedList(listOf(fgsEntry, notFgsEntry))
+
+ val orderedChipNotificationKeys by
+ collectLastValue(underTest.orderedChipNotificationKeys)
+
+ assertThat(orderedChipNotificationKeys)
+ .containsExactly("0|test_pkg|0|projFgs|0")
+ .inOrder()
+ }
+
+ @Test
+ fun orderedChipNotificationKeys_containsOngoingMediaProjNotif_whenNonOngoingNotifExists() =
+ kosmos.runTest {
+ fakeMediaProjectionRepository.mediaProjectionState.value =
+ MediaProjectionState.Projecting.SingleTask(
+ hostPackage = "test_pkg",
+ hostDeviceName = null,
+ createTask(taskId = 1),
+ )
+
+ val ongoingEntry =
+ buildNotificationEntry(tag = "projOngoing", promoted = false) {
+ setFlag(context, FLAG_ONGOING_EVENT, true)
+ }
+ val notOngoingEntry =
+ buildNotificationEntry(tag = "projNotOngoing", promoted = false) {
+ setFlag(context, FLAG_ONGOING_EVENT, false)
+ }
+ renderNotificationListInteractor.setRenderedList(listOf(notOngoingEntry, ongoingEntry))
+
+ val orderedChipNotificationKeys by
+ collectLastValue(underTest.orderedChipNotificationKeys)
+
+ assertThat(orderedChipNotificationKeys)
+ .containsExactly("0|test_pkg|0|projOngoing|0")
+ .inOrder()
+ }
+
+ @Test
+ fun orderedChipNotificationKeys_containsFgsOngoingMediaProjNotif_whenNonFgsOngoingNotifExists() =
+ kosmos.runTest {
+ fakeMediaProjectionRepository.mediaProjectionState.value =
+ MediaProjectionState.Projecting.SingleTask(
+ hostPackage = "test_pkg",
+ hostDeviceName = null,
+ createTask(taskId = 1),
+ )
+
+ val ongoingAndFgsEntry =
+ buildNotificationEntry(tag = "projBoth", promoted = false) {
+ setFlag(context, FLAG_FOREGROUND_SERVICE, true)
+ setFlag(context, FLAG_ONGOING_EVENT, true)
+ }
+ val ongoingButNotFgsEntry =
+ buildNotificationEntry(tag = "projOngoing", promoted = false) {
+ setFlag(context, FLAG_ONGOING_EVENT, true)
+ setFlag(context, FLAG_FOREGROUND_SERVICE, false)
+ }
+ val fgsButNotOngoingEntry =
+ buildNotificationEntry(tag = "projFgs", promoted = false) {
+ setFlag(context, FLAG_FOREGROUND_SERVICE, true)
+ setFlag(context, FLAG_ONGOING_EVENT, false)
+ }
+ renderNotificationListInteractor.setRenderedList(
+ listOf(fgsButNotOngoingEntry, ongoingButNotFgsEntry, ongoingAndFgsEntry)
+ )
+
+ val orderedChipNotificationKeys by
+ collectLastValue(underTest.orderedChipNotificationKeys)
+
+ assertThat(orderedChipNotificationKeys)
+ .containsExactly("0|test_pkg|0|projBoth|0")
+ .inOrder()
+ }
+
+ @Test
+ fun orderedChipNotificationKeys_twoEquivalentNotifsForMediaProj_isEmpty() =
+ kosmos.runTest {
+ fakeMediaProjectionRepository.mediaProjectionState.value =
+ MediaProjectionState.Projecting.SingleTask(
+ hostPackage = "test_pkg",
+ hostDeviceName = null,
+ createTask(taskId = 1),
+ )
+
+ val entry1 =
+ buildNotificationEntry(tag = "entry1", promoted = false) {
+ setFlag(context, FLAG_FOREGROUND_SERVICE, true)
+ }
+ val entry2 =
+ buildNotificationEntry(tag = "entry2", promoted = false) {
+ setFlag(context, FLAG_FOREGROUND_SERVICE, true)
+ }
+ renderNotificationListInteractor.setRenderedList(listOf(entry1, entry2))
+
+ val orderedChipNotificationKeys by
+ collectLastValue(underTest.orderedChipNotificationKeys)
+
+ assertThat(orderedChipNotificationKeys).isEmpty()
+ }
+
+ @Test
+ fun orderedChipNotificationKeys_maintainsPromotedNotifOrder() =
+ kosmos.runTest {
+ activeNotificationListRepository.addNotif(
+ activeNotificationModel(
+ key = "notif1",
+ statusBarChipIcon = createStatusBarIconViewOrNull(),
+ promotedContent = PromotedNotificationContentBuilder("notif1").build(),
+ )
+ )
+ activeNotificationListRepository.addNotif(
+ activeNotificationModel(
+ key = "notif2",
+ statusBarChipIcon = createStatusBarIconViewOrNull(),
+ promotedContent = PromotedNotificationContentBuilder("notif2").build(),
+ )
+ )
+
+ val orderedChipNotificationKeys by
+ collectLastValue(underTest.orderedChipNotificationKeys)
+
+ assertThat(orderedChipNotificationKeys).containsExactly("notif1", "notif2").inOrder()
+ }
+
+ // The ranking between different chips should stay consistent between
+ // PromotedNotificationsInteractor and OngoingActivityChipsViewModel.
+ // See OngoingActivityChipsWithNotifsViewModelTest#chips_screenRecordAndCallAndPromotedNotifs
+ // test for the right ranking.
+ @Test
+ fun orderedChipNotificationKeys_rankingIsCorrect() =
+ kosmos.runTest {
+ // Screen record
+ screenRecordRepository.screenRecordState.value = ScreenRecordModel.Recording
+ fakeMediaProjectionRepository.mediaProjectionState.value =
+ MediaProjectionState.Projecting.SingleTask(
+ hostPackage = "screen.record.package",
+ hostDeviceName = null,
+ createTask(taskId = 1),
+ )
+ activeNotificationListRepository.addNotif(
+ activeNotificationModel(
+ key = "screenRecordKey",
+ packageName = "screen.record.package",
+ isOngoingEvent = true,
+ )
+ )
+ // Call
+ addOngoingCallState(key = "callKey")
+ // Other promoted notifs
+ activeNotificationListRepository.addNotif(
+ activeNotificationModel(
+ key = "notif1",
+ statusBarChipIcon = createStatusBarIconViewOrNull(),
+ promotedContent = PromotedNotificationContentBuilder("notif1").build(),
+ )
+ )
+ activeNotificationListRepository.addNotif(
+ activeNotificationModel(
+ key = "notif2",
+ statusBarChipIcon = createStatusBarIconViewOrNull(),
+ promotedContent = PromotedNotificationContentBuilder("notif2").build(),
+ )
+ )
+
+ val orderedChipNotificationKeys by
+ collectLastValue(underTest.orderedChipNotificationKeys)
+
+ assertThat(orderedChipNotificationKeys)
+ .containsExactly("screenRecordKey", "callKey", "notif1", "notif2")
+ .inOrder()
}
@Test
@@ -114,8 +643,7 @@ class PromotedNotificationsInteractorTest : SysuiTestCase() {
collectLastValue(underTest.aodPromotedNotification)
// THEN the ron is first because the call has no content
- assertThat(topPromotedNotificationContent?.identity?.key)
- .isEqualTo("0|test_pkg|0|ron|0")
+ assertThat(topPromotedNotificationContent?.key).isEqualTo("0|test_pkg|0|ron|0")
}
@Test
@@ -134,8 +662,7 @@ class PromotedNotificationsInteractorTest : SysuiTestCase() {
collectLastValue(underTest.aodPromotedNotification)
// THEN the call is the top notification
- assertThat(topPromotedNotificationContent?.identity?.key)
- .isEqualTo("0|test_pkg|0|call|0")
+ assertThat(topPromotedNotificationContent?.key).isEqualTo("0|test_pkg|0|call|0")
}
@Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowControllerTest.kt
index d306a5bea433..0d453352e882 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowControllerTest.kt
@@ -51,6 +51,7 @@ import com.android.systemui.statusbar.notification.headsup.HeadsUpManager
import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRowController.BUBBLES_SETTING_URI
import com.android.systemui.statusbar.notification.row.icon.NotificationIconStyleProvider
+import com.android.systemui.statusbar.notification.shared.NotificationBundleUi
import com.android.systemui.statusbar.notification.stack.NotificationChildrenContainer
import com.android.systemui.statusbar.notification.stack.NotificationChildrenContainerLogger
import com.android.systemui.statusbar.notification.stack.NotificationListContainer
@@ -318,7 +319,7 @@ class ExpandableNotificationRowControllerTest : SysuiTestCase() {
val notification = Notification.Builder(mContext).build()
val sbn =
SbnBuilder().setNotification(notification).setUser(UserHandle.of(USER_ALL)).build()
- whenever(view.entry)
+ whenever(view.entryLegacy)
.thenReturn(
NotificationEntryBuilder().setSbn(sbn).setUser(UserHandle.of(USER_ALL)).build()
)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/FeedbackInfoTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/FeedbackInfoTest.java
index 0947cd5aaabb..e808deb1d5ad 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/FeedbackInfoTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/FeedbackInfoTest.java
@@ -44,6 +44,7 @@ import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.graphics.drawable.Drawable;
import android.os.UserHandle;
+import android.service.notification.NotificationListenerService;
import android.service.notification.StatusBarNotification;
import android.view.LayoutInflater;
import android.view.View;
@@ -132,7 +133,7 @@ public class FeedbackInfoTest extends SysuiTestCase {
@Test
public void testBindNotification_SetsTextApplicationName() {
when(mMockPackageManager.getApplicationLabel(any())).thenReturn("App Name");
- mFeedbackInfo.bindGuts(mMockPackageManager, mSbn, getEntry(),
+ mFeedbackInfo.bindGuts(mMockPackageManager, mSbn, getEntry().getRanking(),
mMockNotificationRow, mAssistantFeedbackController, mStatusBarService,
mNotificationGutsManager);
final TextView textView = mFeedbackInfo.findViewById(R.id.pkg_name);
@@ -144,7 +145,7 @@ public class FeedbackInfoTest extends SysuiTestCase {
final Drawable iconDrawable = mock(Drawable.class);
when(mMockPackageManager.getApplicationIcon(any(ApplicationInfo.class)))
.thenReturn(iconDrawable);
- mFeedbackInfo.bindGuts(mMockPackageManager, mSbn, getEntry(),
+ mFeedbackInfo.bindGuts(mMockPackageManager, mSbn, getEntry().getRanking(),
mMockNotificationRow, mAssistantFeedbackController, mStatusBarService,
mNotificationGutsManager);
final ImageView iconView = mFeedbackInfo.findViewById(R.id.pkg_icon);
@@ -153,9 +154,11 @@ public class FeedbackInfoTest extends SysuiTestCase {
@Test
public void testPrompt_silenced() {
- when(mAssistantFeedbackController.getFeedbackStatus(any(NotificationEntry.class)))
+ when(mAssistantFeedbackController.getFeedbackStatus(
+ any(NotificationListenerService.Ranking.class)))
.thenReturn(STATUS_SILENCED);
- mFeedbackInfo.bindGuts(mMockPackageManager, mSbn, getEntry(), mMockNotificationRow,
+ mFeedbackInfo.bindGuts(mMockPackageManager, mSbn, getEntry().getRanking(),
+ mMockNotificationRow,
mAssistantFeedbackController, mStatusBarService, mNotificationGutsManager);
TextView prompt = mFeedbackInfo.findViewById(R.id.prompt);
assertEquals("This notification was automatically demoted to Silent by the system. "
@@ -165,9 +168,11 @@ public class FeedbackInfoTest extends SysuiTestCase {
@Test
public void testPrompt_promoted() {
- when(mAssistantFeedbackController.getFeedbackStatus(any(NotificationEntry.class)))
+ when(mAssistantFeedbackController.getFeedbackStatus(
+ any(NotificationListenerService.Ranking.class)))
.thenReturn(STATUS_PROMOTED);
- mFeedbackInfo.bindGuts(mMockPackageManager, mSbn, getEntry(), mMockNotificationRow,
+ mFeedbackInfo.bindGuts(mMockPackageManager, mSbn, getEntry().getRanking(),
+ mMockNotificationRow,
mAssistantFeedbackController, mStatusBarService, mNotificationGutsManager);
TextView prompt = mFeedbackInfo.findViewById(R.id.prompt);
assertEquals("This notification was automatically ranked higher in your shade. "
@@ -177,9 +182,11 @@ public class FeedbackInfoTest extends SysuiTestCase {
@Test
public void testPrompt_alerted() {
- when(mAssistantFeedbackController.getFeedbackStatus(any(NotificationEntry.class)))
+ when(mAssistantFeedbackController.getFeedbackStatus(
+ any(NotificationListenerService.Ranking.class)))
.thenReturn(STATUS_ALERTED);
- mFeedbackInfo.bindGuts(mMockPackageManager, mSbn, getEntry(), mMockNotificationRow,
+ mFeedbackInfo.bindGuts(mMockPackageManager, mSbn, getEntry().getRanking(),
+ mMockNotificationRow,
mAssistantFeedbackController, mStatusBarService, mNotificationGutsManager);
TextView prompt = mFeedbackInfo.findViewById(R.id.prompt);
assertEquals("This notification was automatically promoted to Default by the system. "
@@ -189,9 +196,11 @@ public class FeedbackInfoTest extends SysuiTestCase {
@Test
public void testPrompt_demoted() {
- when(mAssistantFeedbackController.getFeedbackStatus(any(NotificationEntry.class)))
+ when(mAssistantFeedbackController.getFeedbackStatus(
+ any(NotificationListenerService.Ranking.class)))
.thenReturn(STATUS_DEMOTED);
- mFeedbackInfo.bindGuts(mMockPackageManager, mSbn, getEntry(), mMockNotificationRow,
+ mFeedbackInfo.bindGuts(mMockPackageManager, mSbn, getEntry().getRanking(),
+ mMockNotificationRow,
mAssistantFeedbackController, mStatusBarService, mNotificationGutsManager);
TextView prompt = mFeedbackInfo.findViewById(R.id.prompt);
assertEquals("This notification was automatically ranked lower in your shade. "
@@ -201,7 +210,8 @@ public class FeedbackInfoTest extends SysuiTestCase {
@Test
public void testPositiveFeedback() {
- mFeedbackInfo.bindGuts(mMockPackageManager, mSbn, getEntry(), mMockNotificationRow,
+ mFeedbackInfo.bindGuts(mMockPackageManager, mSbn, getEntry().getRanking(),
+ mMockNotificationRow,
mAssistantFeedbackController, mStatusBarService, mNotificationGutsManager);
final View yes = mFeedbackInfo.findViewById(R.id.yes);
@@ -218,7 +228,8 @@ public class FeedbackInfoTest extends SysuiTestCase {
any(NotificationMenuRowPlugin.MenuItem.class)))
.thenReturn(true);
- mFeedbackInfo.bindGuts(mMockPackageManager, mSbn, getEntry(), mMockNotificationRow,
+ mFeedbackInfo.bindGuts(mMockPackageManager, mSbn, getEntry().getRanking(),
+ mMockNotificationRow,
mAssistantFeedbackController, mStatusBarService, mNotificationGutsManager);
final View no = mFeedbackInfo.findViewById(R.id.no);
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java
index 99f2596dbf1d..19b1046f1931 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java
@@ -67,11 +67,11 @@ import com.android.systemui.media.controls.util.MediaFeatureFlag;
import com.android.systemui.statusbar.NotificationRemoteInputManager;
import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips;
import com.android.systemui.statusbar.notification.ConversationNotificationProcessor;
-import com.android.systemui.statusbar.notification.collection.EntryAdapter;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.promoted.FakePromotedNotificationContentExtractor;
import com.android.systemui.statusbar.notification.promoted.PromotedNotificationUi;
-import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel;
+import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentBuilder;
+import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModels;
import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.BindParams;
import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationCallback;
import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag;
@@ -389,8 +389,8 @@ public class NotificationContentInflaterTest extends SysuiTestCase {
@Test
@DisableFlags({PromotedNotificationUi.FLAG_NAME, StatusBarNotifChips.FLAG_NAME})
public void testExtractsPromotedContent_notWhenBothFlagsDisabled() throws Exception {
- final PromotedNotificationContentModel content =
- new PromotedNotificationContentModel.Builder("key").build();
+ final PromotedNotificationContentModels content =
+ new PromotedNotificationContentBuilder("key").build();
mPromotedNotificationContentExtractor.resetForEntry(mRow.getEntry(), content);
inflateAndWait(mNotificationInflater, FLAG_CONTENT_VIEW_ALL, mRow);
@@ -401,43 +401,43 @@ public class NotificationContentInflaterTest extends SysuiTestCase {
@Test
@EnableFlags(PromotedNotificationUi.FLAG_NAME)
@DisableFlags(StatusBarNotifChips.FLAG_NAME)
- public void testExtractsPromotedContent_whenPromotedNotificationUiFlagEnabled()
+ public void testExtractsPromotedContent_whePromotedNotificationUiFlagEnabled()
throws Exception {
- final PromotedNotificationContentModel content =
- new PromotedNotificationContentModel.Builder("key").build();
+ final PromotedNotificationContentModels content =
+ new PromotedNotificationContentBuilder("key").build();
mPromotedNotificationContentExtractor.resetForEntry(mRow.getEntry(), content);
inflateAndWait(mNotificationInflater, FLAG_CONTENT_VIEW_ALL, mRow);
mPromotedNotificationContentExtractor.verifyOneExtractCall();
- assertEquals(content, mRow.getEntry().getPromotedNotificationContentModel());
+ assertEquals(content, mRow.getEntry().getPromotedNotificationContentModels());
}
@Test
@EnableFlags(StatusBarNotifChips.FLAG_NAME)
@DisableFlags(PromotedNotificationUi.FLAG_NAME)
public void testExtractsPromotedContent_whenStatusBarNotifChipsFlagEnabled() throws Exception {
- final PromotedNotificationContentModel content =
- new PromotedNotificationContentModel.Builder("key").build();
+ final PromotedNotificationContentModels content =
+ new PromotedNotificationContentBuilder("key").build();
mPromotedNotificationContentExtractor.resetForEntry(mRow.getEntry(), content);
inflateAndWait(mNotificationInflater, FLAG_CONTENT_VIEW_ALL, mRow);
mPromotedNotificationContentExtractor.verifyOneExtractCall();
- assertEquals(content, mRow.getEntry().getPromotedNotificationContentModel());
+ assertEquals(content, mRow.getEntry().getPromotedNotificationContentModels());
}
@Test
@EnableFlags({PromotedNotificationUi.FLAG_NAME, StatusBarNotifChips.FLAG_NAME})
public void testExtractsPromotedContent_whenBothFlagsEnabled() throws Exception {
- final PromotedNotificationContentModel content =
- new PromotedNotificationContentModel.Builder("key").build();
+ final PromotedNotificationContentModels content =
+ new PromotedNotificationContentBuilder("key").build();
mPromotedNotificationContentExtractor.resetForEntry(mRow.getEntry(), content);
inflateAndWait(mNotificationInflater, FLAG_CONTENT_VIEW_ALL, mRow);
mPromotedNotificationContentExtractor.verifyOneExtractCall();
- assertEquals(content, mRow.getEntry().getPromotedNotificationContentModel());
+ assertEquals(content, mRow.getEntry().getPromotedNotificationContentModels());
}
@Test
@@ -448,7 +448,7 @@ public class NotificationContentInflaterTest extends SysuiTestCase {
inflateAndWait(mNotificationInflater, FLAG_CONTENT_VIEW_ALL, mRow);
mPromotedNotificationContentExtractor.verifyOneExtractCall();
- assertNull(mRow.getEntry().getPromotedNotificationContentModel());
+ assertNull(mRow.getEntry().getPromotedNotificationContentModels());
}
@Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.kt
index 387c62d76083..324487b121bc 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.kt
@@ -67,15 +67,19 @@ import com.android.systemui.statusbar.NotificationLockscreenUserManager
import com.android.systemui.statusbar.NotificationPresenter
import com.android.systemui.statusbar.notification.AssistantFeedbackController
import com.android.systemui.statusbar.notification.NotificationActivityStarter
+import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider
+import com.android.systemui.statusbar.notification.collection.provider.mockHighPriorityProvider
import com.android.systemui.statusbar.notification.domain.interactor.activeNotificationsInteractor
import com.android.systemui.statusbar.notification.headsup.HeadsUpManager
+import com.android.systemui.statusbar.notification.headsup.mockHeadsUpManager
import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier
import com.android.systemui.statusbar.notification.promoted.domain.interactor.PackageDemotionInteractor
import com.android.systemui.statusbar.notification.row.icon.AppIconProvider
import com.android.systemui.statusbar.notification.row.icon.NotificationIconStyleProvider
import com.android.systemui.statusbar.notification.row.icon.appIconProvider
import com.android.systemui.statusbar.notification.row.icon.notificationIconStyleProvider
+import com.android.systemui.statusbar.notification.shared.NotificationBundleUi
import com.android.systemui.statusbar.notification.stack.NotificationListContainer
import com.android.systemui.statusbar.policy.DeviceProvisionedController
import com.android.systemui.testKosmos
@@ -263,7 +267,11 @@ class NotificationGutsManagerTest(flags: FlagsParameterization) : SysuiTestCase(
assertEquals(View.INVISIBLE.toLong(), guts.visibility.toLong())
executor.runAllReady()
verify(guts).openControls(anyInt(), anyInt(), anyBoolean(), any<Runnable>())
- verify(headsUpManager).setGutsShown(realRow.entry, true)
+ if (NotificationBundleUi.isEnabled) {
+ verify(kosmos.mockHeadsUpManager).setGutsShown(any<NotificationEntry>(), eq(true))
+ } else {
+ verify(headsUpManager).setGutsShown(realRow.entry, true)
+ }
assertEquals(View.VISIBLE.toLong(), guts.visibility.toLong())
gutsManager.closeAndSaveGuts(false, false, true, 0, 0, false)
@@ -271,7 +279,11 @@ class NotificationGutsManagerTest(flags: FlagsParameterization) : SysuiTestCase(
verify(guts).closeControls(anyBoolean(), anyBoolean(), anyInt(), anyInt(), anyBoolean())
verify(row, times(1)).setGutsView(any<MenuItem>())
executor.runAllReady()
- verify(headsUpManager).setGutsShown(realRow.entry, false)
+ if (NotificationBundleUi.isEnabled) {
+ verify(kosmos.mockHeadsUpManager).setGutsShown(any<NotificationEntry>(), eq(false))
+ } else {
+ verify(headsUpManager).setGutsShown(realRow.entry, false)
+ }
}
@Test
@@ -301,7 +313,11 @@ class NotificationGutsManagerTest(flags: FlagsParameterization) : SysuiTestCase(
verify(guts).closeControls(anyInt(), anyInt(), eq(false), eq(false))
verify(row, times(1)).setGutsView(any<MenuItem>())
executor.runAllReady()
- verify(headsUpManager).setGutsShown(realRow.entry, false)
+ if (NotificationBundleUi.isEnabled) {
+ verify(kosmos.mockHeadsUpManager).setGutsShown(any<NotificationEntry>(), eq(false))
+ } else {
+ verify(headsUpManager).setGutsShown(realRow.entry, false)
+ }
}
@Test
@@ -505,17 +521,22 @@ class NotificationGutsManagerTest(flags: FlagsParameterization) : SysuiTestCase(
@Throws(Exception::class)
fun testInitializeNotificationInfoView_highPriority() {
val notificationInfoView: NotificationInfo = mock()
- val row = spy(helper.createRow())
+ val row = createTestNotificationRow()
val entry = row.entry
NotificationEntryHelper.modifyRanking(entry)
.setUserSentiment(NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE)
.setImportance(NotificationManager.IMPORTANCE_HIGH)
.build()
- whenever(row.canViewBeDismissed()).thenReturn(true)
whenever(highPriorityProvider.isHighPriority(entry)).thenReturn(true)
+ whenever(kosmos.mockHighPriorityProvider.isHighPriority(entry)).thenReturn(true)
val statusBarNotification = entry.sbn
- gutsManager.initializeNotificationInfo(row, notificationInfoView)
+ gutsManager.initializeNotificationInfo(
+ row,
+ statusBarNotification,
+ entry.ranking,
+ notificationInfoView,
+ )
verify(notificationInfoView)
.bindNotification(
@@ -527,15 +548,17 @@ class NotificationGutsManagerTest(flags: FlagsParameterization) : SysuiTestCase(
eq(channelEditorDialogController),
any<PackageDemotionInteractor>(),
eq(statusBarNotification.packageName),
- any<NotificationChannel>(),
- eq(entry),
+ eq(entry.ranking),
+ eq(statusBarNotification),
+ if (NotificationBundleUi.isEnabled) eq(null) else eq(entry),
+ if (NotificationBundleUi.isEnabled) eq(row.entryAdapter) else eq(null),
any<NotificationInfo.OnSettingsClickListener>(),
any<NotificationInfo.OnAppSettingsClickListener>(),
any<NotificationInfo.OnFeedbackClickListener>(),
any<UiEventLogger>(),
/* isDeviceProvisioned = */ eq(false),
/* isNonblockable = */ eq(false),
- /* isDismissable = */ eq(true),
+ /* isDismissable = */ eq(false),
/* wasShownHighPriority = */ eq(true),
eq(assistantFeedbackController),
eq(metricsLogger),
@@ -547,17 +570,21 @@ class NotificationGutsManagerTest(flags: FlagsParameterization) : SysuiTestCase(
@Throws(Exception::class)
fun testInitializeNotificationInfoView_PassesAlongProvisionedState() {
val notificationInfoView: NotificationInfo = mock()
- val row = spy(helper.createRow())
+ val row = createTestNotificationRow()
NotificationEntryHelper.modifyRanking(row.entry)
.setUserSentiment(NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE)
.build()
- whenever(row.canViewBeDismissed()).thenReturn(true)
val statusBarNotification = row.entry.sbn
val entry = row.entry
whenever(deviceProvisionedController.isDeviceProvisioned).thenReturn(true)
- gutsManager.initializeNotificationInfo(row, notificationInfoView)
+ gutsManager.initializeNotificationInfo(
+ row,
+ statusBarNotification,
+ entry.ranking,
+ notificationInfoView,
+ )
verify(notificationInfoView)
.bindNotification(
@@ -569,15 +596,17 @@ class NotificationGutsManagerTest(flags: FlagsParameterization) : SysuiTestCase(
eq(channelEditorDialogController),
any<PackageDemotionInteractor>(),
eq(statusBarNotification.packageName),
- any<NotificationChannel>(),
- eq(entry),
+ eq(entry.ranking),
+ eq(statusBarNotification),
+ if (NotificationBundleUi.isEnabled) eq(null) else eq(entry),
+ if (NotificationBundleUi.isEnabled) eq(row.entryAdapter) else eq(null),
any<NotificationInfo.OnSettingsClickListener>(),
any<NotificationInfo.OnAppSettingsClickListener>(),
any<NotificationInfo.OnFeedbackClickListener>(),
any<UiEventLogger>(),
/* isDeviceProvisioned = */ eq(true),
/* isNonblockable = */ eq(false),
- /* isDismissable = */ eq(true),
+ /* isDismissable = */ eq(false),
/* wasShownHighPriority = */ eq(false),
eq(assistantFeedbackController),
eq(metricsLogger),
@@ -589,15 +618,19 @@ class NotificationGutsManagerTest(flags: FlagsParameterization) : SysuiTestCase(
@Throws(Exception::class)
fun testInitializeNotificationInfoView_withInitialAction() {
val notificationInfoView: NotificationInfo = mock()
- val row = spy(helper.createRow())
+ val row = createTestNotificationRow()
NotificationEntryHelper.modifyRanking(row.entry)
.setUserSentiment(NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE)
.build()
- whenever(row.canViewBeDismissed()).thenReturn(true)
val statusBarNotification = row.entry.sbn
val entry = row.entry
- gutsManager.initializeNotificationInfo(row, notificationInfoView)
+ gutsManager.initializeNotificationInfo(
+ row,
+ statusBarNotification,
+ entry.ranking,
+ notificationInfoView,
+ )
verify(notificationInfoView)
.bindNotification(
@@ -609,15 +642,17 @@ class NotificationGutsManagerTest(flags: FlagsParameterization) : SysuiTestCase(
eq(channelEditorDialogController),
any<PackageDemotionInteractor>(),
eq(statusBarNotification.packageName),
- any<NotificationChannel>(),
- eq(entry),
+ eq(entry.ranking),
+ eq(statusBarNotification),
+ if (NotificationBundleUi.isEnabled) eq(null) else eq(entry),
+ if (NotificationBundleUi.isEnabled) eq(row.entryAdapter) else eq(null),
any<NotificationInfo.OnSettingsClickListener>(),
any<NotificationInfo.OnAppSettingsClickListener>(),
any<NotificationInfo.OnFeedbackClickListener>(),
any<UiEventLogger>(),
/* isDeviceProvisioned = */ eq(false),
/* isNonblockable = */ eq(false),
- /* isDismissable = */ eq(true),
+ /* isDismissable = */ eq(false),
/* wasShownHighPriority = */ eq(false),
eq(assistantFeedbackController),
eq(metricsLogger),
@@ -639,6 +674,7 @@ class NotificationGutsManagerTest(flags: FlagsParameterization) : SysuiTestCase(
NotificationEntryHelper.modifyRanking(row.entry)
.setChannel(testNotificationChannel)
.build()
+ row.entryAdapter = kosmos.entryAdapterFactory.create(row.entry)
return row
} catch (e: Exception) {
fail()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.kt
index 0ac5fe95957c..b2521b0ce766 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.kt
@@ -62,13 +62,16 @@ import com.android.systemui.kosmos.testCase
import com.android.systemui.res.R
import com.android.systemui.statusbar.RankingBuilder
import com.android.systemui.statusbar.notification.AssistantFeedbackController
+import com.android.systemui.statusbar.notification.collection.EntryAdapter
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder
+import com.android.systemui.statusbar.notification.collection.coordinator.mockVisualStabilityCoordinator
import com.android.systemui.statusbar.notification.promoted.domain.interactor.PackageDemotionInteractor
import com.android.systemui.statusbar.notification.row.icon.AppIconProvider
import com.android.systemui.statusbar.notification.row.icon.NotificationIconStyleProvider
-import com.android.systemui.statusbar.notification.row.icon.appIconProvider
-import com.android.systemui.statusbar.notification.row.icon.notificationIconStyleProvider
+import com.android.systemui.statusbar.notification.row.icon.mockAppIconProvider
+import com.android.systemui.statusbar.notification.row.icon.mockNotificationIconStyleProvider
+import com.android.systemui.statusbar.notification.shared.NotificationBundleUi
import com.android.systemui.testKosmos
import com.android.telecom.telecomManager
import com.google.common.truth.Truth.assertThat
@@ -80,6 +83,7 @@ import org.mockito.ArgumentMatchers.anyBoolean
import org.mockito.ArgumentMatchers.anyInt
import org.mockito.ArgumentMatchers.anyString
import org.mockito.kotlin.any
+import org.mockito.kotlin.anyOrNull
import org.mockito.kotlin.argumentCaptor
import org.mockito.kotlin.eq
import org.mockito.kotlin.mock
@@ -99,8 +103,11 @@ class NotificationInfoTest : SysuiTestCase() {
private lateinit var classifiedNotificationChannel: NotificationChannel
private lateinit var sbn: StatusBarNotification
private lateinit var entry: NotificationEntry
+ private lateinit var entryAdapter: EntryAdapter
private val mockPackageManager = kosmos.mockPackageManager
+ private val mockAppIconProvider = kosmos.mockAppIconProvider
+ private val mockIconStyleProvider = kosmos.mockNotificationIconStyleProvider
private val uiEventLogger = kosmos.uiEventLoggerFake
private val testableLooper by lazy { kosmos.testableLooper }
@@ -186,7 +193,12 @@ class NotificationInfoTest : SysuiTestCase() {
null,
0,
)
- entry = NotificationEntryBuilder().setSbn(sbn).build()
+ entry =
+ NotificationEntryBuilder()
+ .setSbn(sbn)
+ .updateRanking { it.setChannel(notificationChannel) }
+ .build()
+ entryAdapter = kosmos.entryAdapterFactory.create(entry)
whenever(assistantFeedbackController.isFeedbackEnabled).thenReturn(false)
whenever(assistantFeedbackController.getInlineDescriptionResource(any()))
.thenReturn(R.string.notification_channel_summary_automatic)
@@ -202,7 +214,8 @@ class NotificationInfoTest : SysuiTestCase() {
}
@Test
- fun testBindNotification_SetsPackageIcon() {
+ @DisableFlags(com.android.systemui.Flags.FLAG_NOTIFICATIONS_REDESIGN_GUTS)
+ fun testBindNotification_SetsPackageIcon_flagOff() {
val iconDrawable = mock<Drawable>()
whenever(mockPackageManager.getApplicationIcon(any<ApplicationInfo>()))
.thenReturn(iconDrawable)
@@ -212,6 +225,26 @@ class NotificationInfoTest : SysuiTestCase() {
}
@Test
+ @EnableFlags(com.android.systemui.Flags.FLAG_NOTIFICATIONS_REDESIGN_GUTS)
+ fun testBindNotification_SetsPackageIcon_flagOn() {
+ val iconDrawable = mock<Drawable>()
+ whenever(mockIconStyleProvider.shouldShowWorkProfileBadge(anyOrNull(), anyOrNull()))
+ .thenReturn(false)
+ whenever(
+ mockAppIconProvider.getOrFetchAppIcon(
+ anyOrNull(),
+ anyOrNull(),
+ anyBoolean(),
+ anyBoolean(),
+ )
+ )
+ .thenReturn(iconDrawable)
+ bindNotification()
+ val iconView = underTest.findViewById<ImageView>(R.id.pkg_icon)
+ assertThat(iconView.drawable).isEqualTo(iconDrawable)
+ }
+
+ @Test
fun testBindNotification_noDelegate() {
bindNotification()
val nameView = underTest.findViewById<TextView>(R.id.delegate_name)
@@ -239,7 +272,7 @@ class NotificationInfoTest : SysuiTestCase() {
.thenReturn(applicationInfo)
whenever(mockPackageManager.getApplicationLabel(any())).thenReturn("Other")
- val entry = NotificationEntryBuilder().setSbn(sbn).build()
+ val entry = NotificationEntryBuilder(entry).setSbn(sbn).build()
bindNotification(entry = entry)
val nameView = underTest.findViewById<TextView>(R.id.delegate_name)
assertThat(nameView.visibility).isEqualTo(VISIBLE)
@@ -280,6 +313,10 @@ class NotificationInfoTest : SysuiTestCase() {
@Test
fun testBindNotification_DefaultChannelDoesNotUseChannelName() {
+ entry =
+ NotificationEntryBuilder(entry)
+ .updateRanking { it.setChannel(defaultNotificationChannel) }
+ .build()
bindNotification(notificationChannel = defaultNotificationChannel)
val textView = underTest.findViewById<TextView>(R.id.channel_name)
assertThat(textView.visibility).isEqualTo(GONE)
@@ -296,6 +333,10 @@ class NotificationInfoTest : SysuiTestCase() {
)
)
.thenReturn(10)
+ entry =
+ NotificationEntryBuilder(entry)
+ .updateRanking { it.setChannel(notificationChannel) }
+ .build()
bindNotification(notificationChannel = defaultNotificationChannel)
val textView = underTest.findViewById<TextView>(R.id.channel_name)
assertThat(textView.visibility).isEqualTo(VISIBLE)
@@ -723,7 +764,13 @@ class NotificationInfoTest : SysuiTestCase() {
underTest.findViewById<View>(R.id.done).performClick()
underTest.handleCloseControls(true, false)
- verify(onUserInteractionCallback).onImportanceChanged(entry)
+ if (NotificationBundleUi.isEnabled) {
+ verify(kosmos.mockVisualStabilityCoordinator)
+ .temporarilyAllowSectionChanges(eq(entry), any())
+ } else {
+ verify(onUserInteractionCallback).onImportanceChanged(entry)
+ }
+
assertThat(underTest.shouldBeSavedOnClose()).isFalse()
}
@@ -842,7 +889,12 @@ class NotificationInfoTest : SysuiTestCase() {
@EnableFlags(Flags.FLAG_NOTIFICATION_CLASSIFICATION_UI)
@Throws(RemoteException::class)
fun testBindNotification_SetsFeedbackLink_isReservedChannel() {
- entry.setRanking(RankingBuilder(entry.ranking).setSummarization("something").build())
+ entry.setRanking(
+ RankingBuilder(entry.ranking)
+ .setSummarization("something")
+ .setChannel(classifiedNotificationChannel)
+ .build()
+ )
val latch = CountDownLatch(1)
bindNotification(
notificationChannel = classifiedNotificationChannel,
@@ -894,8 +946,8 @@ class NotificationInfoTest : SysuiTestCase() {
private fun bindNotification(
pm: PackageManager = this.mockPackageManager,
iNotificationManager: INotificationManager = this.mockINotificationManager,
- appIconProvider: AppIconProvider = kosmos.appIconProvider,
- iconStyleProvider: NotificationIconStyleProvider = kosmos.notificationIconStyleProvider,
+ appIconProvider: AppIconProvider = this.mockAppIconProvider,
+ iconStyleProvider: NotificationIconStyleProvider = this.mockIconStyleProvider,
onUserInteractionCallback: OnUserInteractionCallback = this.onUserInteractionCallback,
channelEditorDialogController: ChannelEditorDialogController =
this.channelEditorDialogController,
@@ -903,6 +955,7 @@ class NotificationInfoTest : SysuiTestCase() {
pkg: String = TEST_PACKAGE_NAME,
notificationChannel: NotificationChannel = this.notificationChannel,
entry: NotificationEntry = this.entry,
+ entryAdapter: EntryAdapter = this.entryAdapter,
onSettingsClick: NotificationInfo.OnSettingsClickListener? = null,
onAppSettingsClick: NotificationInfo.OnAppSettingsClickListener? = null,
onFeedbackClickListener: NotificationInfo.OnFeedbackClickListener? = null,
@@ -924,8 +977,10 @@ class NotificationInfoTest : SysuiTestCase() {
channelEditorDialogController,
packageDemotionInteractor,
pkg,
- notificationChannel,
+ entry.ranking,
+ entry.sbn,
entry,
+ entryAdapter,
onSettingsClick,
onAppSettingsClick,
onFeedbackClickListener,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationMenuRowTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationMenuRowTest.java
index 9fdfca14a5b2..5ad4a4fab056 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationMenuRowTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationMenuRowTest.java
@@ -28,6 +28,7 @@ import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.platform.test.annotations.DisableFlags;
import android.provider.Settings;
import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
@@ -38,7 +39,10 @@ import android.view.ViewGroup;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
+import com.android.systemui.Flags;
+import com.android.systemui.kosmos.KosmosJavaAdapter;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
+import com.android.systemui.statusbar.notification.collection.EntryAdapter;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier;
@@ -54,6 +58,7 @@ import org.mockito.Mockito;
@SmallTest
public class NotificationMenuRowTest extends LeakCheckedTest {
+ private final KosmosJavaAdapter mKosmos = new KosmosJavaAdapter(this);
private ExpandableNotificationRow mRow;
private View mView;
private PeopleNotificationIdentifier mPeopleNotificationIdentifier;
@@ -66,6 +71,8 @@ public class NotificationMenuRowTest extends LeakCheckedTest {
mPeopleNotificationIdentifier = mock(PeopleNotificationIdentifier.class);
NotificationEntry entry = new NotificationEntryBuilder().build();
when(mRow.getEntry()).thenReturn(entry);
+ EntryAdapter entryAdapter = mKosmos.getEntryAdapterFactory().create(entry);
+ when(mRow.getEntryAdapter()).thenReturn(entryAdapter);
}
@Test
@@ -96,46 +103,6 @@ public class NotificationMenuRowTest extends LeakCheckedTest {
row.resetMenu();
}
-
- @Test
- public void testNoAppOpsInSlowSwipe() {
- when(mRow.getShowSnooze()).thenReturn(false);
- Settings.Global.putInt(mContext.getContentResolver(), SHOW_NEW_NOTIF_DISMISS, 0);
-
- NotificationMenuRow row = new NotificationMenuRow(mContext, mPeopleNotificationIdentifier);
- row.createMenu(mRow);
-
- ViewGroup container = (ViewGroup) row.getMenuView();
- // noti blocking
- assertEquals(1, container.getChildCount());
- }
-
- @Test
- public void testNoSnoozeInSlowSwipe() {
- when(mRow.getShowSnooze()).thenReturn(false);
- Settings.Global.putInt(mContext.getContentResolver(), SHOW_NEW_NOTIF_DISMISS, 0);
-
- NotificationMenuRow row = new NotificationMenuRow(mContext, mPeopleNotificationIdentifier);
- row.createMenu(mRow);
-
- ViewGroup container = (ViewGroup) row.getMenuView();
- // just for noti blocking
- assertEquals(1, container.getChildCount());
- }
-
- @Test
- public void testSnoozeInSlowSwipe() {
- when(mRow.getShowSnooze()).thenReturn(true);
- Settings.Global.putInt(mContext.getContentResolver(), SHOW_NEW_NOTIF_DISMISS, 0);
-
- NotificationMenuRow row = new NotificationMenuRow(mContext, mPeopleNotificationIdentifier);
- row.createMenu(mRow);
-
- ViewGroup container = (ViewGroup) row.getMenuView();
- // one for snooze and one for noti blocking
- assertEquals(2, container.getChildCount());
- }
-
@Test
public void testSlowSwipe_newDismiss() {
when(mRow.getShowSnooze()).thenReturn(true);
@@ -230,6 +197,7 @@ public class NotificationMenuRowTest extends LeakCheckedTest {
new NotificationMenuRow(mContext, mPeopleNotificationIdentifier));
doReturn(30f).when(row).getSnapBackThreshold();
doReturn(50f).when(row).getDismissThreshold();
+ doReturn(70).when(row).getSpaceForMenu();
when(row.isMenuOnLeft()).thenReturn(true);
when(row.getTranslation()).thenReturn(40f);
@@ -413,6 +381,7 @@ public class NotificationMenuRowTest extends LeakCheckedTest {
assertTrue("when alpha is .5, menu is visible", row.isMenuVisible());
}
+ @DisableFlags(Flags.FLAG_MAGNETIC_NOTIFICATION_SWIPES)
@Test
public void testOnTouchMove() {
NotificationMenuRow row = Mockito.spy(
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImplTest.kt
index 063a04ab9f37..dcba3e447dda 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImplTest.kt
@@ -44,7 +44,7 @@ import com.android.systemui.statusbar.notification.ConversationNotificationProce
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.promoted.FakePromotedNotificationContentExtractor
import com.android.systemui.statusbar.notification.promoted.PromotedNotificationUi
-import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel
+import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentBuilder
import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.BindParams
import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_ALL
import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_CONTRACTED
@@ -456,7 +456,7 @@ class NotificationRowContentBinderImplTest : SysuiTestCase() {
@Test
@DisableFlags(PromotedNotificationUi.FLAG_NAME, StatusBarNotifChips.FLAG_NAME)
fun testExtractsPromotedContent_notWhenBothFlagsDisabled() {
- val content = PromotedNotificationContentModel.Builder("key").build()
+ val content = PromotedNotificationContentBuilder("key").build()
promotedNotificationContentExtractor.resetForEntry(row.entry, content)
inflateAndWait(notificationInflater, FLAG_CONTENT_VIEW_ALL, row)
@@ -468,38 +468,38 @@ class NotificationRowContentBinderImplTest : SysuiTestCase() {
@EnableFlags(PromotedNotificationUi.FLAG_NAME)
@DisableFlags(StatusBarNotifChips.FLAG_NAME)
fun testExtractsPromotedContent_whenPromotedNotificationUiFlagEnabled() {
- val content = PromotedNotificationContentModel.Builder("key").build()
+ val content = PromotedNotificationContentBuilder("key").build()
promotedNotificationContentExtractor.resetForEntry(row.entry, content)
inflateAndWait(notificationInflater, FLAG_CONTENT_VIEW_ALL, row)
promotedNotificationContentExtractor.verifyOneExtractCall()
- Assert.assertEquals(content, row.entry.promotedNotificationContentModel)
+ Assert.assertEquals(content, row.entry.promotedNotificationContentModels)
}
@Test
@EnableFlags(StatusBarNotifChips.FLAG_NAME)
@DisableFlags(PromotedNotificationUi.FLAG_NAME)
fun testExtractsPromotedContent_whenStatusBarNotifChipsFlagEnabled() {
- val content = PromotedNotificationContentModel.Builder("key").build()
+ val content = PromotedNotificationContentBuilder("key").build()
promotedNotificationContentExtractor.resetForEntry(row.entry, content)
inflateAndWait(notificationInflater, FLAG_CONTENT_VIEW_ALL, row)
promotedNotificationContentExtractor.verifyOneExtractCall()
- Assert.assertEquals(content, row.entry.promotedNotificationContentModel)
+ Assert.assertEquals(content, row.entry.promotedNotificationContentModels)
}
@Test
@EnableFlags(PromotedNotificationUi.FLAG_NAME, StatusBarNotifChips.FLAG_NAME)
fun testExtractsPromotedContent_whenBothFlagsEnabled() {
- val content = PromotedNotificationContentModel.Builder("key").build()
+ val content = PromotedNotificationContentBuilder("key").build()
promotedNotificationContentExtractor.resetForEntry(row.entry, content)
inflateAndWait(notificationInflater, FLAG_CONTENT_VIEW_ALL, row)
promotedNotificationContentExtractor.verifyOneExtractCall()
- Assert.assertEquals(content, row.entry.promotedNotificationContentModel)
+ Assert.assertEquals(content, row.entry.promotedNotificationContentModels)
}
@Test
@@ -510,7 +510,7 @@ class NotificationRowContentBinderImplTest : SysuiTestCase() {
inflateAndWait(notificationInflater, FLAG_CONTENT_VIEW_ALL, row)
promotedNotificationContentExtractor.verifyOneExtractCall()
- Assert.assertNull(row.entry.promotedNotificationContentModel)
+ Assert.assertNull(row.entry.promotedNotificationContentModels)
}
@Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
index e6b2c2541447..bda2ba79d6b5 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
@@ -81,6 +81,7 @@ import com.android.systemui.statusbar.notification.collection.NotificationEntryB
import com.android.systemui.statusbar.notification.collection.coordinator.VisualStabilityCoordinator;
import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection;
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
+import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider;
import com.android.systemui.statusbar.notification.collection.provider.NotificationDismissibilityProvider;
import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManager;
import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager;
@@ -746,7 +747,9 @@ public class NotificationTestHelper {
mock(PeopleNotificationIdentifier.class),
mock(NotificationIconStyleProvider.class),
mock(VisualStabilityCoordinator.class),
- mock(NotificationActionClickManager.class)
+ mock(NotificationActionClickManager.class),
+ mock(HighPriorityProvider.class),
+ mock(HeadsUpManager.class)
).create(entry);
row.initialize(
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/PartialConversationInfoTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/PartialConversationInfoTest.java
index 57b0f3f8d8c5..0eb60163766c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/PartialConversationInfoTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/PartialConversationInfoTest.java
@@ -40,6 +40,7 @@ import android.content.pm.PackageManager;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.Icon;
import android.os.UserHandle;
+import android.service.notification.NotificationListenerService;
import android.service.notification.StatusBarNotification;
import android.testing.TestableLooper;
import android.text.SpannableString;
@@ -81,7 +82,6 @@ public class PartialConversationInfoTest extends SysuiTestCase {
private TestableLooper mTestableLooper;
private PartialConversationInfo mInfo;
private NotificationChannel mNotificationChannel;
- private NotificationChannel mDefaultNotificationChannel;
private StatusBarNotification mSbn;
private NotificationEntry mEntry;
@@ -141,15 +141,14 @@ public class PartialConversationInfoTest extends SysuiTestCase {
// Some test channels.
mNotificationChannel = new NotificationChannel(
TEST_CHANNEL, TEST_CHANNEL_NAME, IMPORTANCE_LOW);
- mDefaultNotificationChannel = new NotificationChannel(
- NotificationChannel.DEFAULT_CHANNEL_ID, TEST_CHANNEL_NAME,
- IMPORTANCE_LOW);
Notification n = new Notification.Builder(mContext, mNotificationChannel.getId())
.setContentTitle(new SpannableString("title"))
.build();
mSbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME, 0, null, TEST_UID, 0,
n, UserHandle.CURRENT, null, 0);
- mEntry = new NotificationEntryBuilder().setSbn(mSbn).build();
+ mEntry = new NotificationEntryBuilder().setSbn(mSbn).updateRanking(rankingBuilder -> {
+ rankingBuilder.setChannel(mNotificationChannel);
+ }).build();
}
@Test
@@ -160,8 +159,8 @@ public class PartialConversationInfoTest extends SysuiTestCase {
mMockINotificationManager,
mChannelEditorDialogController,
TEST_PACKAGE_NAME,
- mNotificationChannel,
- mEntry,
+ mEntry.getRanking(),
+ mSbn,
null,
true,
false);
@@ -178,8 +177,8 @@ public class PartialConversationInfoTest extends SysuiTestCase {
mMockINotificationManager,
mChannelEditorDialogController,
TEST_PACKAGE_NAME,
- mNotificationChannel,
- mEntry,
+ mEntry.getRanking(),
+ mSbn,
null,
true,
false);
@@ -194,8 +193,8 @@ public class PartialConversationInfoTest extends SysuiTestCase {
mMockINotificationManager,
mChannelEditorDialogController,
TEST_PACKAGE_NAME,
- mNotificationChannel,
- mEntry,
+ mEntry.getRanking(),
+ mSbn,
null,
true,
false);
@@ -219,8 +218,8 @@ public class PartialConversationInfoTest extends SysuiTestCase {
mMockINotificationManager,
mChannelEditorDialogController,
TEST_PACKAGE_NAME,
- mNotificationChannel,
- entry,
+ mEntry.getRanking(),
+ mSbn,
null,
true,
false);
@@ -237,8 +236,8 @@ public class PartialConversationInfoTest extends SysuiTestCase {
mMockINotificationManager,
mChannelEditorDialogController,
TEST_PACKAGE_NAME,
- mNotificationChannel,
- mEntry,
+ mEntry.getRanking(),
+ mSbn,
(View v, NotificationChannel c, int appUid) -> {
assertEquals(mNotificationChannel, c);
latch.countDown();
@@ -260,8 +259,8 @@ public class PartialConversationInfoTest extends SysuiTestCase {
mMockINotificationManager,
mChannelEditorDialogController,
TEST_PACKAGE_NAME,
- mNotificationChannel,
- mEntry,
+ mEntry.getRanking(),
+ mSbn,
(View v, NotificationChannel c, int appUid) -> {
assertEquals(mNotificationChannel, c);
latch.countDown();
@@ -282,8 +281,8 @@ public class PartialConversationInfoTest extends SysuiTestCase {
mMockINotificationManager,
mChannelEditorDialogController,
TEST_PACKAGE_NAME,
- mNotificationChannel,
- mEntry,
+ mEntry.getRanking(),
+ mSbn,
null,
true,
false);
@@ -298,8 +297,8 @@ public class PartialConversationInfoTest extends SysuiTestCase {
mMockINotificationManager,
mChannelEditorDialogController,
TEST_PACKAGE_NAME,
- mNotificationChannel,
- mEntry,
+ mEntry.getRanking(),
+ mSbn,
(View v, NotificationChannel c, int appUid) -> {
assertEquals(mNotificationChannel, c);
},
@@ -316,8 +315,8 @@ public class PartialConversationInfoTest extends SysuiTestCase {
mMockINotificationManager,
mChannelEditorDialogController,
TEST_PACKAGE_NAME,
- mNotificationChannel,
- mEntry,
+ mEntry.getRanking(),
+ mSbn,
null,
true,
true);
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/PromotedNotificationInfoTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/PromotedNotificationInfoTest.java
index 209dfb2d2ed6..9b86412fc051 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/PromotedNotificationInfoTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/PromotedNotificationInfoTest.java
@@ -31,6 +31,7 @@ import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.os.UserHandle;
import android.platform.test.annotations.EnableFlags;
+import android.service.notification.NotificationListenerService;
import android.service.notification.StatusBarNotification;
import android.telecom.TelecomManager;
import android.testing.TestableLooper;
@@ -46,9 +47,16 @@ import com.android.systemui.Dependency;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.res.R;
import com.android.systemui.statusbar.notification.AssistantFeedbackController;
+import com.android.systemui.statusbar.notification.NotificationActivityStarter;
+import com.android.systemui.statusbar.notification.collection.EntryAdapter;
+import com.android.systemui.statusbar.notification.collection.EntryAdapterFactoryImpl;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
+import com.android.systemui.statusbar.notification.headsup.HeadsUpManager;
import com.android.systemui.statusbar.notification.promoted.domain.interactor.PackageDemotionInteractor;
+import com.android.systemui.statusbar.notification.collection.coordinator.VisualStabilityCoordinator;
+import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider;
+import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier;
import com.android.systemui.statusbar.notification.row.icon.AppIconProvider;
import com.android.systemui.statusbar.notification.row.icon.NotificationIconStyleProvider;
@@ -74,6 +82,8 @@ public class PromotedNotificationInfoTest extends SysuiTestCase {
private NotificationChannel mNotificationChannel;
private StatusBarNotification mSbn;
private NotificationEntry mEntry;
+ private NotificationListenerService.Ranking mRanking;
+ private EntryAdapter mEntryAdapter;
private UiEventLoggerFake mUiEventLogger = new UiEventLoggerFake();
@Rule
@@ -110,7 +120,20 @@ public class PromotedNotificationInfoTest extends SysuiTestCase {
notification.extras.putParcelable(EXTRA_BUILDER_APPLICATION_INFO, applicationInfo);
mSbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME, 0, null, TEST_UID, 0,
notification, UserHandle.getUserHandleForUid(TEST_UID), null, 0);
- mEntry = new NotificationEntryBuilder().setSbn(mSbn).build();
+ mEntry = new NotificationEntryBuilder().setSbn(mSbn).updateRanking(rankingBuilder -> {
+ rankingBuilder.setChannel(mNotificationChannel);
+ }).build();
+ mEntryAdapter = new EntryAdapterFactoryImpl(
+ mock(NotificationActivityStarter.class),
+ mock(MetricsLogger.class),
+ mock(PeopleNotificationIdentifier.class),
+ mock(NotificationIconStyleProvider.class),
+ mock(VisualStabilityCoordinator.class),
+ mock(NotificationActionClickManager.class),
+ mock(HighPriorityProvider.class),
+ mock(HeadsUpManager.class)
+ ).create(mEntry);
+ mRanking = mEntry.getRanking();
when(mAssistantFeedbackController.isFeedbackEnabled()).thenReturn(false);
mTestableLooper = TestableLooper.get(this);
@@ -143,8 +166,10 @@ public class PromotedNotificationInfoTest extends SysuiTestCase {
mChannelEditorDialogController,
mPackageDemotionInteractor,
TEST_PACKAGE_NAME,
- mNotificationChannel,
+ mRanking,
+ mSbn,
mEntry,
+ mEntryAdapter,
null,
null,
null,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/MagneticNotificationRowManagerImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/MagneticNotificationRowManagerImplTest.kt
index ccc8be7de038..f52f96efb9d1 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/MagneticNotificationRowManagerImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/MagneticNotificationRowManagerImplTest.kt
@@ -130,14 +130,16 @@ class MagneticNotificationRowManagerImplTest : SysuiTestCase() {
kosmos.testScope.runTest {
// GIVEN a threshold of 100 px
val threshold = 100f
- underTest.setSwipeThresholdPx(threshold)
+ underTest.onDensityChange(
+ threshold / MagneticNotificationRowManager.MAGNETIC_DETACH_THRESHOLD_DP
+ )
// GIVEN that targets are set and the rows are being pulled
setTargets()
underTest.setMagneticRowTranslation(swipedRow, translation = 100f)
// WHEN setting a translation that will fall below the threshold
- val translation = threshold / underTest.swipedRowMultiplier - 50f
+ val translation = 50f
underTest.setMagneticRowTranslation(swipedRow, translation)
// THEN the targets continue to be pulled and translations are set
@@ -150,7 +152,9 @@ class MagneticNotificationRowManagerImplTest : SysuiTestCase() {
kosmos.testScope.runTest {
// GIVEN a threshold of 100 px
val threshold = 100f
- underTest.setSwipeThresholdPx(threshold)
+ underTest.onDensityChange(
+ threshold / MagneticNotificationRowManager.MAGNETIC_DETACH_THRESHOLD_DP
+ )
// GIVEN that targets are set and the rows are being pulled
canRowBeDismissed = false
@@ -158,7 +162,7 @@ class MagneticNotificationRowManagerImplTest : SysuiTestCase() {
underTest.setMagneticRowTranslation(swipedRow, translation = 100f)
// WHEN setting a translation that will fall below the threshold
- val translation = threshold / underTest.swipedRowMultiplier - 50f
+ val translation = 50f
underTest.setMagneticRowTranslation(swipedRow, translation)
// THEN the targets continue to be pulled and reduced translations are set
@@ -172,14 +176,16 @@ class MagneticNotificationRowManagerImplTest : SysuiTestCase() {
kosmos.testScope.runTest {
// GIVEN a threshold of 100 px
val threshold = 100f
- underTest.setSwipeThresholdPx(threshold)
+ underTest.onDensityChange(
+ threshold / MagneticNotificationRowManager.MAGNETIC_DETACH_THRESHOLD_DP
+ )
// GIVEN that targets are set and the rows are being pulled
setTargets()
underTest.setMagneticRowTranslation(swipedRow, translation = 100f)
// WHEN setting a translation that will fall above the threshold
- val translation = threshold / underTest.swipedRowMultiplier + 50f
+ val translation = 150f
underTest.setMagneticRowTranslation(swipedRow, translation)
// THEN the swiped view detaches and the correct detach haptics play
@@ -192,7 +198,9 @@ class MagneticNotificationRowManagerImplTest : SysuiTestCase() {
kosmos.testScope.runTest {
// GIVEN a threshold of 100 px
val threshold = 100f
- underTest.setSwipeThresholdPx(threshold)
+ underTest.onDensityChange(
+ threshold / MagneticNotificationRowManager.MAGNETIC_DETACH_THRESHOLD_DP
+ )
// GIVEN that targets are set and the rows are being pulled
canRowBeDismissed = false
@@ -200,7 +208,7 @@ class MagneticNotificationRowManagerImplTest : SysuiTestCase() {
underTest.setMagneticRowTranslation(swipedRow, translation = 100f)
// WHEN setting a translation that will fall above the threshold
- val translation = threshold / underTest.swipedRowMultiplier + 50f
+ val translation = 150f
underTest.setMagneticRowTranslation(swipedRow, translation)
// THEN the swiped view does not detach and the reduced translation is set
@@ -240,6 +248,19 @@ class MagneticNotificationRowManagerImplTest : SysuiTestCase() {
}
@Test
+ fun onMagneticInteractionEnd_whileTargetsSet_goesToIdle() =
+ kosmos.testScope.runTest {
+ // GIVEN that targets are set
+ setTargets()
+
+ // WHEN the interaction ends on the row
+ underTest.onMagneticInteractionEnd(swipedRow, velocity = null)
+
+ // THEN the state resets
+ assertThat(underTest.currentState).isEqualTo(State.IDLE)
+ }
+
+ @Test
fun onMagneticInteractionEnd_whileDetached_goesToIdle() =
kosmos.testScope.runTest {
// GIVEN the swiped row is detached
@@ -294,6 +315,29 @@ class MagneticNotificationRowManagerImplTest : SysuiTestCase() {
assertThat(underTest.isSwipedViewRoundableSet).isFalse()
}
+ @Test
+ fun isMagneticRowDismissible_isDismissibleWhenDetached() =
+ kosmos.testScope.runTest {
+ setDetachedState()
+
+ val isDismissible = underTest.isMagneticRowSwipeDetached(swipedRow)
+ assertThat(isDismissible).isTrue()
+ }
+
+ @Test
+ fun setMagneticRowTranslation_whenDetached_belowAttachThreshold_reattaches() =
+ kosmos.testScope.runTest {
+ // GIVEN that the swiped view has been detached
+ setDetachedState()
+
+ // WHEN setting a new translation above the attach threshold
+ val translation = 50f
+ underTest.setMagneticRowTranslation(swipedRow, translation)
+
+ // THEN the swiped view reattaches magnetically and the state becomes PULLING
+ assertThat(underTest.currentState).isEqualTo(State.PULLING)
+ }
+
@After
fun tearDown() {
// We reset the manager so that all MagneticRowListener can cancel all animations
@@ -302,14 +346,16 @@ class MagneticNotificationRowManagerImplTest : SysuiTestCase() {
private fun setDetachedState() {
val threshold = 100f
- underTest.setSwipeThresholdPx(threshold)
+ underTest.onDensityChange(
+ threshold / MagneticNotificationRowManager.MAGNETIC_DETACH_THRESHOLD_DP
+ )
// Set the pulling state
setTargets()
underTest.setMagneticRowTranslation(swipedRow, translation = 100f)
// Set a translation that will fall above the threshold
- val translation = threshold / underTest.swipedRowMultiplier + 50f
+ val translation = 150f
underTest.setMagneticRowTranslation(swipedRow, translation)
assertThat(underTest.currentState).isEqualTo(State.DETACHED)
@@ -327,8 +373,8 @@ class MagneticNotificationRowManagerImplTest : SysuiTestCase() {
private fun MagneticRowListener.asTestableListener(rowIndex: Int): MagneticRowListener {
val delegate = this
return object : MagneticRowListener {
- override fun setMagneticTranslation(translation: Float) {
- delegate.setMagneticTranslation(translation)
+ override fun setMagneticTranslation(translation: Float, trackEagerly: Boolean) {
+ delegate.setMagneticTranslation(translation, trackEagerly)
}
override fun triggerMagneticForce(
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelperTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelperTest.java
index 789701f5e4b0..de48f4018989 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelperTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelperTest.java
@@ -49,6 +49,7 @@ import android.view.ViewConfiguration;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
+import com.android.systemui.Flags;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.classifier.FalsingManagerFake;
import com.android.systemui.flags.FakeFeatureFlags;
@@ -362,6 +363,7 @@ public class NotificationSwipeHelperTest extends SysuiTestCase {
verify(mSwipeHelper, times(1)).isFalseGesture();
}
+ @DisableFlags(Flags.FLAG_MAGNETIC_NOTIFICATION_SWIPES)
@Test
public void testIsDismissGesture_farEnough() {
doReturn(false).when(mSwipeHelper).isFalseGesture();
@@ -374,6 +376,20 @@ public class NotificationSwipeHelperTest extends SysuiTestCase {
verify(mSwipeHelper, times(1)).isFalseGesture();
}
+ @EnableFlags(Flags.FLAG_MAGNETIC_NOTIFICATION_SWIPES)
+ @Test
+ public void testIsDismissGesture_magneticSwipeIsDismissible() {
+ doReturn(false).when(mSwipeHelper).isFalseGesture();
+ doReturn(false).when(mSwipeHelper).swipedFarEnough();
+ doReturn(false).when(mSwipeHelper).swipedFastEnough();
+ doReturn(true).when(mCallback).isMagneticViewDetached(any());
+ when(mCallback.canChildBeDismissedInDirection(any(), anyBoolean())).thenReturn(true);
+ when(mEvent.getActionMasked()).thenReturn(MotionEvent.ACTION_UP);
+
+ assertTrue("Should be a dismissal", mSwipeHelper.isDismissGesture(mEvent));
+ verify(mSwipeHelper, times(1)).isFalseGesture();
+ }
+
@Test
public void testIsDismissGesture_notFarOrFastEnough() {
doReturn(false).when(mSwipeHelper).isFalseGesture();
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt
index 41cca19346f0..6ec1f919fb47 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt
@@ -129,7 +129,7 @@ class StackScrollAlgorithmTest(flags: FlagsParameterization) : SysuiTestCase() {
whenever(notificationShelf.viewState).thenReturn(ExpandableViewState())
whenever(notificationRow.key).thenReturn("key")
whenever(notificationRow.viewState).thenReturn(ExpandableViewState())
- whenever(notificationRow.entry).thenReturn(notificationEntry)
+ whenever(notificationRow.entryLegacy).thenReturn(notificationEntry)
whenever(notificationRow.entryAdapter).thenReturn(notificationEntryAdapter)
whenever(notificationRow.roundableState)
.thenReturn(RoundableState(notificationRow, notificationRow, 0f))
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java
index 1ea41de63e64..716353945be2 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java
@@ -186,7 +186,8 @@ public class BiometricsUnlockControllerTest extends SysuiTestCase {
.thenReturn(false);
mBiometricUnlockController.onBiometricAuthenticated(UserHandle.USER_CURRENT,
BiometricSourceType.FINGERPRINT, true /* isStrongBiometric */);
- verify(mStatusBarKeyguardViewManager).showPrimaryBouncer(anyBoolean());
+ verify(mStatusBarKeyguardViewManager).showPrimaryBouncer(anyBoolean(),
+ eq("BiometricUnlockController#MODE_SHOW_BOUNCER"));
verify(mStatusBarKeyguardViewManager, never()).notifyKeyguardAuthenticated(anyBoolean());
assertThat(mBiometricUnlockController.getMode())
.isEqualTo(BiometricUnlockController.MODE_SHOW_BOUNCER);
@@ -198,7 +199,8 @@ public class BiometricsUnlockControllerTest extends SysuiTestCase {
.thenReturn(false);
mBiometricUnlockController.onBiometricAuthenticated(UserHandle.USER_CURRENT,
BiometricSourceType.FINGERPRINT, false /* isStrongBiometric */);
- verify(mStatusBarKeyguardViewManager).showPrimaryBouncer(anyBoolean());
+ verify(mStatusBarKeyguardViewManager).showPrimaryBouncer(anyBoolean(),
+ eq("BiometricUnlockController#MODE_SHOW_BOUNCER"));
assertThat(mBiometricUnlockController.getMode())
.isEqualTo(BiometricUnlockController.MODE_SHOW_BOUNCER);
assertThat(mBiometricUnlockController.getBiometricType())
@@ -248,7 +250,8 @@ public class BiometricsUnlockControllerTest extends SysuiTestCase {
mBiometricUnlockController.onBiometricAuthenticated(UserHandle.USER_CURRENT,
BiometricSourceType.FINGERPRINT, true /* isStrongBiometric */);
- verify(mStatusBarKeyguardViewManager, never()).showPrimaryBouncer(anyBoolean());
+ verify(mStatusBarKeyguardViewManager, never()).showPrimaryBouncer(anyBoolean(),
+ eq("BiometricUnlockController#MODE_SHOW_BOUNCER"));
verify(mStatusBarKeyguardViewManager).notifyKeyguardAuthenticated(eq(false));
assertThat(mBiometricUnlockController.getMode())
.isEqualTo(BiometricUnlockController.MODE_UNLOCK_COLLAPSING);
@@ -327,7 +330,8 @@ public class BiometricsUnlockControllerTest extends SysuiTestCase {
mBiometricUnlockController.onBiometricAuthenticated(UserHandle.USER_CURRENT,
BiometricSourceType.FACE, true /* isStrongBiometric */);
- verify(mStatusBarKeyguardViewManager).showPrimaryBouncer(anyBoolean());
+ verify(mStatusBarKeyguardViewManager).showPrimaryBouncer(anyBoolean(),
+ eq("BiometricUnlockController#MODE_SHOW_BOUNCER"));
assertThat(mBiometricUnlockController.getMode())
.isEqualTo(BiometricUnlockController.MODE_SHOW_BOUNCER);
}
@@ -359,7 +363,8 @@ public class BiometricsUnlockControllerTest extends SysuiTestCase {
mBiometricUnlockController.onBiometricAuthenticated(UserHandle.USER_CURRENT,
BiometricSourceType.FACE, true /* isStrongBiometric */);
- verify(mStatusBarKeyguardViewManager, never()).showPrimaryBouncer(anyBoolean());
+ verify(mStatusBarKeyguardViewManager, never()).showPrimaryBouncer(anyBoolean(),
+ eq("BiometricUnlockController#MODE_SHOW_BOUNCER"));
assertThat(mBiometricUnlockController.getMode())
.isEqualTo(BiometricUnlockController.MODE_NONE);
}
@@ -438,17 +443,20 @@ public class BiometricsUnlockControllerTest extends SysuiTestCase {
// WHEN udfps fails once - then don't show the bouncer yet
mBiometricUnlockController.onBiometricAuthFailed(BiometricSourceType.FINGERPRINT);
- verify(mStatusBarKeyguardViewManager, never()).showPrimaryBouncer(anyBoolean());
+ verify(mStatusBarKeyguardViewManager, never()).showPrimaryBouncer(anyBoolean(),
+ eq("BiometricUnlockController#MODE_SHOW_BOUNCER"));
// WHEN udfps fails the second time - then don't show the bouncer yet
mBiometricUnlockController.onBiometricAuthFailed(BiometricSourceType.FINGERPRINT);
- verify(mStatusBarKeyguardViewManager, never()).showPrimaryBouncer(anyBoolean());
+ verify(mStatusBarKeyguardViewManager, never()).showPrimaryBouncer(anyBoolean(),
+ eq("BiometricUnlockController#MODE_SHOW_BOUNCER"));
// WHEN udpfs fails the third time
mBiometricUnlockController.onBiometricAuthFailed(BiometricSourceType.FINGERPRINT);
// THEN show the bouncer
- verify(mStatusBarKeyguardViewManager).showPrimaryBouncer(true);
+ verify(mStatusBarKeyguardViewManager).showPrimaryBouncer(true,
+ "BiometricUnlockController#MODE_SHOW_BOUNCER");
}
@Test
@@ -460,14 +468,16 @@ public class BiometricsUnlockControllerTest extends SysuiTestCase {
mBiometricUnlockController.onBiometricAuthFailed(BiometricSourceType.FINGERPRINT);
mBiometricUnlockController.onBiometricAuthFailed(BiometricSourceType.FINGERPRINT);
mBiometricUnlockController.onBiometricAuthFailed(BiometricSourceType.FINGERPRINT);
- verify(mStatusBarKeyguardViewManager, never()).showPrimaryBouncer(anyBoolean());
+ verify(mStatusBarKeyguardViewManager, never()).showPrimaryBouncer(anyBoolean(),
+ eq("BiometricUnlockController#MODE_SHOW_BOUNCER"));
// WHEN lockout is received
mBiometricUnlockController.onBiometricError(FingerprintManager.FINGERPRINT_ERROR_LOCKOUT,
"Lockout", BiometricSourceType.FINGERPRINT);
// THEN show bouncer
- verify(mStatusBarKeyguardViewManager).showPrimaryBouncer(true);
+ verify(mStatusBarKeyguardViewManager).showPrimaryBouncer(true,
+ "BiometricUnlockController#MODE_SHOW_BOUNCER");
}
@Test
@@ -544,7 +554,8 @@ public class BiometricsUnlockControllerTest extends SysuiTestCase {
BiometricSourceType.FINGERPRINT, true /* isStrongBiometric */);
// THEN shows primary bouncer
- verify(mStatusBarKeyguardViewManager).showPrimaryBouncer(anyBoolean());
+ verify(mStatusBarKeyguardViewManager).showPrimaryBouncer(anyBoolean(),
+ eq("BiometricUnlockController#MODE_SHOW_BOUNCER"));
}
@Test
@@ -554,7 +565,8 @@ public class BiometricsUnlockControllerTest extends SysuiTestCase {
BiometricSourceType.FACE, false /* isStrongBiometric */);
// THEN shows primary bouncer
- verify(mStatusBarKeyguardViewManager).showPrimaryBouncer(anyBoolean());
+ verify(mStatusBarKeyguardViewManager).showPrimaryBouncer(anyBoolean(),
+ eq("BiometricUnlockController#MODE_SHOW_BOUNCER"));
}
private void givenFingerprintModeUnlockCollapsing() {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.kt
index 46430afecbb1..1f37291efbab 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.kt
@@ -790,6 +790,7 @@ class KeyguardStatusBarViewControllerTest : SysuiTestCase() {
}
@Test
+ @DisableFlags(Flags.FLAG_GLANCEABLE_HUB_V2)
fun animateToGlanceableHub_affectsAlpha() =
testScope.runTest {
try {
@@ -809,6 +810,7 @@ class KeyguardStatusBarViewControllerTest : SysuiTestCase() {
}
@Test
+ @DisableFlags(Flags.FLAG_GLANCEABLE_HUB_V2)
fun animateToGlanceableHub_alphaResetOnCommunalNotShowing() =
testScope.runTest {
try {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallbackTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallbackTest.java
index c23e0e733b41..d9e256228428 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallbackTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallbackTest.java
@@ -124,7 +124,8 @@ public class StatusBarRemoteInputCallbackTest extends SysuiTestCase {
mRemoteInputCallback.onLockedRemoteInput(
mock(ExpandableNotificationRow.class), mock(View.class));
- verify(mStatusBarKeyguardViewManager).showBouncer(true);
+ verify(mStatusBarKeyguardViewManager).showBouncer(true,
+ "StatusBarRemoteInputCallback#onLockedRemoteInput");
}
@Test
@DisableFlags(ExpandHeadsUpOnInlineReply.FLAG_NAME)
@@ -221,6 +222,7 @@ public class StatusBarRemoteInputCallbackTest extends SysuiTestCase {
when(enr.getPrivateLayout()).thenReturn(privateLayout);
when(enr.getEntry()).thenReturn(enrEntry);
+ when(enr.getEntryLegacy()).thenReturn(enrEntry);
when(enr.getEntryAdapter()).thenReturn(enrEntryAdapter);
when(enr.isChildInGroup()).thenReturn(true);
when(enr.areChildrenExpanded()).thenReturn(false);
@@ -251,6 +253,7 @@ public class StatusBarRemoteInputCallbackTest extends SysuiTestCase {
when(enr.getPrivateLayout()).thenReturn(privateLayout);
when(enr.getEntry()).thenReturn(enrEntry);
+ when(enr.getEntryLegacy()).thenReturn(enrEntry);
when(enr.isChildInGroup()).thenReturn(true);
when(enr.areChildrenExpanded()).thenReturn(true);
@@ -277,6 +280,7 @@ public class StatusBarRemoteInputCallbackTest extends SysuiTestCase {
when(enr.getPrivateLayout()).thenReturn(privateLayout);
when(enr.getEntry()).thenReturn(enrEntry);
+ when(enr.getEntryLegacy()).thenReturn(enrEntry);
when(enr.isChildInGroup()).thenReturn(false);
when(enr.isPinned()).thenReturn(false);
when(enr.isExpanded()).thenReturn(false);
@@ -305,6 +309,7 @@ public class StatusBarRemoteInputCallbackTest extends SysuiTestCase {
when(enr.getPrivateLayout()).thenReturn(privateLayout);
when(enr.getEntry()).thenReturn(enrEntry);
+ when(enr.getEntryLegacy()).thenReturn(enrEntry);
when(enr.isChildInGroup()).thenReturn(false);
when(enr.isPinned()).thenReturn(false);
when(enr.isExpanded()).thenReturn(true);
@@ -333,6 +338,7 @@ public class StatusBarRemoteInputCallbackTest extends SysuiTestCase {
when(enr.getPrivateLayout()).thenReturn(privateLayout);
when(enr.getEntry()).thenReturn(enrEntry);
+ when(enr.getEntryLegacy()).thenReturn(enrEntry);
when(enr.isChildInGroup()).thenReturn(false);
when(enr.isPinned()).thenReturn(true);
when(enr.isPinnedAndExpanded()).thenReturn(false);
@@ -361,6 +367,7 @@ public class StatusBarRemoteInputCallbackTest extends SysuiTestCase {
when(enr.getPrivateLayout()).thenReturn(privateLayout);
when(enr.getEntry()).thenReturn(enrEntry);
+ when(enr.getEntryLegacy()).thenReturn(enrEntry);
when(enr.isChildInGroup()).thenReturn(false);
when(enr.isPinned()).thenReturn(true);
when(enr.isPinnedAndExpanded()).thenReturn(true);
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt
index c58b4bc9953c..18074d53e87b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt
@@ -40,7 +40,7 @@ import com.android.systemui.statusbar.notification.data.model.activeNotification
import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationsStore
import com.android.systemui.statusbar.notification.data.repository.activeNotificationListRepository
import com.android.systemui.statusbar.notification.domain.interactor.activeNotificationsInteractor
-import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel
+import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentBuilder
import com.android.systemui.statusbar.notification.shared.ActiveNotificationModel
import com.android.systemui.statusbar.notification.shared.CallType
import com.android.systemui.statusbar.phone.ongoingcall.data.repository.ongoingCallRepository
@@ -170,7 +170,7 @@ class OngoingCallControllerTest : SysuiTestCase() {
@Test
fun interactorHasOngoingCallNotif_repoHasPromotedContent() =
testScope.runTest {
- val promotedContent = PromotedNotificationContentModel.Builder("ongoingNotif").build()
+ val promotedContent = PromotedNotificationContentBuilder("ongoingNotif").build()
setNotifOnRepo(
activeNotificationModel(
key = "ongoingNotif",
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/domain/interactor/OngoingCallInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/domain/interactor/OngoingCallInteractorTest.kt
index 84f1d5cd4895..c071327ae398 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/domain/interactor/OngoingCallInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/domain/interactor/OngoingCallInteractorTest.kt
@@ -29,7 +29,7 @@ import com.android.systemui.kosmos.useUnconfinedTestDispatcher
import com.android.systemui.statusbar.StatusBarIconView
import com.android.systemui.statusbar.data.repository.fakeStatusBarModeRepository
import com.android.systemui.statusbar.gesture.swipeStatusBarAwayGestureHandler
-import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel
+import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentBuilder
import com.android.systemui.statusbar.phone.ongoingcall.EnableChipsModernization
import com.android.systemui.statusbar.phone.ongoingcall.shared.model.OngoingCallModel
import com.android.systemui.statusbar.phone.ongoingcall.shared.model.OngoingCallTestHelper.addOngoingCallState
@@ -75,7 +75,7 @@ class OngoingCallInteractorTest : SysuiTestCase() {
val startTimeMs = 1000L
val testIconView: StatusBarIconView = mock()
val testIntent: PendingIntent = mock()
- val testPromotedContent = PromotedNotificationContentModel.Builder(key).build()
+ val testPromotedContent = PromotedNotificationContentBuilder(key).build()
addOngoingCallState(
key = key,
startTimeMs = startTimeMs,
@@ -106,7 +106,7 @@ class OngoingCallInteractorTest : SysuiTestCase() {
val startTimeMs = 1000L
val testIconView: StatusBarIconView = mock()
val testIntent: PendingIntent = mock()
- val testPromotedContent = PromotedNotificationContentModel.Builder(key).build()
+ val testPromotedContent = PromotedNotificationContentBuilder(key).build()
addOngoingCallState(
key = key,
startTimeMs = startTimeMs,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ui/IconManagerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ui/IconManagerTest.kt
index 90732d0183d2..318eb87f500b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ui/IconManagerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ui/IconManagerTest.kt
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+@file:OptIn(ExperimentalKairosApi::class)
+
package com.android.systemui.statusbar.phone.ui
import android.app.Flags
@@ -28,13 +30,17 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.internal.statusbar.StatusBarIcon
import com.android.systemui.SysuiTestCase
+import com.android.systemui.kairos.ExperimentalKairosApi
+import com.android.systemui.kairos.KairosNetwork
import com.android.systemui.statusbar.StatusBarIconView
import com.android.systemui.statusbar.connectivity.ui.MobileContextProvider
import com.android.systemui.statusbar.phone.StatusBarLocation
import com.android.systemui.statusbar.pipeline.mobile.ui.MobileUiAdapter
+import com.android.systemui.statusbar.pipeline.mobile.ui.MobileUiAdapterKairos
import com.android.systemui.statusbar.pipeline.wifi.ui.WifiUiAdapter
import com.android.systemui.util.Assert
import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.CoroutineScope
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -58,7 +64,10 @@ class IconManagerTest : SysuiTestCase() {
StatusBarLocation.HOME,
mock<WifiUiAdapter>(defaultAnswer = RETURNS_DEEP_STUBS),
mock<MobileUiAdapter>(defaultAnswer = RETURNS_DEEP_STUBS),
+ { mock<MobileUiAdapterKairos>(defaultAnswer = RETURNS_DEEP_STUBS) },
mock<MobileContextProvider>(defaultAnswer = RETURNS_DEEP_STUBS),
+ mock<KairosNetwork>(defaultAnswer = RETURNS_DEEP_STUBS),
+ mock<CoroutineScope>(defaultAnswer = RETURNS_DEEP_STUBS),
)
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ui/StatusBarIconControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ui/StatusBarIconControllerTest.java
index 891ff38764fe..8e3117f47f86 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ui/StatusBarIconControllerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ui/StatusBarIconControllerTest.java
@@ -35,6 +35,7 @@ import androidx.test.filters.SmallTest;
import com.android.internal.statusbar.StatusBarIcon;
import com.android.systemui.demomode.DemoModeController;
import com.android.systemui.dump.DumpManager;
+import com.android.systemui.kairos.KairosNetwork;
import com.android.systemui.plugins.DarkIconDispatcher;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.StatusBarIconView;
@@ -45,12 +46,15 @@ import com.android.systemui.statusbar.phone.StatusBarLocation;
import com.android.systemui.statusbar.pipeline.StatusBarPipelineFlags;
import com.android.systemui.statusbar.pipeline.icons.shared.BindableIconsRegistry;
import com.android.systemui.statusbar.pipeline.mobile.ui.MobileUiAdapter;
+import com.android.systemui.statusbar.pipeline.mobile.ui.MobileUiAdapterKairos;
import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.MobileIconsViewModel;
import com.android.systemui.statusbar.pipeline.wifi.ui.WifiUiAdapter;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.tuner.TunerService;
import com.android.systemui.utils.leaks.LeakCheckedTest;
+import kotlinx.coroutines.CoroutineScope;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -76,7 +80,9 @@ public class StatusBarIconControllerTest extends LeakCheckedTest {
public void testSetCalledOnAdd_IconManager() {
LinearLayout layout = new LinearLayout(mContext);
TestIconManager manager =
- new TestIconManager(layout, mMobileUiAdapter, mMobileContextProvider);
+ new TestIconManager(layout, mMobileUiAdapter, mMobileContextProvider,
+ mock(MobileUiAdapterKairos.class), mock(
+ KairosNetwork.class), mock(CoroutineScope.class));
testCallOnAdd_forManager(manager);
}
@@ -89,7 +95,9 @@ public class StatusBarIconControllerTest extends LeakCheckedTest {
mock(WifiUiAdapter.class),
mMobileUiAdapter,
mMobileContextProvider,
- mock(DarkIconDispatcher.class));
+ mock(DarkIconDispatcher.class),
+ mock(MobileUiAdapterKairos.class), mock(KairosNetwork.class),
+ mock(CoroutineScope.class));
testCallOnAdd_forManager(manager);
}
@@ -139,12 +147,18 @@ public class StatusBarIconControllerTest extends LeakCheckedTest {
WifiUiAdapter wifiUiAdapter,
MobileUiAdapter mobileUiAdapter,
MobileContextProvider contextProvider,
- DarkIconDispatcher darkIconDispatcher) {
+ DarkIconDispatcher darkIconDispatcher,
+ MobileUiAdapterKairos mobileUiAdapterKairos,
+ KairosNetwork kairosNetwork,
+ CoroutineScope appScope) {
super(group,
location,
wifiUiAdapter,
mobileUiAdapter,
+ () -> mobileUiAdapterKairos,
contextProvider,
+ kairosNetwork,
+ appScope,
darkIconDispatcher);
}
@@ -167,13 +181,19 @@ public class StatusBarIconControllerTest extends LeakCheckedTest {
TestIconManager(
ViewGroup group,
MobileUiAdapter adapter,
- MobileContextProvider contextProvider
+ MobileContextProvider contextProvider,
+ MobileUiAdapterKairos adapterKairos,
+ KairosNetwork kairosNetwork,
+ CoroutineScope appScope
) {
super(group,
StatusBarLocation.HOME,
mock(WifiUiAdapter.class),
adapter,
- contextProvider);
+ () -> adapterKairos,
+ contextProvider,
+ kairosNetwork,
+ appScope);
}
@Override
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/CarrierMergedConnectionRepositoryKairosAdapterTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/CarrierMergedConnectionRepositoryKairosAdapterTest.kt
new file mode 100644
index 000000000000..3cf787d50d2f
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/CarrierMergedConnectionRepositoryKairosAdapterTest.kt
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2025 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.pipeline.mobile.data.repository.prod
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.activated
+import com.android.systemui.kairos.ExperimentalKairosApi
+import com.android.systemui.kairos.launchKairosNetwork
+import com.android.systemui.kairos.stateOf
+import com.android.systemui.statusbar.pipeline.mobile.data.model.SystemUiCarrierConfig
+import com.android.systemui.statusbar.pipeline.mobile.data.model.testCarrierConfig
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.test.runCurrent
+import org.junit.runner.RunWith
+import org.mockito.Mockito
+
+@OptIn(ExperimentalKairosApi::class, ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class CarrierMergedConnectionRepositoryKairosAdapterTest :
+ CarrierMergedConnectionRepositoryTestBase() {
+
+ var job: Job? = null
+ val kairosNetwork = testScope.backgroundScope.launchKairosNetwork()
+
+ override fun recreateRepo(): MobileConnectionRepositoryKairosAdapter {
+ lateinit var adapter: MobileConnectionRepositoryKairosAdapter
+ job?.cancel()
+ Mockito.clearInvocations(telephonyManager)
+ job =
+ testScope.backgroundScope.launch {
+ kairosNetwork.activateSpec {
+ val repo = activated {
+ CarrierMergedConnectionRepositoryKairos(
+ SUB_ID,
+ logger,
+ telephonyManager,
+ wifiRepository,
+ isInEcmMode = stateOf(false),
+ )
+ }
+ adapter =
+ MobileConnectionRepositoryKairosAdapter(
+ repo,
+ SystemUiCarrierConfig(SUB_ID, testCarrierConfig()),
+ )
+ Unit
+ }
+ }
+ testScope.runCurrent() // ensure the lateinit is set
+ return adapter
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/CarrierMergedConnectionRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/CarrierMergedConnectionRepositoryTest.kt
index 8e55f2e9a31a..8a6829cf5d21 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/CarrierMergedConnectionRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/CarrierMergedConnectionRepositoryTest.kt
@@ -25,6 +25,7 @@ import com.android.systemui.log.table.TableLogBuffer
import com.android.systemui.statusbar.pipeline.mobile.data.model.DataConnectionState
import com.android.systemui.statusbar.pipeline.mobile.data.model.NetworkNameModel
import com.android.systemui.statusbar.pipeline.mobile.data.model.ResolvedNetworkType
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionRepository
import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityModel
import com.android.systemui.statusbar.pipeline.wifi.data.repository.FakeWifiRepository
import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel
@@ -43,16 +44,30 @@ import org.mockito.MockitoAnnotations
@SmallTest
@RunWith(AndroidJUnit4::class)
-class CarrierMergedConnectionRepositoryTest : SysuiTestCase() {
+class CarrierMergedConnectionRepositoryTest : CarrierMergedConnectionRepositoryTestBase() {
+ override fun recreateRepo() =
+ CarrierMergedConnectionRepository(
+ SUB_ID,
+ logger,
+ telephonyManager,
+ testScope.backgroundScope.coroutineContext,
+ testScope.backgroundScope,
+ wifiRepository,
+ )
+}
+
+abstract class CarrierMergedConnectionRepositoryTestBase : SysuiTestCase() {
- private lateinit var underTest: CarrierMergedConnectionRepository
+ protected lateinit var underTest: MobileConnectionRepository
- private lateinit var wifiRepository: FakeWifiRepository
- @Mock private lateinit var logger: TableLogBuffer
- @Mock private lateinit var telephonyManager: TelephonyManager
+ protected lateinit var wifiRepository: FakeWifiRepository
+ @Mock protected lateinit var logger: TableLogBuffer
+ @Mock protected lateinit var telephonyManager: TelephonyManager
- private val testDispatcher = UnconfinedTestDispatcher()
- private val testScope = TestScope(testDispatcher)
+ protected val testDispatcher = UnconfinedTestDispatcher()
+ protected val testScope = TestScope(testDispatcher)
+
+ abstract fun recreateRepo(): MobileConnectionRepository
@Before
fun setUp() {
@@ -62,15 +77,7 @@ class CarrierMergedConnectionRepositoryTest : SysuiTestCase() {
wifiRepository = FakeWifiRepository()
- underTest =
- CarrierMergedConnectionRepository(
- SUB_ID,
- logger,
- telephonyManager,
- testScope.backgroundScope.coroutineContext,
- testScope.backgroundScope,
- wifiRepository,
- )
+ underTest = recreateRepo()
}
@Test
@@ -121,10 +128,7 @@ class CarrierMergedConnectionRepositoryTest : SysuiTestCase() {
wifiRepository.setIsWifiDefault(true)
wifiRepository.setWifiNetwork(
- WifiNetworkModel.CarrierMerged.of(
- subscriptionId = SUB_ID,
- level = 3,
- )
+ WifiNetworkModel.CarrierMerged.of(subscriptionId = SUB_ID, level = 3)
)
assertThat(latest).isEqualTo(3)
@@ -141,26 +145,17 @@ class CarrierMergedConnectionRepositoryTest : SysuiTestCase() {
wifiRepository.setIsWifiEnabled(true)
wifiRepository.setIsWifiDefault(true)
wifiRepository.setWifiNetwork(
- WifiNetworkModel.CarrierMerged.of(
- subscriptionId = SUB_ID,
- level = 3,
- )
+ WifiNetworkModel.CarrierMerged.of(subscriptionId = SUB_ID, level = 3)
)
wifiRepository.setWifiActivity(
- DataActivityModel(
- hasActivityIn = true,
- hasActivityOut = false,
- )
+ DataActivityModel(hasActivityIn = true, hasActivityOut = false)
)
assertThat(latest!!.hasActivityIn).isTrue()
assertThat(latest!!.hasActivityOut).isFalse()
wifiRepository.setWifiActivity(
- DataActivityModel(
- hasActivityIn = false,
- hasActivityOut = true,
- )
+ DataActivityModel(hasActivityIn = false, hasActivityOut = true)
)
assertThat(latest!!.hasActivityIn).isFalse()
@@ -178,10 +173,7 @@ class CarrierMergedConnectionRepositoryTest : SysuiTestCase() {
val typeJob = underTest.resolvedNetworkType.onEach { latestType = it }.launchIn(this)
wifiRepository.setWifiNetwork(
- WifiNetworkModel.CarrierMerged.of(
- subscriptionId = SUB_ID + 10,
- level = 3,
- )
+ WifiNetworkModel.CarrierMerged.of(subscriptionId = SUB_ID + 10, level = 3)
)
assertThat(latestLevel).isNotEqualTo(3)
@@ -199,10 +191,7 @@ class CarrierMergedConnectionRepositoryTest : SysuiTestCase() {
val job = underTest.primaryLevel.onEach { latest = it }.launchIn(this)
wifiRepository.setWifiNetwork(
- WifiNetworkModel.CarrierMerged.of(
- subscriptionId = SUB_ID,
- level = 3,
- )
+ WifiNetworkModel.CarrierMerged.of(subscriptionId = SUB_ID, level = 3)
)
wifiRepository.setIsWifiEnabled(false)
@@ -219,10 +208,7 @@ class CarrierMergedConnectionRepositoryTest : SysuiTestCase() {
val job = underTest.primaryLevel.onEach { latest = it }.launchIn(this)
wifiRepository.setWifiNetwork(
- WifiNetworkModel.CarrierMerged.of(
- subscriptionId = SUB_ID,
- level = 3,
- )
+ WifiNetworkModel.CarrierMerged.of(subscriptionId = SUB_ID, level = 3)
)
wifiRepository.setIsWifiDefault(false)
@@ -280,6 +266,7 @@ class CarrierMergedConnectionRepositoryTest : SysuiTestCase() {
fun networkName_usesSimOperatorNameAsInitial() =
testScope.runTest {
whenever(telephonyManager.simOperatorName).thenReturn("Test SIM name")
+ underTest = recreateRepo()
var latest: NetworkNameModel? = null
val job = underTest.networkName.onEach { latest = it }.launchIn(this)
@@ -293,6 +280,10 @@ class CarrierMergedConnectionRepositoryTest : SysuiTestCase() {
fun networkName_updatesOnNetworkUpdate() =
testScope.runTest {
whenever(telephonyManager.simOperatorName).thenReturn("Test SIM name")
+ underTest = recreateRepo()
+
+ wifiRepository.setIsWifiEnabled(true)
+ wifiRepository.setIsWifiDefault(true)
var latest: NetworkNameModel? = null
val job = underTest.networkName.onEach { latest = it }.launchIn(this)
@@ -301,10 +292,7 @@ class CarrierMergedConnectionRepositoryTest : SysuiTestCase() {
whenever(telephonyManager.simOperatorName).thenReturn("New SIM name")
wifiRepository.setWifiNetwork(
- WifiNetworkModel.CarrierMerged.of(
- subscriptionId = SUB_ID,
- level = 3,
- )
+ WifiNetworkModel.CarrierMerged.of(subscriptionId = SUB_ID, level = 3)
)
assertThat(latest).isEqualTo(NetworkNameModel.SimDerived("New SIM name"))
@@ -320,7 +308,7 @@ class CarrierMergedConnectionRepositoryTest : SysuiTestCase() {
assertThat(latest).isTrue()
}
- private companion object {
+ companion object {
const val SUB_ID = 123
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractorKairosAdapterTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractorKairosAdapterTest.kt
new file mode 100644
index 000000000000..e72d0c27e632
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractorKairosAdapterTest.kt
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2025 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.pipeline.mobile.domain.interactor
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.settingslib.SignalIcon
+import com.android.settingslib.mobile.MobileIconCarrierIdOverrides
+import com.android.systemui.activated
+import com.android.systemui.kairos.BuildScope
+import com.android.systemui.kairos.ExperimentalKairosApi
+import com.android.systemui.kairos.Incremental
+import com.android.systemui.kairos.State
+import com.android.systemui.kairos.asIncremental
+import com.android.systemui.kairos.buildSpec
+import com.android.systemui.kairos.combine
+import com.android.systemui.kairos.launchKairosNetwork
+import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel
+import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconsInteractorKairosAdapterTest.Companion.wrapRepo
+import com.android.systemui.util.mockito.mock
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.test.runCurrent
+import org.junit.runner.RunWith
+
+@OptIn(ExperimentalKairosApi::class, ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class MobileIconInteractorKairosAdapterTest : MobileIconInteractorTestBase() {
+
+ var job: Job? = null
+ val kairosNetwork = testScope.backgroundScope.launchKairosNetwork()
+
+ override fun createInteractor(overrides: MobileIconCarrierIdOverrides): MobileIconInteractor {
+ lateinit var result: MobileIconInteractor
+ job?.cancel()
+ job =
+ testScope.backgroundScope.launch {
+ kairosNetwork.activateSpec {
+ val wrapped = wrap(mobileIconsInteractor)
+ result =
+ MobileIconInteractorKairosAdapter(
+ kairosImpl =
+ activated {
+ MobileIconInteractorKairosImpl(
+ defaultSubscriptionHasDataEnabled =
+ wrapped.activeDataConnectionHasDataEnabled,
+ alwaysShowDataRatIcon = wrapped.alwaysShowDataRatIcon,
+ alwaysUseCdmaLevel = wrapped.alwaysUseCdmaLevel,
+ isSingleCarrier = wrapped.isSingleCarrier,
+ mobileIsDefault = wrapped.mobileIsDefault,
+ defaultMobileIconMapping = wrapped.defaultMobileIconMapping,
+ defaultMobileIconGroup = wrapped.defaultMobileIconGroup,
+ isDefaultConnectionFailed =
+ wrapped.isDefaultConnectionFailed,
+ isForceHidden = wrapped.isForceHidden,
+ connectionRepository = wrapRepo(connectionRepository),
+ context = context,
+ carrierIdOverrides = overrides,
+ )
+ }
+ )
+ Unit
+ }
+ }
+ testScope.runCurrent() // ensure the lateinit is set
+ return result
+ }
+
+ /** Allows us to wrap a (likely fake) MobileIconsInteractor into a Kairos version. */
+ private fun BuildScope.wrap(interactor: MobileIconsInteractor): MobileIconsInteractorKairos {
+ val filteredSubscriptions = interactor.filteredSubscriptions.toState(emptyList())
+ val icons = interactor.icons.toState()
+ return InteractorWrapper(
+ mobileIsDefault = interactor.mobileIsDefault.toState(),
+ filteredSubscriptions = filteredSubscriptions,
+ icons =
+ combine(filteredSubscriptions, icons) { subs, icons ->
+ subs.zip(icons).associate { (subModel, icon) ->
+ subModel.subscriptionId to buildSpec { wrap(icon) }
+ }
+ }
+ .asIncremental()
+ .applyLatestSpecForKey(),
+ isStackable = interactor.isStackable.toState(),
+ activeDataConnectionHasDataEnabled =
+ interactor.activeDataConnectionHasDataEnabled.toState(),
+ activeDataIconInteractor =
+ interactor.activeDataIconInteractor.toState().mapLatestBuild {
+ it?.let { wrap(it) }
+ },
+ alwaysShowDataRatIcon = interactor.alwaysShowDataRatIcon.toState(),
+ alwaysUseCdmaLevel = interactor.alwaysUseCdmaLevel.toState(),
+ isSingleCarrier = interactor.isSingleCarrier.toState(),
+ defaultMobileIconMapping = interactor.defaultMobileIconMapping.toState(),
+ defaultMobileIconGroup = interactor.defaultMobileIconGroup.toState(),
+ isDefaultConnectionFailed = interactor.isDefaultConnectionFailed.toState(),
+ isUserSetUp = interactor.isUserSetUp.toState(),
+ isForceHidden = interactor.isForceHidden.toState(false),
+ isDeviceInEmergencyCallsOnlyMode =
+ interactor.isDeviceInEmergencyCallsOnlyMode.toState(false),
+ )
+ }
+
+ private fun BuildScope.wrap(interactor: MobileIconInteractor): MobileIconInteractorKairos =
+ // unused in tests
+ mock()
+
+ private class InteractorWrapper(
+ override val mobileIsDefault: State<Boolean>,
+ override val filteredSubscriptions: State<List<SubscriptionModel>>,
+ override val icons: Incremental<Int, MobileIconInteractorKairos>,
+ override val isStackable: State<Boolean>,
+ override val activeDataConnectionHasDataEnabled: State<Boolean>,
+ override val activeDataIconInteractor: State<MobileIconInteractorKairos?>,
+ override val alwaysShowDataRatIcon: State<Boolean>,
+ override val alwaysUseCdmaLevel: State<Boolean>,
+ override val isSingleCarrier: State<Boolean>,
+ override val defaultMobileIconMapping: State<Map<String, SignalIcon.MobileIconGroup>>,
+ override val defaultMobileIconGroup: State<SignalIcon.MobileIconGroup>,
+ override val isDefaultConnectionFailed: State<Boolean>,
+ override val isUserSetUp: State<Boolean>,
+ override val isForceHidden: State<Boolean>,
+ override val isDeviceInEmergencyCallsOnlyMode: State<Boolean>,
+ ) : MobileIconsInteractorKairos
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractorTest.kt
index 8c70da718c08..974a47587c77 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractorTest.kt
@@ -22,6 +22,7 @@ import android.telephony.CellSignalStrength
import android.telephony.TelephonyManager.NETWORK_TYPE_UNKNOWN
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import com.android.internal.telephony.flags.Flags
import com.android.settingslib.mobile.MobileIconCarrierIdOverrides
import com.android.settingslib.mobile.MobileIconCarrierIdOverridesImpl
import com.android.settingslib.mobile.TelephonyIcons
@@ -58,21 +59,40 @@ import org.mockito.ArgumentMatchers.anyString
@SmallTest
@RunWith(AndroidJUnit4::class)
-class MobileIconInteractorTest : SysuiTestCase() {
- private val kosmos = testKosmos()
+class MobileIconInteractorTest : MobileIconInteractorTestBase() {
+ override fun createInteractor(overrides: MobileIconCarrierIdOverrides) =
+ MobileIconInteractorImpl(
+ testScope.backgroundScope,
+ mobileIconsInteractor.activeDataConnectionHasDataEnabled,
+ mobileIconsInteractor.alwaysShowDataRatIcon,
+ mobileIconsInteractor.alwaysUseCdmaLevel,
+ mobileIconsInteractor.isSingleCarrier,
+ mobileIconsInteractor.mobileIsDefault,
+ mobileIconsInteractor.defaultMobileIconMapping,
+ mobileIconsInteractor.defaultMobileIconGroup,
+ mobileIconsInteractor.isDefaultConnectionFailed,
+ mobileIconsInteractor.isForceHidden,
+ connectionRepository,
+ context,
+ overrides,
+ )
+}
- private lateinit var underTest: MobileIconInteractor
- private val mobileMappingsProxy = FakeMobileMappingsProxy()
- private val mobileIconsInteractor = FakeMobileIconsInteractor(mobileMappingsProxy, mock())
+abstract class MobileIconInteractorTestBase : SysuiTestCase() {
+ protected val kosmos = testKosmos()
- private val connectionRepository =
+ protected lateinit var underTest: MobileIconInteractor
+ protected val mobileMappingsProxy = FakeMobileMappingsProxy()
+ protected val mobileIconsInteractor = FakeMobileIconsInteractor(mobileMappingsProxy, mock())
+
+ protected val connectionRepository =
FakeMobileConnectionRepository(
SUB_1_ID,
logcatTableLogBuffer(kosmos, "MobileIconInteractorTest"),
)
- private val testDispatcher = UnconfinedTestDispatcher()
- private val testScope = TestScope(testDispatcher)
+ protected val testDispatcher = UnconfinedTestDispatcher()
+ protected val testScope = TestScope(testDispatcher)
@Before
fun setUp() {
@@ -835,24 +855,9 @@ class MobileIconInteractorTest : SysuiTestCase() {
assertThat(latest!!.level).isEqualTo(0)
}
- private fun createInteractor(
+ abstract fun createInteractor(
overrides: MobileIconCarrierIdOverrides = MobileIconCarrierIdOverridesImpl()
- ) =
- MobileIconInteractorImpl(
- testScope.backgroundScope,
- mobileIconsInteractor.activeDataConnectionHasDataEnabled,
- mobileIconsInteractor.alwaysShowDataRatIcon,
- mobileIconsInteractor.alwaysUseCdmaLevel,
- mobileIconsInteractor.isSingleCarrier,
- mobileIconsInteractor.mobileIsDefault,
- mobileIconsInteractor.defaultMobileIconMapping,
- mobileIconsInteractor.defaultMobileIconGroup,
- mobileIconsInteractor.isDefaultConnectionFailed,
- mobileIconsInteractor.isForceHidden,
- connectionRepository,
- context,
- overrides,
- )
+ ): MobileIconInteractor
companion object {
private const val GSM_LEVEL = 1
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorKairosAdapterTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorKairosAdapterTest.kt
new file mode 100644
index 000000000000..787731e14cd1
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorKairosAdapterTest.kt
@@ -0,0 +1,223 @@
+/*
+ * Copyright (C) 2025 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.pipeline.mobile.domain.interactor
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.settingslib.SignalIcon
+import com.android.settingslib.mobile.MobileMappings
+import com.android.settingslib.mobile.TelephonyIcons
+import com.android.systemui.KairosBuilder
+import com.android.systemui.flags.featureFlagsClassic
+import com.android.systemui.kairos.BuildScope
+import com.android.systemui.kairos.Events
+import com.android.systemui.kairos.ExperimentalKairosApi
+import com.android.systemui.kairos.Incremental
+import com.android.systemui.kairos.State
+import com.android.systemui.kairos.activateKairosActivatable
+import com.android.systemui.kairos.asIncremental
+import com.android.systemui.kairos.buildSpec
+import com.android.systemui.kairos.kairos
+import com.android.systemui.kairos.map
+import com.android.systemui.kairos.mapValues
+import com.android.systemui.kairos.stateOf
+import com.android.systemui.kairosBuilder
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.kosmos.runCurrent
+import com.android.systemui.log.table.TableLogBuffer
+import com.android.systemui.log.table.tableLogBufferFactory
+import com.android.systemui.statusbar.pipeline.mobile.data.model.DataConnectionState
+import com.android.systemui.statusbar.pipeline.mobile.data.model.NetworkNameModel
+import com.android.systemui.statusbar.pipeline.mobile.data.model.ResolvedNetworkType
+import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionRepository
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionRepositoryKairos
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionsRepository
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionsRepositoryKairos
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.mobileMappingsProxy
+import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityModel
+import com.android.systemui.statusbar.pipeline.shared.data.repository.connectivityRepository
+import com.android.systemui.statusbar.policy.data.repository.FakeUserSetupRepository
+import org.junit.runner.RunWith
+import org.mockito.kotlin.mock
+
+@OptIn(ExperimentalKairosApi::class)
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class MobileIconsInteractorKairosAdapterTest : MobileIconsInteractorTestBase() {
+ override fun Kosmos.createInteractor(): MobileIconsInteractor {
+ val userSetupRepo = FakeUserSetupRepository()
+ val repoK =
+ MobileConnectionsRepoWrapper(connectionsRepository).also {
+ activateKairosActivatable(it)
+ }
+ val kairosInteractor =
+ MobileIconsInteractorKairosImpl(
+ mobileConnectionsRepo = repoK,
+ carrierConfigTracker = carrierConfigTracker,
+ tableLogger = mock(),
+ connectivityRepository = connectivityRepository,
+ userSetupRepo = userSetupRepo,
+ context = context,
+ featureFlagsClassic = featureFlagsClassic,
+ )
+ .also { activateKairosActivatable(it) }
+ return MobileIconsInteractorKairosAdapter(
+ kairosInteractor = kairosInteractor,
+ repo = connectionsRepository,
+ repoK = repoK,
+ kairosNetwork = kairos,
+ scope = applicationCoroutineScope,
+ context = context,
+ mobileMappingsProxy = mobileMappingsProxy,
+ userSetupRepo = userSetupRepo,
+ logFactory = tableLogBufferFactory,
+ )
+ .also {
+ activateKairosActivatable(it)
+ runCurrent()
+ }
+ }
+
+ /** Allows us to wrap a (likely fake) MobileConnectionsRepository into a Kairos version. */
+ private class MobileConnectionsRepoWrapper(val unwrapped: MobileConnectionsRepository) :
+ MobileConnectionsRepositoryKairos, KairosBuilder by kairosBuilder() {
+
+ override val mobileConnectionsBySubId: Incremental<Int, MobileConnectionRepositoryKairos> =
+ buildIncremental {
+ unwrapped.subscriptions
+ .toState()
+ .map { it.associate { it.subscriptionId to Unit } }
+ .asIncremental()
+ .mapValues { (subId, _) ->
+ buildSpec { wrapRepo(unwrapped.getRepoForSubId(subId)) }
+ }
+ .applyLatestSpecForKey()
+ }
+ override val subscriptions: State<Collection<SubscriptionModel>> = buildState {
+ unwrapped.subscriptions.toState()
+ }
+ override val activeMobileDataSubscriptionId: State<Int?> = buildState {
+ unwrapped.activeMobileDataSubscriptionId.toState()
+ }
+ override val activeMobileDataRepository: State<MobileConnectionRepositoryKairos?> =
+ buildState {
+ unwrapped.activeMobileDataRepository.toState().mapLatestBuild {
+ it?.let { wrapRepo(it) }
+ }
+ }
+ override val activeSubChangedInGroupEvent: Events<Unit> = buildEvents {
+ unwrapped.activeSubChangedInGroupEvent.toEvents()
+ }
+ override val defaultDataSubId: State<Int?> = buildState {
+ unwrapped.defaultDataSubId.toState()
+ }
+ override val mobileIsDefault: State<Boolean> = buildState {
+ unwrapped.mobileIsDefault.toState()
+ }
+ override val hasCarrierMergedConnection: State<Boolean> = buildState {
+ unwrapped.hasCarrierMergedConnection.toState(false)
+ }
+ override val defaultConnectionIsValidated: State<Boolean> = buildState {
+ unwrapped.defaultConnectionIsValidated.toState()
+ }
+ override val defaultDataSubRatConfig: State<MobileMappings.Config> = buildState {
+ unwrapped.defaultDataSubRatConfig.toState()
+ }
+ override val defaultMobileIconMapping: State<Map<String, SignalIcon.MobileIconGroup>> =
+ buildState {
+ unwrapped.defaultMobileIconMapping.toState(emptyMap())
+ }
+ override val defaultMobileIconGroup: State<SignalIcon.MobileIconGroup> = buildState {
+ unwrapped.defaultMobileIconGroup.toState(TelephonyIcons.THREE_G)
+ }
+ override val isDeviceEmergencyCallCapable: State<Boolean> = buildState {
+ unwrapped.isDeviceEmergencyCallCapable.toState()
+ }
+ override val isAnySimSecure: State<Boolean> = buildState {
+ unwrapped.isDeviceEmergencyCallCapable.toState()
+ }
+ override val isInEcmMode: State<Boolean> = stateOf(false)
+ }
+
+ private class MobileConnectionRepoWrapper(
+ override val subId: Int,
+ override val carrierId: State<Int>,
+ override val inflateSignalStrength: State<Boolean>,
+ override val allowNetworkSliceIndicator: State<Boolean>,
+ override val tableLogBuffer: TableLogBuffer,
+ override val isEmergencyOnly: State<Boolean>,
+ override val isRoaming: State<Boolean>,
+ override val operatorAlphaShort: State<String?>,
+ override val isInService: State<Boolean>,
+ override val isNonTerrestrial: State<Boolean>,
+ override val isGsm: State<Boolean>,
+ override val cdmaLevel: State<Int>,
+ override val primaryLevel: State<Int>,
+ override val satelliteLevel: State<Int>,
+ override val dataConnectionState: State<DataConnectionState>,
+ override val dataActivityDirection: State<DataActivityModel>,
+ override val carrierNetworkChangeActive: State<Boolean>,
+ override val resolvedNetworkType: State<ResolvedNetworkType>,
+ override val numberOfLevels: State<Int>,
+ override val dataEnabled: State<Boolean>,
+ override val cdmaRoaming: State<Boolean>,
+ override val networkName: State<NetworkNameModel>,
+ override val carrierName: State<NetworkNameModel>,
+ override val isAllowedDuringAirplaneMode: State<Boolean>,
+ override val hasPrioritizedNetworkCapabilities: State<Boolean>,
+ override val isInEcmMode: State<Boolean>,
+ ) : MobileConnectionRepositoryKairos
+
+ companion object {
+ /** Allows us to wrap a (likely fake) MobileConnectionRepository into a Kairos version. */
+ fun BuildScope.wrapRepo(
+ conn: MobileConnectionRepository
+ ): MobileConnectionRepositoryKairos =
+ with(conn) {
+ MobileConnectionRepoWrapper(
+ subId = subId,
+ carrierId = carrierId.toState(),
+ inflateSignalStrength = inflateSignalStrength.toState(),
+ allowNetworkSliceIndicator = allowNetworkSliceIndicator.toState(),
+ tableLogBuffer = tableLogBuffer,
+ isEmergencyOnly = isEmergencyOnly.toState(),
+ isRoaming = isRoaming.toState(),
+ operatorAlphaShort = operatorAlphaShort.toState(),
+ isInService = isInService.toState(),
+ isNonTerrestrial = isNonTerrestrial.toState(),
+ isGsm = isGsm.toState(),
+ cdmaLevel = cdmaLevel.toState(),
+ primaryLevel = primaryLevel.toState(),
+ satelliteLevel = satelliteLevel.toState(),
+ dataConnectionState = dataConnectionState.toState(),
+ dataActivityDirection = dataActivityDirection.toState(),
+ carrierNetworkChangeActive = carrierNetworkChangeActive.toState(),
+ resolvedNetworkType = resolvedNetworkType.toState(),
+ numberOfLevels = numberOfLevels.toState(),
+ dataEnabled = dataEnabled.toState(),
+ cdmaRoaming = cdmaRoaming.toState(),
+ networkName = networkName.toState(),
+ carrierName = carrierName.toState(),
+ isAllowedDuringAirplaneMode = isAllowedDuringAirplaneMode.toState(),
+ hasPrioritizedNetworkCapabilities = hasPrioritizedNetworkCapabilities.toState(),
+ isInEcmMode = stateOf(false),
+ )
+ }
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorTest.kt
index 9e914ad0a660..356e5676954e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorTest.kt
@@ -58,8 +58,35 @@ import org.mockito.kotlin.whenever
@SmallTest
@RunWith(AndroidJUnit4::class)
-class MobileIconsInteractorTest : SysuiTestCase() {
- private val kosmos by lazy {
+class MobileIconsInteractorTest : MobileIconsInteractorTestBase() {
+ override fun Kosmos.createInteractor() =
+ MobileIconsInteractorImpl(
+ mobileConnectionsRepository,
+ carrierConfigTracker,
+ tableLogger = mock(),
+ connectivityRepository,
+ FakeUserSetupRepository(),
+ testScope.backgroundScope,
+ context,
+ featureFlagsClassic,
+ )
+
+ @Test
+ fun iconInteractor_cachedPerSubId() =
+ kosmos.runTest {
+ connectionsRepository.setSubscriptions(listOf(SUB_1))
+ runCurrent()
+
+ val interactor1 = underTest.getMobileConnectionInteractorForSubId(SUB_1_ID)
+ val interactor2 = underTest.getMobileConnectionInteractorForSubId(SUB_1_ID)
+
+ assertThat(interactor1).isNotNull()
+ assertThat(interactor1).isSameInstanceAs(interactor2)
+ }
+}
+
+abstract class MobileIconsInteractorTestBase : SysuiTestCase() {
+ protected val kosmos by lazy {
testKosmos().apply {
mobileConnectionsRepositoryLogbufferName = "MobileIconsInteractorTest"
mobileConnectionsRepository.fake.run {
@@ -78,22 +105,13 @@ class MobileIconsInteractorTest : SysuiTestCase() {
}
// shortcut rename
- private val Kosmos.connectionsRepository by Fixture { mobileConnectionsRepository.fake }
+ protected val Kosmos.connectionsRepository by Fixture { mobileConnectionsRepository.fake }
- private val Kosmos.carrierConfigTracker by Fixture { mock<CarrierConfigTracker>() }
+ protected val Kosmos.carrierConfigTracker by Fixture { mock<CarrierConfigTracker>() }
- private val Kosmos.underTest by Fixture {
- MobileIconsInteractorImpl(
- mobileConnectionsRepository,
- carrierConfigTracker,
- tableLogger = mock(),
- connectivityRepository,
- FakeUserSetupRepository(),
- testScope.backgroundScope,
- context,
- featureFlagsClassic,
- )
- }
+ protected val Kosmos.underTest by Fixture { createInteractor() }
+
+ abstract fun Kosmos.createInteractor(): MobileIconsInteractor
@Test
fun filteredSubscriptions_default() =
@@ -744,12 +762,15 @@ class MobileIconsInteractorTest : SysuiTestCase() {
val latest by collectLastValue(underTest.mobileIsDefault)
connectionsRepository.mobileIsDefault.value = true
+ runCurrent()
assertThat(latest).isTrue()
connectionsRepository.mobileIsDefault.value = false
+ runCurrent()
assertThat(latest).isFalse()
connectionsRepository.hasCarrierMergedConnection.value = true
+ runCurrent()
assertThat(latest).isTrue()
}
@@ -874,16 +895,6 @@ class MobileIconsInteractorTest : SysuiTestCase() {
}
@Test
- fun iconInteractor_cachedPerSubId() =
- kosmos.runTest {
- val interactor1 = underTest.getMobileConnectionInteractorForSubId(SUB_1_ID)
- val interactor2 = underTest.getMobileConnectionInteractorForSubId(SUB_1_ID)
-
- assertThat(interactor1).isNotNull()
- assertThat(interactor1).isSameInstanceAs(interactor2)
- }
-
- @Test
fun deviceBasedEmergencyMode_emergencyCallsOnly_followsDeviceServiceStateFromRepo() =
kosmos.runTest {
val latest by collectLastValue(underTest.isDeviceInEmergencyCallsOnlyMode)
@@ -1007,8 +1018,8 @@ class MobileIconsInteractorTest : SysuiTestCase() {
companion object {
- private const val SUB_1_ID = 1
- private val SUB_1 =
+ const val SUB_1_ID = 1
+ val SUB_1 =
SubscriptionModel(
subscriptionId = SUB_1_ID,
carrierName = "Carrier $SUB_1_ID",
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/LocationBasedMobileIconViewModelKairosTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/LocationBasedMobileIconViewModelKairosTest.kt
index 57e63a595b8f..9042ac45cd4d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/LocationBasedMobileIconViewModelKairosTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/LocationBasedMobileIconViewModelKairosTest.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2022 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -19,110 +19,69 @@ package com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
-import com.android.systemui.flags.FakeFeatureFlagsClassic
import com.android.systemui.flags.Flags
+import com.android.systemui.flags.fake
+import com.android.systemui.flags.featureFlagsClassic
+import com.android.systemui.kairos.ActivatedKairosFixture
+import com.android.systemui.kairos.ExperimentalKairosApi
+import com.android.systemui.kairos.KairosTestScope
+import com.android.systemui.kairos.kairos
+import com.android.systemui.kairos.runKairosTest
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.useUnconfinedTestDispatcher
import com.android.systemui.log.table.logcatTableLogBuffer
import com.android.systemui.statusbar.connectivity.MobileIconCarrierIdOverridesFake
-import com.android.systemui.statusbar.pipeline.airplane.data.repository.FakeAirplaneModeRepository
-import com.android.systemui.statusbar.pipeline.airplane.domain.interactor.AirplaneModeInteractor
+import com.android.systemui.statusbar.pipeline.airplane.domain.interactor.airplaneModeInteractor
import com.android.systemui.statusbar.pipeline.mobile.data.model.DataConnectionState
import com.android.systemui.statusbar.pipeline.mobile.data.model.ResolvedNetworkType
-import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionRepository
-import com.android.systemui.statusbar.pipeline.mobile.data.repository.fakeMobileConnectionsRepository
-import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconInteractor
-import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconInteractorImpl
-import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconsInteractor
-import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconsInteractorImpl
+import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionRepositoryKairos
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.fakeMobileConnectionsRepositoryKairos
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.mobileConnectionsRepositoryKairos
+import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconInteractorKairos
+import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconInteractorKairosImpl
+import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconsInteractorKairos
+import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.mobileIconsInteractorKairos
import com.android.systemui.statusbar.pipeline.mobile.domain.model.SignalIconModel
import com.android.systemui.statusbar.pipeline.shared.ConnectivityConstants
-import com.android.systemui.statusbar.pipeline.shared.data.repository.FakeConnectivityRepository
-import com.android.systemui.statusbar.policy.data.repository.FakeUserSetupRepository
+import com.android.systemui.statusbar.pipeline.shared.data.repository.connectivityRepository
+import com.android.systemui.statusbar.pipeline.shared.data.repository.fake
import com.android.systemui.testKosmos
-import com.android.systemui.util.CarrierConfigTracker
-import com.android.systemui.util.mockito.mock
import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.flow.launchIn
-import kotlinx.coroutines.flow.onEach
-import kotlinx.coroutines.test.TestScope
-import kotlinx.coroutines.test.UnconfinedTestDispatcher
-import kotlinx.coroutines.test.runTest
-import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
-import org.mockito.Mock
-import org.mockito.MockitoAnnotations
+import org.mockito.kotlin.mock
-@Suppress("EXPERIMENTAL_IS_NOT_ENABLED")
-@OptIn(ExperimentalCoroutinesApi::class)
+@OptIn(ExperimentalKairosApi::class)
@SmallTest
@RunWith(AndroidJUnit4::class)
class LocationBasedMobileIconViewModelKairosTest : SysuiTestCase() {
- private val kosmos = testKosmos()
-
- private lateinit var commonImpl: MobileIconViewModelCommonKairos
- private lateinit var homeIcon: HomeMobileIconViewModelKairos
- private lateinit var qsIcon: QsMobileIconViewModelKairos
- private lateinit var keyguardIcon: KeyguardMobileIconViewModelKairos
- private lateinit var iconsInteractor: MobileIconsInteractor
- private lateinit var interactor: MobileIconInteractor
- private val connectionsRepository = kosmos.fakeMobileConnectionsRepository
- private lateinit var repository: FakeMobileConnectionRepository
- private lateinit var airplaneModeInteractor: AirplaneModeInteractor
-
- private val connectivityRepository = FakeConnectivityRepository()
- private val flags =
- FakeFeatureFlagsClassic().also {
- it.set(Flags.FILTER_PROVISIONING_NETWORK_SUBSCRIPTIONS, true)
- }
- @Mock private lateinit var constants: ConnectivityConstants
- private val tableLogBuffer =
- logcatTableLogBuffer(kosmos, "LocationBasedMobileIconViewModelTest")
- @Mock private lateinit var carrierConfigTracker: CarrierConfigTracker
-
- private val testDispatcher = UnconfinedTestDispatcher()
- private val testScope = TestScope(testDispatcher)
-
- @Before
- fun setUp() {
- MockitoAnnotations.initMocks(this)
- airplaneModeInteractor =
- AirplaneModeInteractor(
- FakeAirplaneModeRepository(),
- FakeConnectivityRepository(),
- connectionsRepository,
- )
- repository =
- FakeMobileConnectionRepository(SUB_1_ID, tableLogBuffer).apply {
- isInService.value = true
- cdmaLevel.value = 1
- primaryLevel.value = 1
- isEmergencyOnly.value = false
- numberOfLevels.value = 4
- resolvedNetworkType.value = ResolvedNetworkType.DefaultNetworkType(lookupKey = "3G")
- dataConnectionState.value = DataConnectionState.Connected
- }
+ private val Kosmos.commonImpl: MobileIconViewModelKairosCommon by ActivatedKairosFixture {
+ MobileIconViewModelKairos(
+ SUB_1_ID,
+ interactor,
+ airplaneModeInteractor,
+ constants,
+ featureFlagsClassic,
+ )
+ }
- connectionsRepository.activeMobileDataRepository.value = repository
+ private val Kosmos.homeIcon: HomeMobileIconViewModelKairos by
+ Kosmos.Fixture { HomeMobileIconViewModelKairos(commonImpl, mock()) }
- connectivityRepository.apply { setMobileConnected() }
+ private val Kosmos.qsIcon: QsMobileIconViewModelKairos by
+ Kosmos.Fixture { QsMobileIconViewModelKairos(commonImpl) }
- iconsInteractor =
- MobileIconsInteractorImpl(
- connectionsRepository,
- carrierConfigTracker,
- tableLogBuffer,
- connectivityRepository,
- FakeUserSetupRepository(),
- testScope.backgroundScope,
- context,
- flags,
- )
+ private val Kosmos.keyguardIcon: KeyguardMobileIconViewModelKairos by
+ Kosmos.Fixture { KeyguardMobileIconViewModelKairos(commonImpl) }
+
+ private val Kosmos.iconsInteractor: MobileIconsInteractorKairos
+ get() = mobileIconsInteractorKairos
- interactor =
- MobileIconInteractorImpl(
- testScope.backgroundScope,
+ private val Kosmos.interactor: MobileIconInteractorKairos by
+ Kosmos.Fixture {
+ MobileIconInteractorKairosImpl(
iconsInteractor.activeDataConnectionHasDataEnabled,
iconsInteractor.alwaysShowDataRatIcon,
iconsInteractor.alwaysUseCdmaLevel,
@@ -136,50 +95,74 @@ class LocationBasedMobileIconViewModelKairosTest : SysuiTestCase() {
context,
MobileIconCarrierIdOverridesFake(),
)
+ }
- commonImpl =
- MobileIconViewModelKairos(
- SUB_1_ID,
- interactor,
- airplaneModeInteractor,
- constants,
- testScope.backgroundScope,
- )
-
- homeIcon = HomeMobileIconViewModelKairos(commonImpl, mock())
- qsIcon = QsMobileIconViewModelKairos(commonImpl)
- keyguardIcon = KeyguardMobileIconViewModelKairos(commonImpl)
- }
+ private val Kosmos.repository: FakeMobileConnectionRepositoryKairos by
+ Kosmos.Fixture {
+ FakeMobileConnectionRepositoryKairos(SUB_1_ID, kairos, tableLogBuffer).apply {
+ isInService.setValue(true)
+ cdmaLevel.setValue(1)
+ primaryLevel.setValue(1)
+ isEmergencyOnly.setValue(false)
+ numberOfLevels.setValue(4)
+ resolvedNetworkType.setValue(
+ ResolvedNetworkType.DefaultNetworkType(lookupKey = "3G")
+ )
+ dataConnectionState.setValue(DataConnectionState.Connected)
+ }
+ }
- @Test
- fun locationBasedViewModelsReceiveSameIconIdWhenCommonImplUpdates() =
- testScope.runTest {
- var latestHome: SignalIconModel? = null
- val homeJob = homeIcon.icon.onEach { latestHome = it }.launchIn(this)
+ private val Kosmos.constants: ConnectivityConstants by Kosmos.Fixture { mock() }
+ private val Kosmos.tableLogBuffer by
+ Kosmos.Fixture { logcatTableLogBuffer(this, "LocationBasedMobileIconViewModelTest") }
+
+ private val kosmos =
+ testKosmos().apply {
+ useUnconfinedTestDispatcher()
+ mobileConnectionsRepositoryKairos =
+ fakeMobileConnectionsRepositoryKairos.apply {
+ setActiveMobileDataSubscriptionId(SUB_1_ID)
+ subscriptions.setValue(
+ listOf(
+ SubscriptionModel(
+ SUB_1_ID,
+ carrierName = "carrierName",
+ profileClass = 0,
+ )
+ )
+ )
+ }
+ connectivityRepository.fake.apply { setMobileConnected() }
+ featureFlagsClassic.fake.apply {
+ set(Flags.FILTER_PROVISIONING_NETWORK_SUBSCRIPTIONS, true)
+ }
+ }
- var latestQs: SignalIconModel? = null
- val qsJob = qsIcon.icon.onEach { latestQs = it }.launchIn(this)
+ private fun runTest(block: suspend KairosTestScope.() -> Unit) =
+ kosmos.run { runKairosTest { block() } }
- var latestKeyguard: SignalIconModel? = null
- val keyguardJob = keyguardIcon.icon.onEach { latestKeyguard = it }.launchIn(this)
+ @Test
+ fun locationBasedViewModelsReceiveSameIconIdWhenCommonImplUpdates() = runTest {
+ repository.dataEnabled.setValue(true)
+ repository.isInService.setValue(true)
- var expected = defaultSignal(level = 1)
+ val latestHome by homeIcon.icon.collectLastValue()
+ val latestQs by qsIcon.icon.collectLastValue()
+ val latestKeyguard by keyguardIcon.icon.collectLastValue()
- assertThat(latestHome).isEqualTo(expected)
- assertThat(latestQs).isEqualTo(expected)
- assertThat(latestKeyguard).isEqualTo(expected)
+ var expected = defaultSignal(level = 1)
- repository.setAllLevels(2)
- expected = defaultSignal(level = 2)
+ assertThat(latestHome).isEqualTo(expected)
+ assertThat(latestQs).isEqualTo(expected)
+ assertThat(latestKeyguard).isEqualTo(expected)
- assertThat(latestHome).isEqualTo(expected)
- assertThat(latestQs).isEqualTo(expected)
- assertThat(latestKeyguard).isEqualTo(expected)
+ repository.setAllLevels(2)
+ expected = defaultSignal(level = 2)
- homeJob.cancel()
- qsJob.cancel()
- keyguardJob.cancel()
- }
+ assertThat(latestHome).isEqualTo(expected)
+ assertThat(latestQs).isEqualTo(expected)
+ assertThat(latestKeyguard).isEqualTo(expected)
+ }
companion object {
private const val SUB_1_ID = 1
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModelKairosTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModelKairosTest.kt
index 6b114a8256f2..68499d1bc57c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModelKairosTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModelKairosTest.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2022 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -28,1039 +28,893 @@ import com.android.systemui.Flags.FLAG_STATUS_BAR_STATIC_INOUT_INDICATORS
import com.android.systemui.SysuiTestCase
import com.android.systemui.common.shared.model.ContentDescription
import com.android.systemui.common.shared.model.Icon
-import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.flags.FakeFeatureFlagsClassic
import com.android.systemui.flags.Flags
+import com.android.systemui.flags.fake
+import com.android.systemui.flags.featureFlagsClassic
+import com.android.systemui.kairos.ActivatedKairosFixture
+import com.android.systemui.kairos.ExperimentalKairosApi
+import com.android.systemui.kairos.KairosTestScope
+import com.android.systemui.kairos.kairos
+import com.android.systemui.kairos.map
+import com.android.systemui.kairos.runKairosTest
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.useUnconfinedTestDispatcher
import com.android.systemui.log.table.logcatTableLogBuffer
import com.android.systemui.res.R
import com.android.systemui.statusbar.connectivity.MobileIconCarrierIdOverridesFake
import com.android.systemui.statusbar.core.NewStatusBarIcons
import com.android.systemui.statusbar.core.StatusBarRootModernization
-import com.android.systemui.statusbar.pipeline.airplane.data.repository.FakeAirplaneModeRepository
-import com.android.systemui.statusbar.pipeline.airplane.domain.interactor.AirplaneModeInteractor
+import com.android.systemui.statusbar.pipeline.airplane.data.repository.airplaneModeRepository
+import com.android.systemui.statusbar.pipeline.airplane.data.repository.fake
+import com.android.systemui.statusbar.pipeline.airplane.domain.interactor.airplaneModeInteractor
import com.android.systemui.statusbar.pipeline.mobile.data.model.DataConnectionState
import com.android.systemui.statusbar.pipeline.mobile.data.model.NetworkNameModel
-import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionRepository
+import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel
import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionRepository.Companion.DEFAULT_NETWORK_NAME
-import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionsRepository
-import com.android.systemui.statusbar.pipeline.mobile.data.repository.fakeMobileConnectionsRepository
-import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconInteractorImpl
-import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconsInteractorImpl
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionRepositoryKairos
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.fake
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.fakeMobileConnectionsRepositoryKairos
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.mobileConnectionsRepositoryKairos
+import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconInteractorKairos
+import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconInteractorKairosImpl
+import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.mobileIconsInteractorKairos
import com.android.systemui.statusbar.pipeline.mobile.domain.model.SignalIconModel
import com.android.systemui.statusbar.pipeline.mobile.ui.model.MobileContentDescription
-import com.android.systemui.statusbar.pipeline.mobile.util.FakeMobileMappingsProxy
import com.android.systemui.statusbar.pipeline.shared.ConnectivityConstants
import com.android.systemui.statusbar.pipeline.shared.data.model.ConnectivitySlot
import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityModel
-import com.android.systemui.statusbar.pipeline.shared.data.repository.FakeConnectivityRepository
-import com.android.systemui.statusbar.policy.data.repository.FakeUserSetupRepository
+import com.android.systemui.statusbar.pipeline.shared.data.repository.connectivityRepository
+import com.android.systemui.statusbar.pipeline.shared.data.repository.fake
import com.android.systemui.testKosmos
-import com.android.systemui.util.CarrierConfigTracker
-import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
import com.google.common.truth.Truth.assertWithMessage
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.flow.filterIsInstance
-import kotlinx.coroutines.flow.launchIn
-import kotlinx.coroutines.flow.onEach
-import kotlinx.coroutines.test.TestScope
-import kotlinx.coroutines.test.UnconfinedTestDispatcher
-import kotlinx.coroutines.test.runTest
import kotlinx.coroutines.yield
-import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
-import org.mockito.Mock
-import org.mockito.MockitoAnnotations
+import org.mockito.kotlin.doReturn
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.stub
-@Suppress("EXPERIMENTAL_IS_NOT_ENABLED")
-@OptIn(ExperimentalCoroutinesApi::class)
+@OptIn(ExperimentalKairosApi::class)
@SmallTest
@RunWith(AndroidJUnit4::class)
class MobileIconViewModelKairosTest : SysuiTestCase() {
- private val kosmos = testKosmos()
-
- private var connectivityRepository = FakeConnectivityRepository()
-
- private lateinit var underTest: MobileIconViewModelKairos
- private lateinit var interactor: MobileIconInteractorImpl
- private lateinit var iconsInteractor: MobileIconsInteractorImpl
- private lateinit var repository: FakeMobileConnectionRepository
- private lateinit var connectionsRepository: FakeMobileConnectionsRepository
- private lateinit var airplaneModeRepository: FakeAirplaneModeRepository
- private lateinit var airplaneModeInteractor: AirplaneModeInteractor
- @Mock private lateinit var constants: ConnectivityConstants
- private val tableLogBuffer = logcatTableLogBuffer(kosmos, "MobileIconViewModelTest")
- @Mock private lateinit var carrierConfigTracker: CarrierConfigTracker
-
- private val flags =
- FakeFeatureFlagsClassic().also {
- it.set(Flags.FILTER_PROVISIONING_NETWORK_SUBSCRIPTIONS, true)
+
+ private val Kosmos.underTest: MobileIconViewModelKairos by ActivatedKairosFixture {
+ MobileIconViewModelKairos(
+ SUB_1_ID,
+ interactor,
+ airplaneModeInteractor,
+ constants,
+ featureFlagsClassic,
+ )
+ }
+ private val Kosmos.interactor: MobileIconInteractorKairos by ActivatedKairosFixture {
+ MobileIconInteractorKairosImpl(
+ mobileIconsInteractorKairos.activeDataConnectionHasDataEnabled,
+ mobileIconsInteractorKairos.alwaysShowDataRatIcon,
+ mobileIconsInteractorKairos.alwaysUseCdmaLevel,
+ mobileIconsInteractorKairos.isSingleCarrier,
+ mobileIconsInteractorKairos.mobileIsDefault,
+ mobileIconsInteractorKairos.defaultMobileIconMapping,
+ mobileIconsInteractorKairos.defaultMobileIconGroup,
+ mobileIconsInteractorKairos.isDefaultConnectionFailed,
+ mobileIconsInteractorKairos.isForceHidden,
+ repository,
+ context,
+ MobileIconCarrierIdOverridesFake(),
+ )
+ }
+ private val Kosmos.repository: FakeMobileConnectionRepositoryKairos by
+ Kosmos.Fixture {
+ FakeMobileConnectionRepositoryKairos(SUB_1_ID, kairos, tableLogBuffer)
+ .also {
+ mobileConnectionsRepositoryKairos.fake.setActiveMobileDataSubscriptionId(
+ SUB_1_ID
+ )
+ mobileConnectionsRepositoryKairos.fake.subscriptions.setValue(
+ listOf(
+ SubscriptionModel(
+ SUB_1_ID,
+ carrierName = "carrierName",
+ profileClass = 0,
+ )
+ )
+ )
+ }
+ .apply {
+ isInService.setValue(true)
+ dataConnectionState.setValue(DataConnectionState.Connected)
+ dataEnabled.setValue(true)
+ setNetworkTypeKey(mobileConnectionsRepositoryKairos.fake.GSM_KEY)
+ }
}
- private val testDispatcher = UnconfinedTestDispatcher()
- private val testScope = TestScope(testDispatcher)
-
- @Before
- fun setUp() {
- MockitoAnnotations.initMocks(this)
- whenever(constants.hasDataCapabilities).thenReturn(true)
-
- connectionsRepository =
- FakeMobileConnectionsRepository(FakeMobileMappingsProxy(), tableLogBuffer)
-
- repository =
- FakeMobileConnectionRepository(SUB_1_ID, tableLogBuffer).apply {
- setNetworkTypeKey(connectionsRepository.GSM_KEY)
- isInService.value = true
- dataConnectionState.value = DataConnectionState.Connected
- dataEnabled.value = true
+ private val Kosmos.constants: ConnectivityConstants by
+ Kosmos.Fixture { mock { on { hasDataCapabilities } doReturn true } }
+ private val Kosmos.tableLogBuffer by
+ Kosmos.Fixture { logcatTableLogBuffer(this, "MobileIconViewModelKairosTest") }
+
+ private val kosmos =
+ testKosmos().apply {
+ useUnconfinedTestDispatcher()
+ mobileConnectionsRepositoryKairos =
+ fakeMobileConnectionsRepositoryKairos.apply { mobileIsDefault.setValue(true) }
+ featureFlagsClassic.fake.apply {
+ set(Flags.FILTER_PROVISIONING_NETWORK_SUBSCRIPTIONS, true)
}
- connectionsRepository.activeMobileDataRepository.value = repository
- connectionsRepository.mobileIsDefault.value = true
-
- airplaneModeRepository = FakeAirplaneModeRepository()
- airplaneModeInteractor =
- AirplaneModeInteractor(
- airplaneModeRepository,
- connectivityRepository,
- kosmos.fakeMobileConnectionsRepository,
- )
-
- iconsInteractor =
- MobileIconsInteractorImpl(
- connectionsRepository,
- carrierConfigTracker,
- tableLogBuffer,
- connectivityRepository,
- FakeUserSetupRepository(),
- testScope.backgroundScope,
- context,
- flags,
- )
+ }
- interactor =
- MobileIconInteractorImpl(
- testScope.backgroundScope,
- iconsInteractor.activeDataConnectionHasDataEnabled,
- iconsInteractor.alwaysShowDataRatIcon,
- iconsInteractor.alwaysUseCdmaLevel,
- iconsInteractor.isSingleCarrier,
- iconsInteractor.mobileIsDefault,
- iconsInteractor.defaultMobileIconMapping,
- iconsInteractor.defaultMobileIconGroup,
- iconsInteractor.isDefaultConnectionFailed,
- iconsInteractor.isForceHidden,
- repository,
- context,
- MobileIconCarrierIdOverridesFake(),
- )
- createAndSetViewModel()
- }
+ private fun runTest(block: suspend KairosTestScope.() -> Unit) =
+ kosmos.run { runKairosTest { block() } }
@Test
- fun isVisible_notDataCapable_alwaysFalse() =
- testScope.runTest {
- // Create a new view model here so the constants are properly read
- whenever(constants.hasDataCapabilities).thenReturn(false)
- createAndSetViewModel()
-
- var latest: Boolean? = null
- val job = underTest.isVisible.onEach { latest = it }.launchIn(this)
+ fun isVisible_notDataCapable_alwaysFalse() = runTest {
+ // Create a new view model here so the constants are properly read
+ constants.stub { on { hasDataCapabilities } doReturn false }
- assertThat(latest).isFalse()
+ val latest by underTest.isVisible.collectLastValue()
- job.cancel()
- }
+ assertThat(latest).isFalse()
+ }
@Test
- fun isVisible_notAirplane_notForceHidden_true() =
- testScope.runTest {
- var latest: Boolean? = null
- val job = underTest.isVisible.onEach { latest = it }.launchIn(this)
+ fun isVisible_notAirplane_notForceHidden_true() = runTest {
+ val latest by underTest.isVisible.collectLastValue()
- airplaneModeRepository.setIsAirplaneMode(false)
+ airplaneModeRepository.fake.setIsAirplaneMode(false)
- assertThat(latest).isTrue()
-
- job.cancel()
- }
+ assertThat(latest).isTrue()
+ }
@Test
- fun isVisible_airplaneAndNotAllowed_false() =
- testScope.runTest {
- var latest: Boolean? = null
- val job = underTest.isVisible.onEach { latest = it }.launchIn(this)
-
- airplaneModeRepository.setIsAirplaneMode(true)
- repository.isAllowedDuringAirplaneMode.value = false
- connectivityRepository.setForceHiddenIcons(setOf())
+ fun isVisible_airplaneAndNotAllowed_false() = runTest {
+ val latest by underTest.isVisible.collectLastValue()
- assertThat(latest).isFalse()
+ airplaneModeRepository.fake.setIsAirplaneMode(true)
+ repository.isAllowedDuringAirplaneMode.setValue(false)
+ connectivityRepository.fake.setForceHiddenIcons(setOf())
- job.cancel()
- }
+ assertThat(latest).isEqualTo(false)
+ }
/** Regression test for b/291993542. */
@Test
- fun isVisible_airplaneButAllowed_true() =
- testScope.runTest {
- var latest: Boolean? = null
- val job = underTest.isVisible.onEach { latest = it }.launchIn(this)
-
- airplaneModeRepository.setIsAirplaneMode(true)
- repository.isAllowedDuringAirplaneMode.value = true
- connectivityRepository.setForceHiddenIcons(setOf())
+ fun isVisible_airplaneButAllowed_true() = runTest {
+ val latest by underTest.isVisible.collectLastValue()
- assertThat(latest).isTrue()
+ airplaneModeRepository.fake.setIsAirplaneMode(true)
+ repository.isAllowedDuringAirplaneMode.setValue(true)
+ connectivityRepository.fake.setForceHiddenIcons(setOf())
- job.cancel()
- }
+ assertThat(latest).isTrue()
+ }
@Test
- fun isVisible_forceHidden_false() =
- testScope.runTest {
- var latest: Boolean? = null
- val job = underTest.isVisible.onEach { latest = it }.launchIn(this)
+ fun isVisible_forceHidden_false() = runTest {
+ val latest by underTest.isVisible.collectLastValue()
- airplaneModeRepository.setIsAirplaneMode(false)
- connectivityRepository.setForceHiddenIcons(setOf(ConnectivitySlot.MOBILE))
+ airplaneModeRepository.fake.setIsAirplaneMode(false)
+ connectivityRepository.fake.setForceHiddenIcons(setOf(ConnectivitySlot.MOBILE))
- assertThat(latest).isFalse()
-
- job.cancel()
- }
+ assertThat(latest).isFalse()
+ }
@Test
- fun isVisible_respondsToUpdates() =
- testScope.runTest {
- var latest: Boolean? = null
- val job = underTest.isVisible.onEach { latest = it }.launchIn(this)
-
- airplaneModeRepository.setIsAirplaneMode(false)
- connectivityRepository.setForceHiddenIcons(setOf())
+ fun isVisible_respondsToUpdates() = runTest {
+ val latest by underTest.isVisible.collectLastValue()
- assertThat(latest).isTrue()
+ airplaneModeRepository.fake.setIsAirplaneMode(false)
+ connectivityRepository.fake.setForceHiddenIcons(setOf())
- airplaneModeRepository.setIsAirplaneMode(true)
- assertThat(latest).isFalse()
+ assertThat(latest).isEqualTo(true)
- repository.isAllowedDuringAirplaneMode.value = true
- assertThat(latest).isTrue()
+ airplaneModeRepository.fake.setIsAirplaneMode(true)
+ assertThat(latest).isEqualTo(false)
- connectivityRepository.setForceHiddenIcons(setOf(ConnectivitySlot.MOBILE))
- assertThat(latest).isFalse()
+ repository.isAllowedDuringAirplaneMode.setValue(true)
+ assertThat(latest).isEqualTo(true)
- job.cancel()
- }
+ connectivityRepository.fake.setForceHiddenIcons(setOf(ConnectivitySlot.MOBILE))
+ assertThat(latest).isEqualTo(false)
+ }
@Test
- fun isVisible_satellite_respectsAirplaneMode() =
- testScope.runTest {
- val latest by collectLastValue(underTest.isVisible)
+ fun isVisible_satellite_respectsAirplaneMode() = runTest {
+ val latest by underTest.isVisible.collectLastValue()
- repository.isNonTerrestrial.value = true
- airplaneModeInteractor.setIsAirplaneMode(false)
+ repository.isNonTerrestrial.setValue(true)
+ airplaneModeInteractor.setIsAirplaneMode(false)
- assertThat(latest).isTrue()
+ assertThat(latest).isTrue()
- airplaneModeInteractor.setIsAirplaneMode(true)
+ airplaneModeInteractor.setIsAirplaneMode(true)
- assertThat(latest).isFalse()
- }
+ assertThat(latest).isFalse()
+ }
@Test
- fun contentDescription_notInService_usesNoPhone() =
- testScope.runTest {
- val latest by collectLastValue(underTest.contentDescription)
+ fun contentDescription_notInService_usesNoPhone() = runTest {
+ val latest by underTest.contentDescription.collectLastValue()
- repository.isInService.value = false
+ repository.isInService.setValue(false)
- assertThat(latest as MobileContentDescription.Cellular)
- .isEqualTo(MobileContentDescription.Cellular(DEFAULT_NETWORK_NAME, NO_SIGNAL))
- }
+ assertThat(latest)
+ .isEqualTo(MobileContentDescription.Cellular(DEFAULT_NETWORK_NAME, NO_SIGNAL))
+ }
@Test
- fun contentDescription_includesNetworkName() =
- testScope.runTest {
- val latest by collectLastValue(underTest.contentDescription)
+ fun contentDescription_includesNetworkName() = runTest {
+ val latest by underTest.contentDescription.collectLastValue()
- repository.isInService.value = true
- repository.networkName.value = NetworkNameModel.SubscriptionDerived("Test Network Name")
- repository.numberOfLevels.value = 5
- repository.setAllLevels(3)
+ repository.isInService.setValue(true)
+ repository.networkName.setValue(NetworkNameModel.SubscriptionDerived("Test Network Name"))
+ repository.numberOfLevels.setValue(5)
+ repository.setAllLevels(3)
- assertThat(latest as MobileContentDescription.Cellular)
- .isEqualTo(MobileContentDescription.Cellular("Test Network Name", THREE_BARS))
- }
+ assertThat(latest)
+ .isEqualTo(MobileContentDescription.Cellular("Test Network Name", THREE_BARS))
+ }
@Test
- fun contentDescription_inService_usesLevel() =
- testScope.runTest {
- val latest by collectLastValue(underTest.contentDescription)
+ fun contentDescription_inService_usesLevel() = runTest {
+ val latest by underTest.contentDescription.collectLastValue()
- repository.setAllLevels(2)
+ repository.setAllLevels(2)
- assertThat(latest as MobileContentDescription.Cellular)
- .isEqualTo(MobileContentDescription.Cellular(DEFAULT_NETWORK_NAME, TWO_BARS))
+ assertThat(latest as MobileContentDescription.Cellular)
+ .isEqualTo(MobileContentDescription.Cellular(DEFAULT_NETWORK_NAME, TWO_BARS))
- repository.setAllLevels(0)
+ repository.setAllLevels(0)
- assertThat(latest as MobileContentDescription.Cellular)
- .isEqualTo(MobileContentDescription.Cellular(DEFAULT_NETWORK_NAME, NO_SIGNAL))
- }
+ assertThat(latest as MobileContentDescription.Cellular)
+ .isEqualTo(MobileContentDescription.Cellular(DEFAULT_NETWORK_NAME, NO_SIGNAL))
+ }
@Test
- fun contentDescription_nonInflated_invalidLevelUsesNoSignalText() =
- testScope.runTest {
- val latest by collectLastValue(underTest.contentDescription)
+ fun contentDescription_nonInflated_invalidLevelUsesNoSignalText() = runTest {
+ val latest by underTest.contentDescription.collectLastValue()
- repository.inflateSignalStrength.value = false
- repository.setAllLevels(-1)
+ repository.inflateSignalStrength.setValue(false)
+ repository.setAllLevels(-1)
- assertThat(latest as MobileContentDescription.Cellular)
- .isEqualTo(MobileContentDescription.Cellular(DEFAULT_NETWORK_NAME, NO_SIGNAL))
+ assertThat(latest)
+ .isEqualTo(MobileContentDescription.Cellular(DEFAULT_NETWORK_NAME, NO_SIGNAL))
- repository.setAllLevels(100)
+ repository.setAllLevels(100)
- assertThat(latest as MobileContentDescription.Cellular)
- .isEqualTo(MobileContentDescription.Cellular(DEFAULT_NETWORK_NAME, NO_SIGNAL))
- }
+ assertThat(latest)
+ .isEqualTo(MobileContentDescription.Cellular(DEFAULT_NETWORK_NAME, NO_SIGNAL))
+ }
@Test
- fun contentDescription_nonInflated_levelStrings() =
- testScope.runTest {
- val latest by collectLastValue(underTest.contentDescription)
+ fun contentDescription_nonInflated_levelStrings() = runTest {
+ val latest by underTest.contentDescription.collectLastValue()
- repository.inflateSignalStrength.value = false
- repository.setAllLevels(0)
+ repository.inflateSignalStrength.setValue(false)
+ repository.setAllLevels(0)
- assertThat(latest as MobileContentDescription.Cellular)
- .isEqualTo(MobileContentDescription.Cellular(DEFAULT_NETWORK_NAME, NO_SIGNAL))
+ assertThat(latest as MobileContentDescription.Cellular)
+ .isEqualTo(MobileContentDescription.Cellular(DEFAULT_NETWORK_NAME, NO_SIGNAL))
- repository.setAllLevels(1)
+ repository.setAllLevels(1)
- assertThat(latest as MobileContentDescription.Cellular)
- .isEqualTo(MobileContentDescription.Cellular(DEFAULT_NETWORK_NAME, ONE_BAR))
+ assertThat(latest as MobileContentDescription.Cellular)
+ .isEqualTo(MobileContentDescription.Cellular(DEFAULT_NETWORK_NAME, ONE_BAR))
- repository.setAllLevels(2)
+ repository.setAllLevels(2)
- assertThat(latest as MobileContentDescription.Cellular)
- .isEqualTo(MobileContentDescription.Cellular(DEFAULT_NETWORK_NAME, TWO_BARS))
+ assertThat(latest as MobileContentDescription.Cellular)
+ .isEqualTo(MobileContentDescription.Cellular(DEFAULT_NETWORK_NAME, TWO_BARS))
- repository.setAllLevels(3)
+ repository.setAllLevels(3)
- assertThat(latest as MobileContentDescription.Cellular)
- .isEqualTo(MobileContentDescription.Cellular(DEFAULT_NETWORK_NAME, THREE_BARS))
+ assertThat(latest as MobileContentDescription.Cellular)
+ .isEqualTo(MobileContentDescription.Cellular(DEFAULT_NETWORK_NAME, THREE_BARS))
- repository.setAllLevels(4)
+ repository.setAllLevels(4)
- assertThat(latest as MobileContentDescription.Cellular)
- .isEqualTo(MobileContentDescription.Cellular(DEFAULT_NETWORK_NAME, FULL_BARS))
- }
+ assertThat(latest as MobileContentDescription.Cellular)
+ .isEqualTo(MobileContentDescription.Cellular(DEFAULT_NETWORK_NAME, FULL_BARS))
+ }
@Test
- fun contentDescription_inflated_invalidLevelUsesNoSignalText() =
- testScope.runTest {
- val latest by collectLastValue(underTest.contentDescription)
+ fun contentDescription_inflated_invalidLevelUsesNoSignalText() = runTest {
+ val latest by underTest.contentDescription.collectLastValue()
- repository.inflateSignalStrength.value = true
- repository.numberOfLevels.value = 6
+ repository.inflateSignalStrength.setValue(true)
+ repository.numberOfLevels.setValue(6)
+ repository.setAllLevels(-2)
- repository.setAllLevels(-2)
+ assertThat(latest as MobileContentDescription.Cellular)
+ .isEqualTo(MobileContentDescription.Cellular(DEFAULT_NETWORK_NAME, NO_SIGNAL))
- assertThat(latest as MobileContentDescription.Cellular)
- .isEqualTo(MobileContentDescription.Cellular(DEFAULT_NETWORK_NAME, NO_SIGNAL))
+ repository.setAllLevels(100)
- repository.setAllLevels(100)
-
- assertThat(latest as MobileContentDescription.Cellular)
- .isEqualTo(MobileContentDescription.Cellular(DEFAULT_NETWORK_NAME, NO_SIGNAL))
- }
+ assertThat(latest as MobileContentDescription.Cellular)
+ .isEqualTo(MobileContentDescription.Cellular(DEFAULT_NETWORK_NAME, NO_SIGNAL))
+ }
@Test
- fun contentDescription_inflated_levelStrings() =
- testScope.runTest {
- val latest by collectLastValue(underTest.contentDescription)
+ fun contentDescription_inflated_levelStrings() = runTest {
+ val latest by underTest.contentDescription.collectLastValue()
- repository.inflateSignalStrength.value = true
- repository.numberOfLevels.value = 6
+ repository.inflateSignalStrength.setValue(true)
+ repository.numberOfLevels.setValue(6)
- // Note that the _repo_ level is 1 lower than the reported level through the interactor
+ // Note that the _repo_ level is 1 lower than the reported level through the interactor
- repository.setAllLevels(0)
+ repository.setAllLevels(0)
- assertThat(latest as MobileContentDescription.Cellular)
- .isEqualTo(MobileContentDescription.Cellular(DEFAULT_NETWORK_NAME, ONE_BAR))
+ assertThat(latest)
+ .isEqualTo(MobileContentDescription.Cellular(DEFAULT_NETWORK_NAME, ONE_BAR))
- repository.setAllLevels(1)
+ repository.setAllLevels(1)
- assertThat(latest as MobileContentDescription.Cellular)
- .isEqualTo(MobileContentDescription.Cellular(DEFAULT_NETWORK_NAME, TWO_BARS))
+ assertThat(latest)
+ .isEqualTo(MobileContentDescription.Cellular(DEFAULT_NETWORK_NAME, TWO_BARS))
- repository.setAllLevels(2)
+ repository.setAllLevels(2)
- assertThat(latest as MobileContentDescription.Cellular)
- .isEqualTo(MobileContentDescription.Cellular(DEFAULT_NETWORK_NAME, THREE_BARS))
+ assertThat(latest)
+ .isEqualTo(MobileContentDescription.Cellular(DEFAULT_NETWORK_NAME, THREE_BARS))
- repository.setAllLevels(3)
+ repository.setAllLevels(3)
- assertThat(latest as MobileContentDescription.Cellular)
- .isEqualTo(MobileContentDescription.Cellular(DEFAULT_NETWORK_NAME, FOUR_BARS))
+ assertThat(latest)
+ .isEqualTo(MobileContentDescription.Cellular(DEFAULT_NETWORK_NAME, FOUR_BARS))
- repository.setAllLevels(4)
+ repository.setAllLevels(4)
- assertThat(latest as MobileContentDescription.Cellular)
- .isEqualTo(MobileContentDescription.Cellular(DEFAULT_NETWORK_NAME, FULL_BARS))
- }
+ assertThat(latest)
+ .isEqualTo(MobileContentDescription.Cellular(DEFAULT_NETWORK_NAME, FULL_BARS))
+ }
@Test
- fun contentDescription_nonInflated_testABunchOfLevelsForNull() =
- testScope.runTest {
- val latest by collectLastValue(underTest.contentDescription)
-
- repository.inflateSignalStrength.value = false
- repository.numberOfLevels.value = 5
-
- // -1 and 5 are out of the bounds for non-inflated content descriptions
- for (i in -1..5) {
- repository.setAllLevels(i)
- when (i) {
- -1,
- 5 ->
- assertWithMessage("Level $i is expected to be 'no signal'")
- .that((latest as MobileContentDescription.Cellular).levelDescriptionRes)
- .isEqualTo(NO_SIGNAL)
- else ->
- assertWithMessage("Level $i is expected not to be null")
- .that(latest)
- .isNotNull()
- }
+ fun contentDescription_nonInflated_testABunchOfLevelsForNull() = runTest {
+ val latest by underTest.contentDescription.collectLastValue()
+
+ repository.inflateSignalStrength.setValue(false)
+ repository.numberOfLevels.setValue(5)
+
+ // -1 and 5 are out of the bounds for non-inflated content descriptions
+ for (i in -1..5) {
+ repository.setAllLevels(i)
+ when (i) {
+ -1,
+ 5 ->
+ assertWithMessage("Level $i is expected to be null")
+ .that((latest as MobileContentDescription.Cellular).levelDescriptionRes)
+ .isEqualTo(NO_SIGNAL)
+ else ->
+ assertWithMessage("Level $i is expected not to be null")
+ .that(latest)
+ .isNotNull()
}
}
+ }
@Test
- fun contentDescription_inflated_testABunchOfLevelsForNull() =
- testScope.runTest {
- val latest by collectLastValue(underTest.contentDescription)
- repository.inflateSignalStrength.value = true
- repository.numberOfLevels.value = 6
- // -1 and 6 are out of the bounds for inflated content descriptions
- // Note that the interactor adds 1 to the reported level, hence the -2 to 5 range
- for (i in -2..5) {
- repository.setAllLevels(i)
- when (i) {
- -2,
- 5 ->
- assertWithMessage("Level $i is expected to be 'no signal'")
- .that((latest as MobileContentDescription.Cellular).levelDescriptionRes)
- .isEqualTo(NO_SIGNAL)
- else ->
- assertWithMessage("Level $i is not expected to be null")
- .that(latest)
- .isNotNull()
- }
+ fun contentDescription_inflated_testABunchOfLevelsForNull() = runTest {
+ val latest by underTest.contentDescription.collectLastValue()
+ repository.inflateSignalStrength.setValue(true)
+ repository.numberOfLevels.setValue(6)
+ // -1 and 6 are out of the bounds for inflated content descriptions
+ // Note that the interactor adds 1 to the reported level, hence the -2 to 5 range
+ for (i in -2..5) {
+ repository.setAllLevels(i)
+ when (i) {
+ -2,
+ 5 ->
+ assertWithMessage("Level $i is expected to be null")
+ .that((latest as MobileContentDescription.Cellular).levelDescriptionRes)
+ .isEqualTo(NO_SIGNAL)
+ else ->
+ assertWithMessage("Level $i is not expected to be null")
+ .that(latest)
+ .isNotNull()
}
}
+ }
@Test
- fun networkType_dataEnabled_groupIsRepresented() =
- testScope.runTest {
- val expected =
- Icon.Resource(
- THREE_G.dataType,
- ContentDescription.Resource(THREE_G.dataContentDescription),
- )
- connectionsRepository.mobileIsDefault.value = true
- repository.setNetworkTypeKey(connectionsRepository.GSM_KEY)
-
- var latest: Icon? = null
- val job = underTest.networkTypeIcon.onEach { latest = it }.launchIn(this)
-
- assertThat(latest).isEqualTo(expected)
-
- job.cancel()
- }
-
- @Test
- fun networkType_null_whenDisabled() =
- testScope.runTest {
- repository.setNetworkTypeKey(connectionsRepository.GSM_KEY)
- repository.setDataEnabled(false)
- connectionsRepository.mobileIsDefault.value = true
- var latest: Icon? = null
- val job = underTest.networkTypeIcon.onEach { latest = it }.launchIn(this)
+ fun networkType_dataEnabled_groupIsRepresented() = runTest {
+ val expected =
+ Icon.Resource(
+ THREE_G.dataType,
+ ContentDescription.Resource(THREE_G.dataContentDescription),
+ )
+ mobileConnectionsRepositoryKairos.fake.mobileIsDefault.setValue(true)
+ repository.setNetworkTypeKey(mobileConnectionsRepositoryKairos.fake.GSM_KEY)
- assertThat(latest).isNull()
+ val latest by underTest.networkTypeIcon.collectLastValue()
- job.cancel()
- }
+ assertThat(latest).isEqualTo(expected)
+ }
@Test
- fun networkType_null_whenCarrierNetworkChangeActive() =
- testScope.runTest {
- repository.setNetworkTypeKey(connectionsRepository.GSM_KEY)
- repository.carrierNetworkChangeActive.value = true
- connectionsRepository.mobileIsDefault.value = true
- var latest: Icon? = null
- val job = underTest.networkTypeIcon.onEach { latest = it }.launchIn(this)
-
- assertThat(latest).isNull()
+ fun networkType_null_whenDisabled() = runTest {
+ repository.setNetworkTypeKey(mobileConnectionsRepositoryKairos.fake.GSM_KEY)
+ repository.dataEnabled.setValue(false)
+ mobileConnectionsRepositoryKairos.fake.mobileIsDefault.setValue(true)
+ val latest by underTest.networkTypeIcon.collectLastValue()
- job.cancel()
- }
+ assertThat(latest).isNull()
+ }
@Test
- fun networkTypeIcon_notNull_whenEnabled() =
- testScope.runTest {
- val expected =
- Icon.Resource(
- THREE_G.dataType,
- ContentDescription.Resource(THREE_G.dataContentDescription),
- )
- repository.setNetworkTypeKey(connectionsRepository.GSM_KEY)
- repository.setDataEnabled(true)
- repository.dataConnectionState.value = DataConnectionState.Connected
- connectionsRepository.mobileIsDefault.value = true
- var latest: Icon? = null
- val job = underTest.networkTypeIcon.onEach { latest = it }.launchIn(this)
-
- assertThat(latest).isEqualTo(expected)
-
- job.cancel()
- }
+ fun networkType_null_whenCarrierNetworkChangeActive() = runTest {
+ repository.setNetworkTypeKey(mobileConnectionsRepositoryKairos.fake.GSM_KEY)
+ repository.carrierNetworkChangeActive.setValue(true)
+ mobileConnectionsRepositoryKairos.fake.mobileIsDefault.setValue(true)
+ val latest by underTest.networkTypeIcon.collectLastValue()
+
+ assertThat(latest).isNull()
+ }
@Test
- fun networkType_nullWhenDataDisconnects() =
- testScope.runTest {
- val initial =
- Icon.Resource(
- THREE_G.dataType,
- ContentDescription.Resource(THREE_G.dataContentDescription),
- )
+ fun networkTypeIcon_notNull_whenEnabled() = runTest {
+ val expected =
+ Icon.Resource(
+ THREE_G.dataType,
+ ContentDescription.Resource(THREE_G.dataContentDescription),
+ )
+ repository.setNetworkTypeKey(mobileConnectionsRepositoryKairos.fake.GSM_KEY)
+ repository.dataEnabled.setValue(true)
+ repository.dataConnectionState.setValue(DataConnectionState.Connected)
+ mobileConnectionsRepositoryKairos.fake.mobileIsDefault.setValue(true)
+ val latest by underTest.networkTypeIcon.collectLastValue()
- repository.setNetworkTypeKey(connectionsRepository.GSM_KEY)
- var latest: Icon? = null
- val job = underTest.networkTypeIcon.onEach { latest = it }.launchIn(this)
+ assertThat(latest).isEqualTo(expected)
+ }
- assertThat(latest).isEqualTo(initial)
+ @Test
+ fun networkType_nullWhenDataDisconnects() = runTest {
+ val initial =
+ Icon.Resource(
+ THREE_G.dataType,
+ ContentDescription.Resource(THREE_G.dataContentDescription),
+ )
- repository.dataConnectionState.value = DataConnectionState.Disconnected
+ repository.setNetworkTypeKey(mobileConnectionsRepositoryKairos.fake.GSM_KEY)
+ val latest by underTest.networkTypeIcon.collectLastValue()
- assertThat(latest).isNull()
+ assertThat(latest).isEqualTo(initial)
- job.cancel()
- }
+ repository.dataConnectionState.setValue(DataConnectionState.Disconnected)
- @Test
- fun networkType_null_changeToDisabled() =
- testScope.runTest {
- val expected =
- Icon.Resource(
- THREE_G.dataType,
- ContentDescription.Resource(THREE_G.dataContentDescription),
- )
- repository.dataEnabled.value = true
- var latest: Icon? = null
- val job = underTest.networkTypeIcon.onEach { latest = it }.launchIn(this)
+ assertThat(latest).isNull()
+ }
- assertThat(latest).isEqualTo(expected)
+ @Test
+ fun networkType_null_changeToDisabled() = runTest {
+ val expected =
+ Icon.Resource(
+ THREE_G.dataType,
+ ContentDescription.Resource(THREE_G.dataContentDescription),
+ )
+ repository.dataEnabled.setValue(true)
+ val latest by underTest.networkTypeIcon.collectLastValue()
- repository.dataEnabled.value = false
+ assertThat(latest).isEqualTo(expected)
- assertThat(latest).isNull()
+ repository.dataEnabled.setValue(false)
- job.cancel()
- }
+ assertThat(latest).isNull()
+ }
@Test
- fun networkType_alwaysShow_shownEvenWhenDisabled() =
- testScope.runTest {
- repository.dataEnabled.value = false
-
- connectionsRepository.defaultDataSubRatConfig.value =
- MobileMappings.Config().also { it.alwaysShowDataRatIcon = true }
+ fun networkType_alwaysShow_shownEvenWhenDisabled() = runTest {
+ repository.dataEnabled.setValue(false)
- var latest: Icon? = null
- val job = underTest.networkTypeIcon.onEach { latest = it }.launchIn(this)
+ mobileConnectionsRepositoryKairos.fake.defaultDataSubRatConfig.setValue(
+ MobileMappings.Config().also { it.alwaysShowDataRatIcon = true }
+ )
- val expected =
- Icon.Resource(
- THREE_G.dataType,
- ContentDescription.Resource(THREE_G.dataContentDescription),
- )
- assertThat(latest).isEqualTo(expected)
+ val latest by underTest.networkTypeIcon.collectLastValue()
- job.cancel()
- }
+ val expected =
+ Icon.Resource(
+ THREE_G.dataType,
+ ContentDescription.Resource(THREE_G.dataContentDescription),
+ )
+ assertThat(latest).isEqualTo(expected)
+ }
@Test
- fun networkType_alwaysShow_shownEvenWhenDisconnected() =
- testScope.runTest {
- repository.setNetworkTypeKey(connectionsRepository.GSM_KEY)
- repository.dataConnectionState.value = DataConnectionState.Disconnected
+ fun networkType_alwaysShow_shownEvenWhenDisconnected() = runTest {
+ repository.setNetworkTypeKey(mobileConnectionsRepositoryKairos.fake.GSM_KEY)
+ repository.dataConnectionState.setValue(DataConnectionState.Disconnected)
- connectionsRepository.defaultDataSubRatConfig.value =
- MobileMappings.Config().also { it.alwaysShowDataRatIcon = true }
+ mobileConnectionsRepositoryKairos.fake.defaultDataSubRatConfig.setValue(
+ MobileMappings.Config().also { it.alwaysShowDataRatIcon = true }
+ )
- var latest: Icon? = null
- val job = underTest.networkTypeIcon.onEach { latest = it }.launchIn(this)
+ val latest by underTest.networkTypeIcon.collectLastValue()
- val expected =
- Icon.Resource(
- THREE_G.dataType,
- ContentDescription.Resource(THREE_G.dataContentDescription),
- )
- assertThat(latest).isEqualTo(expected)
-
- job.cancel()
- }
+ val expected =
+ Icon.Resource(
+ THREE_G.dataType,
+ ContentDescription.Resource(THREE_G.dataContentDescription),
+ )
+ assertThat(latest).isEqualTo(expected)
+ }
@Test
- fun networkType_alwaysShow_shownEvenWhenFailedConnection() =
- testScope.runTest {
- repository.setNetworkTypeKey(connectionsRepository.GSM_KEY)
- connectionsRepository.mobileIsDefault.value = true
- connectionsRepository.defaultDataSubRatConfig.value =
- MobileMappings.Config().also { it.alwaysShowDataRatIcon = true }
-
- var latest: Icon? = null
- val job = underTest.networkTypeIcon.onEach { latest = it }.launchIn(this)
-
- val expected =
- Icon.Resource(
- THREE_G.dataType,
- ContentDescription.Resource(THREE_G.dataContentDescription),
- )
- assertThat(latest).isEqualTo(expected)
-
- job.cancel()
- }
+ fun networkType_alwaysShow_shownEvenWhenFailedConnection() = runTest {
+ repository.setNetworkTypeKey(mobileConnectionsRepositoryKairos.fake.GSM_KEY)
+ mobileConnectionsRepositoryKairos.fake.mobileIsDefault.setValue(true)
+ mobileConnectionsRepositoryKairos.fake.defaultDataSubRatConfig.setValue(
+ MobileMappings.Config().also { it.alwaysShowDataRatIcon = true }
+ )
- @Test
- fun networkType_alwaysShow_usesDefaultIconWhenInvalid() =
- testScope.runTest {
- // The UNKNOWN icon group doesn't have a valid data type icon ID, and the logic from the
- // old pipeline was to use the default icon group if the map doesn't exist
- repository.setNetworkTypeKey(UNKNOWN.name)
- connectionsRepository.defaultDataSubRatConfig.value =
- MobileMappings.Config().also { it.alwaysShowDataRatIcon = true }
-
- var latest: Icon? = null
- val job = underTest.networkTypeIcon.onEach { latest = it }.launchIn(this)
-
- val expected =
- Icon.Resource(
- connectionsRepository.defaultMobileIconGroup.value.dataType,
- ContentDescription.Resource(G.dataContentDescription),
- )
-
- assertThat(latest).isEqualTo(expected)
-
- job.cancel()
- }
+ val latest by underTest.networkTypeIcon.collectLastValue()
- @Test
- fun networkType_alwaysShow_shownWhenNotDefault() =
- testScope.runTest {
- repository.setNetworkTypeKey(connectionsRepository.GSM_KEY)
- connectionsRepository.mobileIsDefault.value = false
- connectionsRepository.defaultDataSubRatConfig.value =
- MobileMappings.Config().also { it.alwaysShowDataRatIcon = true }
-
- var latest: Icon? = null
- val job = underTest.networkTypeIcon.onEach { latest = it }.launchIn(this)
-
- val expected =
- Icon.Resource(
- THREE_G.dataType,
- ContentDescription.Resource(THREE_G.dataContentDescription),
- )
- assertThat(latest).isEqualTo(expected)
-
- job.cancel()
- }
+ val expected =
+ Icon.Resource(
+ THREE_G.dataType,
+ ContentDescription.Resource(THREE_G.dataContentDescription),
+ )
+ assertThat(latest).isEqualTo(expected)
+ }
@Test
- fun networkType_notShownWhenNotDefault() =
- testScope.runTest {
- repository.setNetworkTypeKey(connectionsRepository.GSM_KEY)
- repository.dataConnectionState.value = DataConnectionState.Connected
- connectionsRepository.mobileIsDefault.value = false
+ fun networkType_alwaysShow_usesDefaultIconWhenInvalid() = runTest {
+ // The UNKNOWN icon group doesn't have a valid data type icon ID, and the logic from the
+ // old pipeline was to use the default icon group if the map doesn't exist
+ repository.setNetworkTypeKey(UNKNOWN.name)
+ mobileConnectionsRepositoryKairos.fake.defaultDataSubRatConfig.setValue(
+ MobileMappings.Config().also { it.alwaysShowDataRatIcon = true }
+ )
- var latest: Icon? = null
- val job = underTest.networkTypeIcon.onEach { latest = it }.launchIn(this)
+ val latest by underTest.networkTypeIcon.collectLastValue()
- assertThat(latest).isNull()
+ val expected =
+ Icon.Resource(
+ kairos.transact {
+ mobileConnectionsRepositoryKairos.fake.defaultMobileIconGroup.sample().dataType
+ },
+ ContentDescription.Resource(G.dataContentDescription),
+ )
- job.cancel()
- }
+ assertThat(latest).isEqualTo(expected)
+ }
@Test
- fun roaming() =
- testScope.runTest {
- repository.setAllRoaming(true)
+ fun networkType_alwaysShow_shownWhenNotDefault() = runTest {
+ repository.setNetworkTypeKey(mobileConnectionsRepositoryKairos.fake.GSM_KEY)
+ mobileConnectionsRepositoryKairos.fake.mobileIsDefault.setValue(false)
+ mobileConnectionsRepositoryKairos.fake.defaultDataSubRatConfig.setValue(
+ MobileMappings.Config().also { it.alwaysShowDataRatIcon = true }
+ )
- var latest: Boolean? = null
- val job = underTest.roaming.onEach { latest = it }.launchIn(this)
+ val latest by underTest.networkTypeIcon.collectLastValue()
- assertThat(latest).isTrue()
+ val expected =
+ Icon.Resource(
+ THREE_G.dataType,
+ ContentDescription.Resource(THREE_G.dataContentDescription),
+ )
+ assertThat(latest).isEqualTo(expected)
+ }
- repository.setAllRoaming(false)
+ @Test
+ fun networkType_notShownWhenNotDefault() = runTest {
+ repository.setNetworkTypeKey(mobileConnectionsRepositoryKairos.fake.GSM_KEY)
+ repository.dataConnectionState.setValue(DataConnectionState.Connected)
+ mobileConnectionsRepositoryKairos.fake.mobileIsDefault.setValue(false)
- assertThat(latest).isFalse()
+ val latest by underTest.networkTypeIcon.collectLastValue()
- job.cancel()
- }
+ assertThat(latest).isNull()
+ }
@Test
- fun dataActivity_nullWhenConfigIsOff() =
- testScope.runTest {
- // Create a new view model here so the constants are properly read
- whenever(constants.shouldShowActivityConfig).thenReturn(false)
- createAndSetViewModel()
+ fun roaming() = runTest {
+ repository.setAllRoaming(true)
- var inVisible: Boolean? = null
- val inJob = underTest.activityInVisible.onEach { inVisible = it }.launchIn(this)
+ val latest by underTest.roaming.collectLastValue()
- var outVisible: Boolean? = null
- val outJob = underTest.activityInVisible.onEach { outVisible = it }.launchIn(this)
+ assertThat(latest).isTrue()
- var containerVisible: Boolean? = null
- val containerJob =
- underTest.activityInVisible.onEach { containerVisible = it }.launchIn(this)
+ repository.setAllRoaming(false)
- repository.dataActivityDirection.value =
- DataActivityModel(hasActivityIn = true, hasActivityOut = true)
+ assertThat(latest).isFalse()
+ }
- assertThat(inVisible).isFalse()
- assertThat(outVisible).isFalse()
- assertThat(containerVisible).isFalse()
+ @Test
+ fun dataActivity_nullWhenConfigIsOff() = runTest {
+ constants.stub { on { shouldShowActivityConfig } doReturn false }
- inJob.cancel()
- outJob.cancel()
- containerJob.cancel()
- }
+ val inVisible by underTest.activityInVisible.collectLastValue()
+
+ val outVisible by underTest.activityInVisible.collectLastValue()
+
+ val containerVisible by underTest.activityInVisible.collectLastValue()
+
+ repository.dataActivityDirection.setValue(
+ DataActivityModel(hasActivityIn = true, hasActivityOut = true)
+ )
+
+ assertThat(inVisible).isFalse()
+ assertThat(outVisible).isFalse()
+ assertThat(containerVisible).isFalse()
+ }
@Test
@DisableFlags(FLAG_STATUS_BAR_STATIC_INOUT_INDICATORS)
- fun dataActivity_configOn_testIndicators_staticFlagOff() =
- testScope.runTest {
- // Create a new view model here so the constants are properly read
- whenever(constants.shouldShowActivityConfig).thenReturn(true)
- createAndSetViewModel()
+ fun dataActivity_configOn_testIndicators_staticFlagOff() = runTest {
+ constants.stub { on { shouldShowActivityConfig } doReturn true }
- var inVisible: Boolean? = null
- val inJob = underTest.activityInVisible.onEach { inVisible = it }.launchIn(this)
+ val inVisible by underTest.activityInVisible.collectLastValue()
- var outVisible: Boolean? = null
- val outJob = underTest.activityOutVisible.onEach { outVisible = it }.launchIn(this)
+ val outVisible by underTest.activityOutVisible.collectLastValue()
- var containerVisible: Boolean? = null
- val containerJob =
- underTest.activityContainerVisible.onEach { containerVisible = it }.launchIn(this)
+ val containerVisible by underTest.activityContainerVisible.collectLastValue()
- repository.dataActivityDirection.value =
- DataActivityModel(hasActivityIn = true, hasActivityOut = false)
+ repository.dataActivityDirection.setValue(
+ DataActivityModel(hasActivityIn = true, hasActivityOut = false)
+ )
- yield()
+ yield()
- assertThat(inVisible).isTrue()
- assertThat(outVisible).isFalse()
- assertThat(containerVisible).isTrue()
+ assertThat(inVisible).isTrue()
+ assertThat(outVisible).isFalse()
+ assertThat(containerVisible).isTrue()
- repository.dataActivityDirection.value =
- DataActivityModel(hasActivityIn = false, hasActivityOut = true)
+ repository.dataActivityDirection.setValue(
+ DataActivityModel(hasActivityIn = false, hasActivityOut = true)
+ )
- assertThat(inVisible).isFalse()
- assertThat(outVisible).isTrue()
- assertThat(containerVisible).isTrue()
+ assertThat(inVisible).isFalse()
+ assertThat(outVisible).isTrue()
+ assertThat(containerVisible).isTrue()
- repository.dataActivityDirection.value =
- DataActivityModel(hasActivityIn = false, hasActivityOut = false)
+ repository.dataActivityDirection.setValue(
+ DataActivityModel(hasActivityIn = false, hasActivityOut = false)
+ )
- assertThat(inVisible).isFalse()
- assertThat(outVisible).isFalse()
- assertThat(containerVisible).isFalse()
-
- inJob.cancel()
- outJob.cancel()
- containerJob.cancel()
- }
+ assertThat(inVisible).isFalse()
+ assertThat(outVisible).isFalse()
+ assertThat(containerVisible).isFalse()
+ }
@Test
@EnableFlags(FLAG_STATUS_BAR_STATIC_INOUT_INDICATORS)
- fun dataActivity_configOn_testIndicators_staticFlagOn() =
- testScope.runTest {
- // Create a new view model here so the constants are properly read
- whenever(constants.shouldShowActivityConfig).thenReturn(true)
- createAndSetViewModel()
-
- var inVisible: Boolean? = null
- val inJob = underTest.activityInVisible.onEach { inVisible = it }.launchIn(this)
+ fun dataActivity_configOn_testIndicators_staticFlagOn() = runTest {
+ constants.stub { on { shouldShowActivityConfig } doReturn true }
- var outVisible: Boolean? = null
- val outJob = underTest.activityOutVisible.onEach { outVisible = it }.launchIn(this)
+ val inVisible by underTest.activityInVisible.collectLastValue()
- var containerVisible: Boolean? = null
- val containerJob =
- underTest.activityContainerVisible.onEach { containerVisible = it }.launchIn(this)
+ val outVisible by underTest.activityOutVisible.collectLastValue()
- repository.dataActivityDirection.value =
- DataActivityModel(hasActivityIn = true, hasActivityOut = false)
+ val containerVisible by underTest.activityContainerVisible.collectLastValue()
- yield()
+ repository.dataActivityDirection.setValue(
+ DataActivityModel(hasActivityIn = true, hasActivityOut = false)
+ )
- assertThat(inVisible).isTrue()
- assertThat(outVisible).isFalse()
- assertThat(containerVisible).isTrue()
+ yield()
- repository.dataActivityDirection.value =
- DataActivityModel(hasActivityIn = false, hasActivityOut = true)
+ assertThat(inVisible).isTrue()
+ assertThat(outVisible).isFalse()
+ assertThat(containerVisible).isTrue()
- assertThat(inVisible).isFalse()
- assertThat(outVisible).isTrue()
- assertThat(containerVisible).isTrue()
+ repository.dataActivityDirection.setValue(
+ DataActivityModel(hasActivityIn = false, hasActivityOut = true)
+ )
- repository.dataActivityDirection.value =
- DataActivityModel(hasActivityIn = false, hasActivityOut = false)
+ assertThat(inVisible).isFalse()
+ assertThat(outVisible).isTrue()
+ assertThat(containerVisible).isTrue()
- assertThat(inVisible).isFalse()
- assertThat(outVisible).isFalse()
- assertThat(containerVisible).isTrue()
+ repository.dataActivityDirection.setValue(
+ DataActivityModel(hasActivityIn = false, hasActivityOut = false)
+ )
- inJob.cancel()
- outJob.cancel()
- containerJob.cancel()
- }
+ assertThat(inVisible).isFalse()
+ assertThat(outVisible).isFalse()
+ assertThat(containerVisible).isTrue()
+ }
@Test
- fun netTypeBackground_nullWhenNoPrioritizedCapabilities() =
- testScope.runTest {
- createAndSetViewModel()
-
- val latest by collectLastValue(underTest.networkTypeBackground)
+ fun netTypeBackground_nullWhenNoPrioritizedCapabilities() = runTest {
+ val latest by underTest.networkTypeBackground.collectLastValue()
- repository.hasPrioritizedNetworkCapabilities.value = false
+ repository.hasPrioritizedNetworkCapabilities.setValue(false)
- assertThat(latest).isNull()
- }
+ assertThat(latest).isNull()
+ }
@Test
@EnableFlags(NewStatusBarIcons.FLAG_NAME, StatusBarRootModernization.FLAG_NAME)
- fun netTypeBackground_sliceUiEnabled_notNullWhenPrioritizedCapabilities_newIcons() =
- testScope.runTest {
- createAndSetViewModel()
+ fun netTypeBackground_sliceUiEnabled_notNullWhenPrioritizedCapabilities_newIcons() = runTest {
+ val latest by underTest.networkTypeBackground.collectLastValue()
- val latest by collectLastValue(underTest.networkTypeBackground)
+ repository.hasPrioritizedNetworkCapabilities.setValue(true)
- repository.hasPrioritizedNetworkCapabilities.value = true
-
- assertThat(latest)
- .isEqualTo(Icon.Resource(R.drawable.mobile_network_type_background_updated, null))
- }
+ assertThat(latest)
+ .isEqualTo(Icon.Resource(R.drawable.mobile_network_type_background_updated, null))
+ }
@Test
@DisableFlags(NewStatusBarIcons.FLAG_NAME, StatusBarRootModernization.FLAG_NAME)
- fun netTypeBackground_sliceUiDisabled_notNullWhenPrioritizedCapabilities_oldIcons() =
- testScope.runTest {
- createAndSetViewModel()
-
- val latest by collectLastValue(underTest.networkTypeBackground)
+ fun netTypeBackground_sliceUiDisabled_notNullWhenPrioritizedCapabilities_oldIcons() = runTest {
+ val latest by underTest.networkTypeBackground.collectLastValue()
- repository.hasPrioritizedNetworkCapabilities.value = true
+ repository.allowNetworkSliceIndicator.setValue(true)
+ repository.hasPrioritizedNetworkCapabilities.setValue(true)
- assertThat(latest)
- .isEqualTo(Icon.Resource(R.drawable.mobile_network_type_background, null))
- }
+ assertThat(latest).isEqualTo(Icon.Resource(R.drawable.mobile_network_type_background, null))
+ }
@Test
- fun nonTerrestrial_defaultProperties() =
- testScope.runTest {
- repository.isNonTerrestrial.value = true
-
- val roaming by collectLastValue(underTest.roaming)
- val networkTypeIcon by collectLastValue(underTest.networkTypeIcon)
- val networkTypeBackground by collectLastValue(underTest.networkTypeBackground)
- val activityInVisible by collectLastValue(underTest.activityInVisible)
- val activityOutVisible by collectLastValue(underTest.activityOutVisible)
- val activityContainerVisible by collectLastValue(underTest.activityContainerVisible)
-
- assertThat(roaming).isFalse()
- assertThat(networkTypeIcon).isNull()
- assertThat(networkTypeBackground).isNull()
- assertThat(activityInVisible).isFalse()
- assertThat(activityOutVisible).isFalse()
- assertThat(activityContainerVisible).isFalse()
- }
+ fun nonTerrestrial_defaultProperties() = runTest {
+ repository.isNonTerrestrial.setValue(true)
+
+ val roaming by underTest.roaming.collectLastValue()
+ val networkTypeIcon by underTest.networkTypeIcon.collectLastValue()
+ val networkTypeBackground by underTest.networkTypeBackground.collectLastValue()
+ val activityInVisible by underTest.activityInVisible.collectLastValue()
+ val activityOutVisible by underTest.activityOutVisible.collectLastValue()
+ val activityContainerVisible by underTest.activityContainerVisible.collectLastValue()
+
+ assertThat(roaming).isFalse()
+ assertThat(networkTypeIcon).isNull()
+ assertThat(networkTypeBackground).isNull()
+ assertThat(activityInVisible).isFalse()
+ assertThat(activityOutVisible).isFalse()
+ assertThat(activityContainerVisible).isFalse()
+ }
@Test
- fun nonTerrestrial_ignoresDefaultProperties() =
- testScope.runTest {
- repository.isNonTerrestrial.value = true
-
- val roaming by collectLastValue(underTest.roaming)
- val networkTypeIcon by collectLastValue(underTest.networkTypeIcon)
- val networkTypeBackground by collectLastValue(underTest.networkTypeBackground)
- val activityInVisible by collectLastValue(underTest.activityInVisible)
- val activityOutVisible by collectLastValue(underTest.activityOutVisible)
- val activityContainerVisible by collectLastValue(underTest.activityContainerVisible)
-
- repository.setAllRoaming(true)
- repository.setNetworkTypeKey(connectionsRepository.LTE_KEY)
- // sets the background on cellular
- repository.hasPrioritizedNetworkCapabilities.value = true
- repository.dataActivityDirection.value =
- DataActivityModel(hasActivityIn = true, hasActivityOut = true)
-
- assertThat(roaming).isFalse()
- assertThat(networkTypeIcon).isNull()
- assertThat(networkTypeBackground).isNull()
- assertThat(activityInVisible).isFalse()
- assertThat(activityOutVisible).isFalse()
- assertThat(activityContainerVisible).isFalse()
- }
+ fun nonTerrestrial_ignoresDefaultProperties() = runTest {
+ repository.isNonTerrestrial.setValue(true)
+
+ val roaming by underTest.roaming.collectLastValue()
+ val networkTypeIcon by underTest.networkTypeIcon.collectLastValue()
+ val networkTypeBackground by underTest.networkTypeBackground.collectLastValue()
+ val activityInVisible by underTest.activityInVisible.collectLastValue()
+ val activityOutVisible by underTest.activityOutVisible.collectLastValue()
+ val activityContainerVisible by underTest.activityContainerVisible.collectLastValue()
+
+ repository.setAllRoaming(true)
+ repository.setNetworkTypeKey(mobileConnectionsRepositoryKairos.fake.LTE_KEY)
+ // sets the background on cellular
+ repository.hasPrioritizedNetworkCapabilities.setValue(true)
+ repository.dataActivityDirection.setValue(
+ DataActivityModel(hasActivityIn = true, hasActivityOut = true)
+ )
+
+ assertThat(roaming).isFalse()
+ assertThat(networkTypeIcon).isNull()
+ assertThat(networkTypeBackground).isNull()
+ assertThat(activityInVisible).isFalse()
+ assertThat(activityOutVisible).isFalse()
+ assertThat(activityContainerVisible).isFalse()
+ }
@DisableFlags(com.android.internal.telephony.flags.Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN)
@Test
- fun nonTerrestrial_usesSatelliteIcon_flagOff() =
- testScope.runTest {
- repository.isNonTerrestrial.value = true
- repository.setAllLevels(0)
- repository.satelliteLevel.value = 0
-
- val latest by
- collectLastValue(underTest.icon.filterIsInstance(SignalIconModel.Satellite::class))
-
- // Level 0 -> no connection
- assertThat(latest).isNotNull()
- assertThat(latest!!.icon.res).isEqualTo(R.drawable.ic_satellite_connected_0)
-
- // 1-2 -> 1 bar
- repository.setAllLevels(1)
- repository.satelliteLevel.value = 1
- assertThat(latest!!.icon.res).isEqualTo(R.drawable.ic_satellite_connected_1)
-
- repository.setAllLevels(2)
- repository.satelliteLevel.value = 2
- assertThat(latest!!.icon.res).isEqualTo(R.drawable.ic_satellite_connected_1)
-
- // 3-4 -> 2 bars
- repository.setAllLevels(3)
- repository.satelliteLevel.value = 3
- assertThat(latest!!.icon.res).isEqualTo(R.drawable.ic_satellite_connected_2)
-
- repository.setAllLevels(4)
- repository.satelliteLevel.value = 4
- assertThat(latest!!.icon.res).isEqualTo(R.drawable.ic_satellite_connected_2)
- }
+ fun nonTerrestrial_usesSatelliteIcon_flagOff() = runTest {
+ repository.isNonTerrestrial.setValue(true)
+ repository.setAllLevels(0)
+ repository.satelliteLevel.setValue(0)
+
+ val latest by underTest.icon.map { it as SignalIconModel.Satellite }.collectLastValue()
+
+ // Level 0 -> no connection
+ assertThat(latest).isNotNull()
+ assertThat(latest!!.icon.res).isEqualTo(R.drawable.ic_satellite_connected_0)
+
+ // 1-2 -> 1 bar
+ repository.setAllLevels(1)
+ repository.satelliteLevel.setValue(1)
+ assertThat(latest!!.icon.res).isEqualTo(R.drawable.ic_satellite_connected_1)
+
+ repository.setAllLevels(2)
+ repository.satelliteLevel.setValue(2)
+ assertThat(latest!!.icon.res).isEqualTo(R.drawable.ic_satellite_connected_1)
+
+ // 3-4 -> 2 bars
+ repository.setAllLevels(3)
+ repository.satelliteLevel.setValue(3)
+ assertThat(latest!!.icon.res).isEqualTo(R.drawable.ic_satellite_connected_2)
+
+ repository.setAllLevels(4)
+ repository.satelliteLevel.setValue(4)
+ assertThat(latest!!.icon.res).isEqualTo(R.drawable.ic_satellite_connected_2)
+ }
@EnableFlags(com.android.internal.telephony.flags.Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN)
@Test
- fun nonTerrestrial_usesSatelliteIcon_flagOn() =
- testScope.runTest {
- repository.isNonTerrestrial.value = true
- repository.satelliteLevel.value = 0
+ fun nonTerrestrial_usesSatelliteIcon_flagOn() = runTest {
+ repository.isNonTerrestrial.setValue(true)
+ repository.satelliteLevel.setValue(0)
- val latest by
- collectLastValue(underTest.icon.filterIsInstance(SignalIconModel.Satellite::class))
+ val latest by underTest.icon.map { it as SignalIconModel.Satellite }.collectLastValue()
- // Level 0 -> no connection
- assertThat(latest).isNotNull()
- assertThat(latest!!.icon.res).isEqualTo(R.drawable.ic_satellite_connected_0)
+ // Level 0 -> no connection
+ assertThat(latest).isNotNull()
+ assertThat(latest!!.icon.res).isEqualTo(R.drawable.ic_satellite_connected_0)
- // 1-2 -> 1 bar
- repository.satelliteLevel.value = 1
- assertThat(latest!!.icon.res).isEqualTo(R.drawable.ic_satellite_connected_1)
+ // 1-2 -> 1 bar
+ repository.satelliteLevel.setValue(1)
+ assertThat(latest!!.icon.res).isEqualTo(R.drawable.ic_satellite_connected_1)
- repository.satelliteLevel.value = 2
- assertThat(latest!!.icon.res).isEqualTo(R.drawable.ic_satellite_connected_1)
+ repository.satelliteLevel.setValue(2)
+ assertThat(latest!!.icon.res).isEqualTo(R.drawable.ic_satellite_connected_1)
- // 3-4 -> 2 bars
- repository.satelliteLevel.value = 3
- assertThat(latest!!.icon.res).isEqualTo(R.drawable.ic_satellite_connected_2)
+ // 3-4 -> 2 bars
+ repository.satelliteLevel.setValue(3)
+ assertThat(latest!!.icon.res).isEqualTo(R.drawable.ic_satellite_connected_2)
- repository.satelliteLevel.value = 4
- assertThat(latest!!.icon.res).isEqualTo(R.drawable.ic_satellite_connected_2)
- }
+ repository.satelliteLevel.setValue(4)
+ assertThat(latest!!.icon.res).isEqualTo(R.drawable.ic_satellite_connected_2)
+ }
@DisableFlags(com.android.internal.telephony.flags.Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN)
@Test
- fun satelliteIcon_ignoresInflateSignalStrength_flagOff() =
- testScope.runTest {
- // Note that this is the exact same test as above, but with inflateSignalStrength set to
- // true we note that the level is unaffected by inflation
- repository.inflateSignalStrength.value = true
- repository.isNonTerrestrial.value = true
- repository.setAllLevels(0)
- repository.satelliteLevel.value = 0
-
- val latest by
- collectLastValue(underTest.icon.filterIsInstance(SignalIconModel.Satellite::class))
-
- // Level 0 -> no connection
- assertThat(latest).isNotNull()
- assertThat(latest!!.icon.res).isEqualTo(R.drawable.ic_satellite_connected_0)
-
- // 1-2 -> 1 bar
- repository.setAllLevels(1)
- repository.satelliteLevel.value = 1
- assertThat(latest!!.icon.res).isEqualTo(R.drawable.ic_satellite_connected_1)
-
- repository.setAllLevels(2)
- repository.satelliteLevel.value = 2
- assertThat(latest!!.icon.res).isEqualTo(R.drawable.ic_satellite_connected_1)
-
- // 3-4 -> 2 bars
- repository.setAllLevels(3)
- repository.satelliteLevel.value = 3
- assertThat(latest!!.icon.res).isEqualTo(R.drawable.ic_satellite_connected_2)
-
- repository.setAllLevels(4)
- repository.satelliteLevel.value = 4
- assertThat(latest!!.icon.res).isEqualTo(R.drawable.ic_satellite_connected_2)
- }
+ fun satelliteIcon_ignoresInflateSignalStrength_flagOff() = runTest {
+ // Note that this is the exact same test as above, but with inflateSignalStrength set to
+ // true we note that the level is unaffected by inflation
+ repository.inflateSignalStrength.setValue(true)
+ repository.isNonTerrestrial.setValue(true)
+ repository.setAllLevels(0)
+ repository.satelliteLevel.setValue(0)
+
+ val latest by underTest.icon.map { it as SignalIconModel.Satellite }.collectLastValue()
+
+ // Level 0 -> no connection
+ assertThat(latest).isNotNull()
+ assertThat(latest!!.icon.res).isEqualTo(R.drawable.ic_satellite_connected_0)
+
+ // 1-2 -> 1 bar
+ repository.setAllLevels(1)
+ repository.satelliteLevel.setValue(1)
+ assertThat(latest!!.icon.res).isEqualTo(R.drawable.ic_satellite_connected_1)
+
+ repository.setAllLevels(2)
+ repository.satelliteLevel.setValue(2)
+ assertThat(latest!!.icon.res).isEqualTo(R.drawable.ic_satellite_connected_1)
+
+ // 3-4 -> 2 bars
+ repository.setAllLevels(3)
+ repository.satelliteLevel.setValue(3)
+ assertThat(latest!!.icon.res).isEqualTo(R.drawable.ic_satellite_connected_2)
+
+ repository.setAllLevels(4)
+ repository.satelliteLevel.setValue(4)
+ assertThat(latest!!.icon.res).isEqualTo(R.drawable.ic_satellite_connected_2)
+ }
@EnableFlags(com.android.internal.telephony.flags.Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN)
@Test
- fun satelliteIcon_ignoresInflateSignalStrength_flagOn() =
- testScope.runTest {
- // Note that this is the exact same test as above, but with inflateSignalStrength set to
- // true we note that the level is unaffected by inflation
- repository.inflateSignalStrength.value = true
- repository.isNonTerrestrial.value = true
- repository.satelliteLevel.value = 0
-
- val latest by
- collectLastValue(underTest.icon.filterIsInstance(SignalIconModel.Satellite::class))
-
- // Level 0 -> no connection
- assertThat(latest).isNotNull()
- assertThat(latest!!.icon.res).isEqualTo(R.drawable.ic_satellite_connected_0)
-
- // 1-2 -> 1 bar
- repository.satelliteLevel.value = 1
- assertThat(latest!!.icon.res).isEqualTo(R.drawable.ic_satellite_connected_1)
-
- repository.satelliteLevel.value = 2
- assertThat(latest!!.icon.res).isEqualTo(R.drawable.ic_satellite_connected_1)
-
- // 3-4 -> 2 bars
- repository.satelliteLevel.value = 3
- assertThat(latest!!.icon.res).isEqualTo(R.drawable.ic_satellite_connected_2)
-
- repository.satelliteLevel.value = 4
- assertThat(latest!!.icon.res).isEqualTo(R.drawable.ic_satellite_connected_2)
- }
+ fun satelliteIcon_ignoresInflateSignalStrength_flagOn() = runTest {
+ // Note that this is the exact same test as above, but with inflateSignalStrength set to
+ // true we note that the level is unaffected by inflation
+ repository.inflateSignalStrength.setValue(true)
+ repository.isNonTerrestrial.setValue(true)
+ repository.satelliteLevel.setValue(0)
- private fun createAndSetViewModel() {
- underTest =
- MobileIconViewModelKairos(
- SUB_1_ID,
- interactor,
- airplaneModeInteractor,
- constants,
- testScope.backgroundScope,
- )
+ val latest by underTest.icon.map { it as SignalIconModel.Satellite }.collectLastValue()
+
+ // Level 0 -> no connection
+ assertThat(latest).isNotNull()
+ assertThat(latest!!.icon.res).isEqualTo(R.drawable.ic_satellite_connected_0)
+
+ // 1-2 -> 1 bar
+ repository.satelliteLevel.setValue(1)
+ assertThat(latest!!.icon.res).isEqualTo(R.drawable.ic_satellite_connected_1)
+
+ repository.satelliteLevel.setValue(2)
+ assertThat(latest!!.icon.res).isEqualTo(R.drawable.ic_satellite_connected_1)
+
+ // 3-4 -> 2 bars
+ repository.satelliteLevel.setValue(3)
+ assertThat(latest!!.icon.res).isEqualTo(R.drawable.ic_satellite_connected_2)
+
+ repository.satelliteLevel.setValue(4)
+ assertThat(latest!!.icon.res).isEqualTo(R.drawable.ic_satellite_connected_2)
}
companion object {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModelKairosTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModelKairosTest.kt
index e921430394c2..b11bad6f3ad3 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModelKairosTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModelKairosTest.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2022 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,348 +16,323 @@
package com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel
+import android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID
import android.telephony.SubscriptionManager.PROFILE_CLASS_UNSET
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.settingslib.mobile.TelephonyIcons
import com.android.systemui.SysuiTestCase
-import com.android.systemui.flags.FakeFeatureFlagsClassic
-import com.android.systemui.statusbar.phone.StatusBarLocation
-import com.android.systemui.statusbar.pipeline.airplane.data.repository.FakeAirplaneModeRepository
-import com.android.systemui.statusbar.pipeline.airplane.domain.interactor.AirplaneModeInteractor
+import com.android.systemui.flags.Flags
+import com.android.systemui.flags.fake
+import com.android.systemui.flags.featureFlagsClassic
+import com.android.systemui.kairos.ExperimentalKairosApi
+import com.android.systemui.kairos.KairosTestScope
+import com.android.systemui.kairos.runKairosTest
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.useUnconfinedTestDispatcher
+import com.android.systemui.statusbar.pipeline.mobile.data.model.DataConnectionState
+import com.android.systemui.statusbar.pipeline.mobile.data.model.ResolvedNetworkType.DefaultNetworkType
+import com.android.systemui.statusbar.pipeline.mobile.data.model.ResolvedNetworkType.UnknownNetworkType
import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel
-import com.android.systemui.statusbar.pipeline.mobile.data.repository.fakeMobileConnectionsRepository
-import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.FakeMobileIconsInteractor
-import com.android.systemui.statusbar.pipeline.mobile.domain.model.NetworkTypeIconModel
-import com.android.systemui.statusbar.pipeline.mobile.ui.MobileViewLogger
-import com.android.systemui.statusbar.pipeline.mobile.ui.VerboseMobileViewLogger
-import com.android.systemui.statusbar.pipeline.mobile.util.FakeMobileMappingsProxy
-import com.android.systemui.statusbar.pipeline.shared.ConnectivityConstants
-import com.android.systemui.statusbar.pipeline.shared.data.repository.FakeConnectivityRepository
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.fake
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.fakeMobileConnectionsRepositoryKairos
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.mobileConnectionsRepositoryKairos
import com.android.systemui.testKosmos
-import com.android.systemui.util.mockito.mock
import com.google.common.truth.Truth.assertThat
-import junit.framework.Assert.assertFalse
-import junit.framework.Assert.assertTrue
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.flow.launchIn
-import kotlinx.coroutines.flow.onEach
-import kotlinx.coroutines.isActive
-import kotlinx.coroutines.test.TestScope
-import kotlinx.coroutines.test.UnconfinedTestDispatcher
-import kotlinx.coroutines.test.runTest
-import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
-import org.mockito.Mock
-import org.mockito.MockitoAnnotations
-@Suppress("EXPERIMENTAL_IS_NOT_ENABLED")
-@OptIn(ExperimentalCoroutinesApi::class)
+@OptIn(ExperimentalKairosApi::class)
@SmallTest
@RunWith(AndroidJUnit4::class)
class MobileIconsViewModelKairosTest : SysuiTestCase() {
- private val kosmos = testKosmos()
- private lateinit var underTest: MobileIconsViewModelKairos
- private val interactor = FakeMobileIconsInteractor(FakeMobileMappingsProxy(), mock())
- private val flags = FakeFeatureFlagsClassic()
-
- private lateinit var airplaneModeInteractor: AirplaneModeInteractor
- @Mock private lateinit var constants: ConnectivityConstants
- @Mock private lateinit var logger: MobileViewLogger
- @Mock private lateinit var verboseLogger: VerboseMobileViewLogger
-
- private val testDispatcher = UnconfinedTestDispatcher()
- private val testScope = TestScope(testDispatcher)
+ private val Kosmos.underTest
+ get() = mobileIconsViewModelKairos
+
+ private val kosmos =
+ testKosmos().apply {
+ useUnconfinedTestDispatcher()
+ featureFlagsClassic.fake.apply {
+ setDefault(Flags.FILTER_PROVISIONING_NETWORK_SUBSCRIPTIONS)
+ }
+ mobileConnectionsRepositoryKairos =
+ fakeMobileConnectionsRepositoryKairos.apply {
+ val subList = listOf(SUB_1, SUB_2)
+ setActiveMobileDataSubscriptionId(SUB_1.subscriptionId)
+ subscriptions.setValue(subList)
+ }
+ }
- @Before
- fun setUp() {
- MockitoAnnotations.initMocks(this)
+ private fun runTest(block: suspend KairosTestScope.() -> Unit) =
+ kosmos.run { runKairosTest { block() } }
+
+ private fun KairosTestScope.setSubscriptions(
+ subList: List<SubscriptionModel>,
+ activeSubId: Int = subList.getOrNull(0)?.subscriptionId ?: INVALID_SUBSCRIPTION_ID,
+ ) {
+ println("setSubscriptions: mobileConnectionsRepositoryKairos.fake.subscriptions")
+ mobileConnectionsRepositoryKairos.fake.subscriptions.setValue(subList)
+ println(
+ "setSubscriptions: mobileConnectionsRepositoryKairos.fake.setActiveMobileDataSubscriptionId"
+ )
+ mobileConnectionsRepositoryKairos.fake.setActiveMobileDataSubscriptionId(activeSubId)
+ }
- airplaneModeInteractor =
- AirplaneModeInteractor(
- FakeAirplaneModeRepository(),
- FakeConnectivityRepository(),
- kosmos.fakeMobileConnectionsRepository,
+ @Test
+ fun subscriptionIdsFlow_matchesInteractor() = runTest {
+ val latest by underTest.subscriptionIds.collectLastValue()
+ setSubscriptions(
+ listOf(
+ SubscriptionModel(
+ subscriptionId = 1,
+ isOpportunistic = false,
+ carrierName = "Carrier 1",
+ profileClass = PROFILE_CLASS_UNSET,
+ )
)
-
- underTest =
- MobileIconsViewModelKairos(
- logger,
- verboseLogger,
- interactor,
- airplaneModeInteractor,
- constants,
- testScope.backgroundScope,
+ )
+ assertThat(latest).isEqualTo(listOf(1))
+
+ setSubscriptions(
+ listOf(
+ SubscriptionModel(
+ subscriptionId = 2,
+ isOpportunistic = false,
+ carrierName = "Carrier 2",
+ profileClass = PROFILE_CLASS_UNSET,
+ ),
+ SubscriptionModel(
+ subscriptionId = 5,
+ isOpportunistic = true,
+ carrierName = "Carrier 5",
+ profileClass = PROFILE_CLASS_UNSET,
+ ),
+ SubscriptionModel(
+ subscriptionId = 7,
+ isOpportunistic = true,
+ carrierName = "Carrier 7",
+ profileClass = PROFILE_CLASS_UNSET,
+ ),
)
+ )
+ assertThat(latest).isEqualTo(listOf(2, 5, 7))
- interactor.filteredSubscriptions.value = listOf(SUB_1, SUB_2)
+ setSubscriptions(emptyList())
+ assertThat(latest).isEmpty()
}
@Test
- fun subscriptionIdsFlow_matchesInteractor() =
- testScope.runTest {
- var latest: List<Int>? = null
- val job = underTest.subscriptionIdsFlow.onEach { latest = it }.launchIn(this)
-
- interactor.filteredSubscriptions.value =
- listOf(
- SubscriptionModel(
- subscriptionId = 1,
- isOpportunistic = false,
- carrierName = "Carrier 1",
- profileClass = PROFILE_CLASS_UNSET,
- )
- )
- assertThat(latest).isEqualTo(listOf(1))
-
- interactor.filteredSubscriptions.value =
- listOf(
- SubscriptionModel(
- subscriptionId = 2,
- isOpportunistic = false,
- carrierName = "Carrier 2",
- profileClass = PROFILE_CLASS_UNSET,
- ),
- SubscriptionModel(
- subscriptionId = 5,
- isOpportunistic = true,
- carrierName = "Carrier 5",
- profileClass = PROFILE_CLASS_UNSET,
- ),
- SubscriptionModel(
- subscriptionId = 7,
- isOpportunistic = true,
- carrierName = "Carrier 7",
- profileClass = PROFILE_CLASS_UNSET,
- ),
- )
- assertThat(latest).isEqualTo(listOf(2, 5, 7))
+ fun firstMobileSubShowingNetworkTypeIcon_noSubs_false() = runTest {
+ val latest by underTest.firstMobileSubShowingNetworkTypeIcon.collectLastValue()
- interactor.filteredSubscriptions.value = emptyList()
- assertThat(latest).isEmpty()
+ setSubscriptions(emptyList())
- job.cancel()
- }
+ assertThat(latest).isEqualTo(false)
+ }
@Test
- fun caching_mobileIconViewModelIsReusedForSameSubId() =
- testScope.runTest {
- val model1 = underTest.viewModelForSub(1, StatusBarLocation.HOME)
- val model2 = underTest.viewModelForSub(1, StatusBarLocation.QS)
+ fun firstMobileSubShowingNetworkTypeIcon_oneSub_notShowingRat_false() = runTest {
+ val latest by underTest.firstMobileSubShowingNetworkTypeIcon.collectLastValue()
- assertThat(model1.commonImpl).isSameInstanceAs(model2.commonImpl)
- }
+ setSubscriptions(listOf(SUB_1))
+
+ // The unknown icon group doesn't show a RAT
+ mobileConnectionsRepositoryKairos.fake.mobileConnectionsBySubId
+ .sample()[SUB_1.subscriptionId]
+ ?.resolvedNetworkType
+ ?.setValue(UnknownNetworkType)
+
+ assertThat(latest).isFalse()
+ }
@Test
- fun caching_invalidViewModelsAreRemovedFromCacheWhenSubDisappears() =
- testScope.runTest {
- // Retrieve models to trigger caching
- val model1 = underTest.viewModelForSub(1, StatusBarLocation.HOME)
- val model2 = underTest.viewModelForSub(2, StatusBarLocation.QS)
+ fun firstMobileSubShowingNetworkTypeIcon_oneSub_showingRat_true() = runTest {
+ val latest by underTest.firstMobileSubShowingNetworkTypeIcon.collectLastValue()
+ setSubscriptions(listOf(SUB_1))
- // Both impls are cached
- assertThat(underTest.reuseCache.keys).containsExactly(1, 2)
+ mobileConnectionsRepositoryKairos.fake.mobileIsDefault.setValue(true)
- // SUB_1 is removed from the list...
- interactor.filteredSubscriptions.value = listOf(SUB_2)
+ // The 3G icon group will show a RAT
+ val repo =
+ mobileConnectionsRepositoryKairos.fake.mobileConnectionsBySubId
+ .sample()[SUB_1.subscriptionId]!!
- // ... and dropped from the cache
- assertThat(underTest.reuseCache.keys).containsExactly(2)
- }
+ repo.resolvedNetworkType.setValue(
+ DefaultNetworkType(mobileConnectionsRepositoryKairos.fake.GSM_KEY)
+ )
+ repo.dataConnectionState.setValue(DataConnectionState.Connected)
+
+ assertThat(latest).isEqualTo(true)
+ }
@Test
- fun caching_invalidatedViewModelsAreCanceled() =
- testScope.runTest {
- // Retrieve models to trigger caching
- val model1 = underTest.viewModelForSub(1, StatusBarLocation.HOME)
- val model2 = underTest.viewModelForSub(2, StatusBarLocation.QS)
+ fun firstMobileSubShowingNetworkTypeIcon_updatesAsSubUpdates() = runTest {
+ val latest by underTest.firstMobileSubShowingNetworkTypeIcon.collectLastValue()
+ setSubscriptions(listOf(SUB_1))
- var scope1 = underTest.reuseCache[1]?.second
- var scope2 = underTest.reuseCache[2]?.second
+ mobileConnectionsRepositoryKairos.fake.mobileIsDefault.setValue(true)
- // Scopes are not canceled
- assertTrue(scope1!!.isActive)
- assertTrue(scope2!!.isActive)
+ val repo =
+ mobileConnectionsRepositoryKairos.fake.mobileConnectionsBySubId
+ .sample()[SUB_1.subscriptionId]!!
- // SUB_1 is removed from the list...
- interactor.filteredSubscriptions.value = listOf(SUB_2)
+ repo.dataConnectionState.setValue(DataConnectionState.Connected)
- // scope1 is canceled
- assertFalse(scope1!!.isActive)
- assertTrue(scope2!!.isActive)
- }
+ repo.resolvedNetworkType.setValue(
+ DefaultNetworkType(mobileConnectionsRepositoryKairos.fake.GSM_KEY)
+ )
+ assertThat(latest).isEqualTo(true)
+
+ mobileConnectionsRepositoryKairos.fake.defaultMobileIconGroup.setValue(
+ TelephonyIcons.UNKNOWN
+ )
+
+ repo.resolvedNetworkType.setValue(UnknownNetworkType)
+ assertThat(latest).isEqualTo(false)
+
+ repo.resolvedNetworkType.setValue(
+ DefaultNetworkType(mobileConnectionsRepositoryKairos.fake.LTE_KEY)
+ )
+ assertThat(latest).isEqualTo(true)
+ }
@Test
- fun firstMobileSubShowingNetworkTypeIcon_noSubs_false() =
- testScope.runTest {
- var latest: Boolean? = null
- val job =
- underTest.firstMobileSubShowingNetworkTypeIcon.onEach { latest = it }.launchIn(this)
+ fun firstMobileSubShowingNetworkTypeIcon_multipleSubs_lastSubNotShowingRat_false() = runTest {
+ val latest by underTest.firstMobileSubShowingNetworkTypeIcon.collectLastValue()
- interactor.filteredSubscriptions.value = emptyList()
+ mobileConnectionsRepositoryKairos.fake.defaultMobileIconGroup.setValue(
+ TelephonyIcons.UNKNOWN
+ )
+ mobileConnectionsRepositoryKairos.fake.mobileIsDefault.setValue(true)
- assertThat(latest).isFalse()
+ val repo1 =
+ mobileConnectionsRepositoryKairos.fake.mobileConnectionsBySubId
+ .sample()[SUB_1.subscriptionId]!!
- job.cancel()
- }
+ repo1.resolvedNetworkType.setValue(
+ DefaultNetworkType(mobileConnectionsRepositoryKairos.fake.GSM_KEY)
+ )
+
+ val repo2 =
+ mobileConnectionsRepositoryKairos.fake.mobileConnectionsBySubId
+ .sample()[SUB_2.subscriptionId]!!
+
+ repo2.resolvedNetworkType.setValue(UnknownNetworkType)
+
+ assertThat(latest).isFalse()
+ }
@Test
- fun firstMobileSubShowingNetworkTypeIcon_oneSub_notShowingRat_false() =
- testScope.runTest {
- var latest: Boolean? = null
- val job =
- underTest.firstMobileSubShowingNetworkTypeIcon.onEach { latest = it }.launchIn(this)
+ fun firstMobileSubShowingNetworkTypeIcon_multipleSubs_lastSubShowingRat_true() = runTest {
+ val latest by underTest.firstMobileSubShowingNetworkTypeIcon.collectLastValue()
- interactor.filteredSubscriptions.value = listOf(SUB_1)
- // The unknown icon group doesn't show a RAT
- interactor.getInteractorForSubId(1)!!.networkTypeIconGroup.value =
- NetworkTypeIconModel.DefaultIcon(TelephonyIcons.UNKNOWN)
+ mobileConnectionsRepositoryKairos.fake.defaultMobileIconGroup.setValue(
+ TelephonyIcons.UNKNOWN
+ )
+ mobileConnectionsRepositoryKairos.fake.mobileIsDefault.setValue(true)
- assertThat(latest).isFalse()
+ val repo1 =
+ mobileConnectionsRepositoryKairos.fake.mobileConnectionsBySubId
+ .sample()[SUB_1.subscriptionId]!!
- job.cancel()
- }
+ repo1.dataConnectionState.setValue(DataConnectionState.Connected)
+ repo1.resolvedNetworkType.setValue(UnknownNetworkType)
+
+ val repo2 =
+ mobileConnectionsRepositoryKairos.fake.mobileConnectionsBySubId
+ .sample()[SUB_2.subscriptionId]!!
+
+ repo2.dataConnectionState.setValue(DataConnectionState.Connected)
+ repo2.resolvedNetworkType.setValue(
+ DefaultNetworkType(mobileConnectionsRepositoryKairos.fake.GSM_KEY)
+ )
+
+ assertThat(latest).isEqualTo(true)
+ }
@Test
- fun firstMobileSubShowingNetworkTypeIcon_oneSub_showingRat_true() =
- testScope.runTest {
- var latest: Boolean? = null
- val job =
- underTest.firstMobileSubShowingNetworkTypeIcon.onEach { latest = it }.launchIn(this)
+ fun firstMobileSubShowingNetworkTypeIcon_subListUpdates_valAlsoUpdates() = runTest {
+ val latest by underTest.firstMobileSubShowingNetworkTypeIcon.collectLastValue()
- interactor.filteredSubscriptions.value = listOf(SUB_1)
- // The 3G icon group will show a RAT
- interactor.getInteractorForSubId(1)!!.networkTypeIconGroup.value =
- NetworkTypeIconModel.DefaultIcon(TelephonyIcons.THREE_G)
+ mobileConnectionsRepositoryKairos.fake.defaultMobileIconGroup.setValue(
+ TelephonyIcons.UNKNOWN
+ )
+ mobileConnectionsRepositoryKairos.fake.mobileIsDefault.setValue(true)
- assertThat(latest).isTrue()
+ val repo1 =
+ mobileConnectionsRepositoryKairos.fake.mobileConnectionsBySubId
+ .sample()[SUB_1.subscriptionId]!!
- job.cancel()
- }
+ repo1.dataConnectionState.setValue(DataConnectionState.Connected)
+ repo1.resolvedNetworkType.setValue(UnknownNetworkType)
- @Test
- fun firstMobileSubShowingNetworkTypeIcon_updatesAsSubUpdates() =
- testScope.runTest {
- var latest: Boolean? = null
- val job =
- underTest.firstMobileSubShowingNetworkTypeIcon.onEach { latest = it }.launchIn(this)
+ val repo2 =
+ mobileConnectionsRepositoryKairos.fake.mobileConnectionsBySubId
+ .sample()[SUB_2.subscriptionId]!!
- interactor.filteredSubscriptions.value = listOf(SUB_1)
- val sub1Interactor = interactor.getInteractorForSubId(1)!!
+ repo2.dataConnectionState.setValue(DataConnectionState.Connected)
+ repo2.resolvedNetworkType.setValue(
+ DefaultNetworkType(mobileConnectionsRepositoryKairos.fake.GSM_KEY)
+ )
- sub1Interactor.networkTypeIconGroup.value =
- NetworkTypeIconModel.DefaultIcon(TelephonyIcons.THREE_G)
- assertThat(latest).isTrue()
+ assertThat(latest).isEqualTo(true)
- sub1Interactor.networkTypeIconGroup.value =
- NetworkTypeIconModel.DefaultIcon(TelephonyIcons.UNKNOWN)
- assertThat(latest).isFalse()
+ // WHEN the sub list gets new subscriptions where the last subscription is not showing
+ // the network type icon
+ setSubscriptions(listOf(SUB_1, SUB_2, SUB_3))
- sub1Interactor.networkTypeIconGroup.value =
- NetworkTypeIconModel.DefaultIcon(TelephonyIcons.LTE)
- assertThat(latest).isTrue()
+ val repo3 =
+ mobileConnectionsRepositoryKairos.fake.mobileConnectionsBySubId
+ .sample()[SUB_3.subscriptionId]!!
- job.cancel()
- }
+ repo3.dataConnectionState.setValue(DataConnectionState.Connected)
+ repo3.resolvedNetworkType.setValue(UnknownNetworkType)
+
+ // THEN the flow updates
+ assertThat(latest).isEqualTo(false)
+ }
@Test
- fun firstMobileSubShowingNetworkTypeIcon_multipleSubs_lastSubNotShowingRat_false() =
- testScope.runTest {
- var latest: Boolean? = null
- val job =
- underTest.firstMobileSubShowingNetworkTypeIcon.onEach { latest = it }.launchIn(this)
+ fun firstMobileSubShowingNetworkTypeIcon_subListReorders_valAlsoUpdates() = runTest {
+ val latest by underTest.firstMobileSubShowingNetworkTypeIcon.collectLastValue()
- interactor.filteredSubscriptions.value = listOf(SUB_1, SUB_2)
- interactor.getInteractorForSubId(1)?.networkTypeIconGroup?.value =
- NetworkTypeIconModel.DefaultIcon(TelephonyIcons.THREE_G)
- interactor.getInteractorForSubId(2)!!.networkTypeIconGroup.value =
- NetworkTypeIconModel.DefaultIcon(TelephonyIcons.UNKNOWN)
+ mobileConnectionsRepositoryKairos.fake.defaultMobileIconGroup.setValue(
+ TelephonyIcons.UNKNOWN
+ )
+ mobileConnectionsRepositoryKairos.fake.mobileIsDefault.setValue(true)
- assertThat(latest).isFalse()
+ setSubscriptions(listOf(SUB_1, SUB_2))
+ // Immediately switch the order so that we've created both interactors
+ setSubscriptions(listOf(SUB_2, SUB_1))
- job.cancel()
- }
+ val repos = mobileConnectionsRepositoryKairos.fake.mobileConnectionsBySubId.sample()
+ val repo1 = repos[SUB_1.subscriptionId]!!
+ repo1.dataConnectionState.setValue(DataConnectionState.Connected)
- @Test
- fun firstMobileSubShowingNetworkTypeIcon_multipleSubs_lastSubShowingRat_true() =
- testScope.runTest {
- var latest: Boolean? = null
- val job =
- underTest.firstMobileSubShowingNetworkTypeIcon.onEach { latest = it }.launchIn(this)
-
- interactor.filteredSubscriptions.value = listOf(SUB_1, SUB_2)
- interactor.getInteractorForSubId(1)?.networkTypeIconGroup?.value =
- NetworkTypeIconModel.DefaultIcon(TelephonyIcons.UNKNOWN)
- interactor.getInteractorForSubId(2)!!.networkTypeIconGroup.value =
- NetworkTypeIconModel.DefaultIcon(TelephonyIcons.THREE_G)
-
- assertThat(latest).isTrue()
- job.cancel()
- }
+ val repo2 = repos[SUB_2.subscriptionId]!!
+ repo2.dataConnectionState.setValue(DataConnectionState.Connected)
- @Test
- fun firstMobileSubShowingNetworkTypeIcon_subListUpdates_valAlsoUpdates() =
- testScope.runTest {
- var latest: Boolean? = null
- val job =
- underTest.firstMobileSubShowingNetworkTypeIcon.onEach { latest = it }.launchIn(this)
-
- interactor.filteredSubscriptions.value = listOf(SUB_1, SUB_2)
- interactor.getInteractorForSubId(1)?.networkTypeIconGroup?.value =
- NetworkTypeIconModel.DefaultIcon(TelephonyIcons.UNKNOWN)
- interactor.getInteractorForSubId(2)!!.networkTypeIconGroup.value =
- NetworkTypeIconModel.DefaultIcon(TelephonyIcons.THREE_G)
-
- assertThat(latest).isTrue()
-
- // WHEN the sub list gets new subscriptions where the last subscription is not showing
- // the network type icon
- interactor.filteredSubscriptions.value = listOf(SUB_1, SUB_2, SUB_3)
- interactor.getInteractorForSubId(3)!!.networkTypeIconGroup.value =
- NetworkTypeIconModel.DefaultIcon(TelephonyIcons.UNKNOWN)
-
- // THEN the flow updates
- assertThat(latest).isFalse()
-
- job.cancel()
- }
+ setSubscriptions(listOf(SUB_1, SUB_2))
+ repo1.resolvedNetworkType.setValue(UnknownNetworkType)
+ repo2.resolvedNetworkType.setValue(
+ DefaultNetworkType(mobileConnectionsRepositoryKairos.fake.GSM_KEY)
+ )
- @Test
- fun firstMobileSubShowingNetworkTypeIcon_subListReorders_valAlsoUpdates() =
- testScope.runTest {
- var latest: Boolean? = null
- val job =
- underTest.firstMobileSubShowingNetworkTypeIcon.onEach { latest = it }.launchIn(this)
-
- interactor.filteredSubscriptions.value = listOf(SUB_1, SUB_2)
- // Immediately switch the order so that we've created both interactors
- interactor.filteredSubscriptions.value = listOf(SUB_2, SUB_1)
- val sub1Interactor = interactor.getInteractorForSubId(1)!!
- val sub2Interactor = interactor.getInteractorForSubId(2)!!
-
- interactor.filteredSubscriptions.value = listOf(SUB_1, SUB_2)
- sub1Interactor.networkTypeIconGroup.value =
- NetworkTypeIconModel.DefaultIcon(TelephonyIcons.UNKNOWN)
- sub2Interactor.networkTypeIconGroup.value =
- NetworkTypeIconModel.DefaultIcon(TelephonyIcons.THREE_G)
- assertThat(latest).isTrue()
-
- // WHEN sub1 becomes last and sub1 has no network type icon
- interactor.filteredSubscriptions.value = listOf(SUB_2, SUB_1)
-
- // THEN the flow updates
- assertThat(latest).isFalse()
-
- // WHEN sub2 becomes last and sub2 has a network type icon
- interactor.filteredSubscriptions.value = listOf(SUB_1, SUB_2)
-
- // THEN the flow updates
- assertThat(latest).isTrue()
-
- job.cancel()
- }
+ assertThat(latest).isEqualTo(true)
+
+ // WHEN sub1 becomes last and sub1 has no network type icon
+ setSubscriptions(listOf(SUB_2, SUB_1))
+
+ // THEN the flow updates
+ assertThat(latest).isEqualTo(false)
+
+ // WHEN sub2 becomes last and sub2 has a network type icon
+ setSubscriptions(listOf(SUB_1, SUB_2))
+
+ // THEN the flow updates
+ assertThat(latest).isEqualTo(true)
+ }
companion object {
private val SUB_1 =
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/StackedMobileIconViewModelKairosTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/StackedMobileIconViewModelKairosTest.kt
index ce35d9d8610f..75f5cbb041c4 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/StackedMobileIconViewModelKairosTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/StackedMobileIconViewModelKairosTest.kt
@@ -21,81 +21,83 @@ import android.telephony.SubscriptionManager.PROFILE_CLASS_UNSET
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
-import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.flags.Flags
+import com.android.systemui.flags.fake
+import com.android.systemui.flags.featureFlagsClassic
+import com.android.systemui.kairos.ExperimentalKairosApi
+import com.android.systemui.kairos.KairosTestScope
+import com.android.systemui.kairos.runKairosTest
import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.kosmos.Kosmos.Fixture
-import com.android.systemui.kosmos.runTest
-import com.android.systemui.kosmos.testScope
import com.android.systemui.kosmos.useUnconfinedTestDispatcher
-import com.android.systemui.lifecycle.activateIn
import com.android.systemui.statusbar.core.NewStatusBarIcons
import com.android.systemui.statusbar.core.StatusBarRootModernization
import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel
-import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.fakeMobileIconsInteractor
-import com.android.systemui.statusbar.pipeline.mobile.domain.model.SignalIconModel
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.fake
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.fakeMobileConnectionsRepositoryKairos
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.mobileConnectionsRepositoryKairos
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
-import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
+@OptIn(ExperimentalKairosApi::class)
@SmallTest
@RunWith(AndroidJUnit4::class)
class StackedMobileIconViewModelKairosTest : SysuiTestCase() {
- private val kosmos = testKosmos().useUnconfinedTestDispatcher()
- private val testScope = kosmos.testScope
-
- private val Kosmos.underTest: StackedMobileIconViewModelKairos by Fixture {
- stackedMobileIconViewModelKairos
- }
+ private val kosmos =
+ testKosmos().useUnconfinedTestDispatcher().apply {
+ mobileConnectionsRepositoryKairos = fakeMobileConnectionsRepositoryKairos
+ featureFlagsClassic.fake.apply {
+ setDefault(Flags.FILTER_PROVISIONING_NETWORK_SUBSCRIPTIONS)
+ }
+ }
- @Before
- fun setUp() {
- kosmos.underTest.activateIn(testScope)
- }
+ private val Kosmos.underTest
+ get() = stackedMobileIconViewModelKairos
@Test
@EnableFlags(NewStatusBarIcons.FLAG_NAME, StatusBarRootModernization.FLAG_NAME)
fun dualSim_filtersOutNonDualConnections() =
- kosmos.runTest {
- fakeMobileIconsInteractor.filteredSubscriptions.value = listOf()
+ kosmos.runKairosTest {
+ mobileConnectionsRepositoryKairos.fake.subscriptions.setValue(listOf())
assertThat(underTest.dualSim).isNull()
- fakeMobileIconsInteractor.filteredSubscriptions.value = listOf(SUB_1)
+ mobileConnectionsRepositoryKairos.fake.subscriptions.setValue(listOf(SUB_1))
assertThat(underTest.dualSim).isNull()
- fakeMobileIconsInteractor.filteredSubscriptions.value = listOf(SUB_1, SUB_2, SUB_3)
+ mobileConnectionsRepositoryKairos.fake.subscriptions.setValue(
+ listOf(SUB_1, SUB_2, SUB_3)
+ )
assertThat(underTest.dualSim).isNull()
- fakeMobileIconsInteractor.filteredSubscriptions.value = listOf(SUB_1, SUB_2)
+ mobileConnectionsRepositoryKairos.fake.subscriptions.setValue(listOf(SUB_1, SUB_2))
assertThat(underTest.dualSim).isNotNull()
}
@Test
@EnableFlags(NewStatusBarIcons.FLAG_NAME, StatusBarRootModernization.FLAG_NAME)
fun dualSim_filtersOutNonCellularIcons() =
- kosmos.runTest {
- fakeMobileIconsInteractor.filteredSubscriptions.value = listOf(SUB_1)
+ kosmos.runKairosTest {
+ mobileConnectionsRepositoryKairos.fake.subscriptions.setValue(listOf(SUB_1))
assertThat(underTest.dualSim).isNull()
- fakeMobileIconsInteractor
- .getInteractorForSubId(SUB_1.subscriptionId)!!
- .signalLevelIcon
- .value =
- SignalIconModel.Satellite(
- level = 0,
- icon = Icon.Resource(res = 0, contentDescription = null),
- )
- fakeMobileIconsInteractor.filteredSubscriptions.value = listOf(SUB_1, SUB_2)
+ mobileConnectionsRepositoryKairos.fake.mobileConnectionsBySubId
+ .sample()[SUB_1.subscriptionId]!!
+ .apply {
+ isNonTerrestrial.setValue(true)
+ satelliteLevel.setValue(0)
+ }
+
+ mobileConnectionsRepositoryKairos.fake.subscriptions.setValue(listOf(SUB_1, SUB_2))
assertThat(underTest.dualSim).isNull()
}
@Test
@EnableFlags(NewStatusBarIcons.FLAG_NAME, StatusBarRootModernization.FLAG_NAME)
fun dualSim_tracksActiveSubId() =
- kosmos.runTest {
+ kosmos.runKairosTest {
// Active sub id is null, order is unchanged
- fakeMobileIconsInteractor.filteredSubscriptions.value = listOf(SUB_1, SUB_2)
+ mobileConnectionsRepositoryKairos.fake.subscriptions.setValue(listOf(SUB_1, SUB_2))
setIconLevel(SUB_1.subscriptionId, 1)
setIconLevel(SUB_2.subscriptionId, 2)
@@ -103,16 +105,21 @@ class StackedMobileIconViewModelKairosTest : SysuiTestCase() {
assertThat(underTest.dualSim!!.secondary.level).isEqualTo(2)
// Active sub is 2, order is swapped
- fakeMobileIconsInteractor.activeMobileDataSubscriptionId.value = SUB_2.subscriptionId
+ mobileConnectionsRepositoryKairos.fake.setActiveMobileDataSubscriptionId(
+ SUB_2.subscriptionId
+ )
assertThat(underTest.dualSim!!.primary.level).isEqualTo(2)
assertThat(underTest.dualSim!!.secondary.level).isEqualTo(1)
}
- private fun setIconLevel(subId: Int, level: Int) {
- with(kosmos.fakeMobileIconsInteractor.getInteractorForSubId(subId)!!) {
- signalLevelIcon.value =
- (signalLevelIcon.value as SignalIconModel.Cellular).copy(level = level)
+ private suspend fun KairosTestScope.setIconLevel(subId: Int, level: Int) {
+ mobileConnectionsRepositoryKairos.fake.mobileConnectionsBySubId.sample()[subId]!!.apply {
+ isNonTerrestrial.setValue(false)
+ isInService.setValue(true)
+ inflateSignalStrength.setValue(false)
+ isGsm.setValue(true)
+ primaryLevel.setValue(level)
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/StackedMobileIconViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/StackedMobileIconViewModelTest.kt
index d7bcf88f6941..8a7b698623f8 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/StackedMobileIconViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/StackedMobileIconViewModelTest.kt
@@ -45,8 +45,8 @@ class StackedMobileIconViewModelTest : SysuiTestCase() {
private val kosmos = testKosmos().useUnconfinedTestDispatcher()
private val testScope = kosmos.testScope
- private val Kosmos.underTest: StackedMobileIconViewModel by Fixture {
- stackedMobileIconViewModel
+ private val Kosmos.underTest: StackedMobileIconViewModelImpl by Fixture {
+ stackedMobileIconViewModelImpl
}
@Before
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/topwindoweffects/TopLevelWindowEffectsTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/topwindoweffects/TopLevelWindowEffectsTest.kt
index 6d3813c90bfd..bcc6ea3d3b35 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/topwindoweffects/TopLevelWindowEffectsTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/topwindoweffects/TopLevelWindowEffectsTest.kt
@@ -20,8 +20,6 @@ import android.view.View
import android.view.WindowManager
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.app.viewcapture.ViewCapture
-import com.android.app.viewcapture.ViewCaptureAwareWindowManager
import com.android.systemui.SysuiTestCase
import com.android.systemui.keyevent.data.repository.fakeKeyEventRepository
import com.android.systemui.keyevent.data.repository.keyEventRepository
@@ -58,20 +56,13 @@ class TopLevelWindowEffectsTest : SysuiTestCase() {
private lateinit var windowManager: WindowManager
@Mock
- private lateinit var viewCapture: Lazy<ViewCapture>
-
- @Mock
private lateinit var viewModelFactory: SqueezeEffectViewModel.Factory
private val Kosmos.underTest by Kosmos.Fixture {
TopLevelWindowEffects(
context = mContext,
applicationScope = testScope.backgroundScope,
- windowManager = ViewCaptureAwareWindowManager(
- windowManager = windowManager,
- lazyViewCapture = viewCapture,
- isViewCaptureEnabled = false
- ),
+ windowManager = windowManager,
keyEventInteractor = keyEventInteractor,
viewModelFactory = viewModelFactory,
squeezeEffectInteractor = SqueezeEffectInteractor(
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/DisplaySwitchLatencyTrackerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/DisplaySwitchLatencyTrackerTest.kt
index 18a124cf362e..033503f9ad8e 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/DisplaySwitchLatencyTrackerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/DisplaySwitchLatencyTrackerTest.kt
@@ -52,6 +52,7 @@ import com.android.systemui.unfold.DisplaySwitchLatencyTracker.Companion.FOLDABL
import com.android.systemui.unfold.DisplaySwitchLatencyTracker.Companion.FOLDABLE_DEVICE_STATE_HALF_OPEN
import com.android.systemui.unfold.DisplaySwitchLatencyTracker.Companion.SCREEN_EVENT_TIMEOUT
import com.android.systemui.unfold.DisplaySwitchLatencyTracker.DisplaySwitchLatencyEvent
+import com.android.systemui.unfold.data.repository.ScreenTimeoutPolicyRepository
import com.android.systemui.unfold.data.repository.UnfoldTransitionRepositoryImpl
import com.android.systemui.unfold.domain.interactor.UnfoldTransitionInteractor
import com.android.systemui.unfoldedDeviceState
@@ -97,6 +98,8 @@ class DisplaySwitchLatencyTrackerTest : SysuiTestCase() {
private val animationStatusRepository = kosmos.fakeAnimationStatusRepository
private val keyguardInteractor = mock<KeyguardInteractor>()
private val displaySwitchLatencyLogger = mock<DisplaySwitchLatencyLogger>()
+ private val screenTimeoutPolicyRepository = mock<ScreenTimeoutPolicyRepository>()
+ private val screenTimeoutActive = MutableStateFlow(true)
private val latencyTracker = mock<LatencyTracker>()
private val deviceStateManager = kosmos.deviceStateManager
@@ -136,6 +139,7 @@ class DisplaySwitchLatencyTrackerTest : SysuiTestCase() {
whenever(resources.getIntArray(R.array.config_foldedDeviceStates))
.thenReturn(nonEmptyClosedDeviceStatesArray)
whenever(keyguardInteractor.isAodAvailable).thenReturn(isAodAvailable)
+ whenever(screenTimeoutPolicyRepository.screenTimeoutActive).thenReturn(screenTimeoutActive)
animationStatusRepository.onAnimationStatusChanged(true)
powerInteractor.setAwakeForTest()
powerInteractor.setScreenPowerState(SCREEN_ON)
@@ -144,6 +148,7 @@ class DisplaySwitchLatencyTrackerTest : SysuiTestCase() {
mockContext,
foldStateRepository,
powerInteractor,
+ screenTimeoutPolicyRepository,
unfoldTransitionInteractor,
animationStatusRepository,
keyguardInteractor,
@@ -196,6 +201,7 @@ class DisplaySwitchLatencyTrackerTest : SysuiTestCase() {
mockContext,
foldStateRepository,
powerInteractor,
+ screenTimeoutPolicyRepository,
unfoldTransitionInteractorWithEmptyProgressProvider,
animationStatusRepository,
keyguardInteractor,
@@ -625,6 +631,44 @@ class DisplaySwitchLatencyTrackerTest : SysuiTestCase() {
}
}
+ @Test
+ fun displaySwitch_screenTimeoutActive_logsNoScreenWakelocks() {
+ testScope.runTest {
+ startInFoldedState(displaySwitchLatencyTracker)
+ screenTimeoutActive.value = true
+
+ startUnfolding()
+ advanceTimeBy(100.milliseconds)
+ finishUnfolding()
+
+ val event = capturedLogEvent()
+ assertThat(event.screenWakelockStatus)
+ .isEqualTo(
+ SysUiStatsLog
+ .DISPLAY_SWITCH_LATENCY_TRACKED__SCREEN_WAKELOCK_STATUS__SCREEN_WAKELOCK_STATUS_NO_WAKELOCKS
+ )
+ }
+ }
+
+ @Test
+ fun displaySwitch_screenTimeoutNotActive_logsHasScreenWakelocks() {
+ testScope.runTest {
+ startInFoldedState(displaySwitchLatencyTracker)
+ screenTimeoutActive.value = false
+
+ startUnfolding()
+ advanceTimeBy(100.milliseconds)
+ finishUnfolding()
+
+ val event = capturedLogEvent()
+ assertThat(event.screenWakelockStatus)
+ .isEqualTo(
+ SysUiStatsLog
+ .DISPLAY_SWITCH_LATENCY_TRACKED__SCREEN_WAKELOCK_STATUS__SCREEN_WAKELOCK_STATUS_HAS_SCREEN_WAKELOCKS
+ )
+ }
+ }
+
private fun capturedLogEvent(): DisplaySwitchLatencyEvent {
verify(displaySwitchLatencyLogger).log(capture(loggerArgumentCaptor))
return loggerArgumentCaptor.value
@@ -662,6 +706,9 @@ class DisplaySwitchLatencyTrackerTest : SysuiTestCase() {
fromFoldableDeviceState = fromFoldableDeviceState,
toFoldableDeviceState = toFoldableDeviceState,
toState = toState,
+ screenWakelockStatus =
+ SysUiStatsLog
+ .DISPLAY_SWITCH_LATENCY_TRACKED__SCREEN_WAKELOCK_STATUS__SCREEN_WAKELOCK_STATUS_NO_WAKELOCKS,
trackingResult = SysUiStatsLog.DISPLAY_SWITCH_LATENCY_TRACKED__TRACKING_RESULT__SUCCESS,
)
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/user/data/repository/UserRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/user/data/repository/UserRepositoryImplTest.kt
index bafa8cf05a7f..da5622a2a8da 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/user/data/repository/UserRepositoryImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/user/data/repository/UserRepositoryImplTest.kt
@@ -17,8 +17,10 @@
package com.android.systemui.user.data.repository
+import android.app.admin.DevicePolicyManager
import android.app.admin.devicePolicyManager
import android.content.Intent
+import android.content.applicationContext
import android.content.pm.UserInfo
import android.internal.statusbar.fakeStatusBarService
import android.os.UserHandle
@@ -77,10 +79,7 @@ class UserRepositoryImplTest : SysuiTestCase() {
fun setUp() {
MockitoAnnotations.initMocks(this)
tracker = FakeUserTracker()
- context.orCreateTestableResources.addOverride(
- R.bool.config_userSwitchingMustGoThroughLoginScreen,
- false,
- )
+ setUserSwitchingMustGoThroughLoginScreen(false)
}
@Test
@@ -308,6 +307,117 @@ class UserRepositoryImplTest : SysuiTestCase() {
job.cancel()
}
+ @Test
+ fun isSecondaryUserLogoutEnabled_secondaryLogoutDisabled_alwaysFalse() =
+ testScope.runTest {
+ underTest = create(testScope.backgroundScope)
+ mockLogoutUser(LogoutUserResult.NON_SYSTEM_CURRENT)
+ setSecondaryUserLogoutEnabled(false)
+ setUpUsers(count = 2, selectedIndex = 0)
+ tracker.onProfileChanged()
+
+ val secondaryUserLogoutEnabled by
+ collectLastValue(underTest.isSecondaryUserLogoutEnabled)
+
+ assertThat(secondaryUserLogoutEnabled).isFalse()
+
+ setUpUsers(count = 2, selectedIndex = 1)
+ tracker.onProfileChanged()
+ assertThat(secondaryUserLogoutEnabled).isFalse()
+ }
+
+ @Test
+ fun isSecondaryUserLogoutEnabled_secondaryLogoutEnabled_NullLogoutUser_alwaysFalse() =
+ testScope.runTest {
+ underTest = create(testScope.backgroundScope)
+ mockLogoutUser(LogoutUserResult.NONE)
+ setSecondaryUserLogoutEnabled(true)
+ setUpUsers(count = 2, selectedIndex = 0)
+ tracker.onProfileChanged()
+
+ val secondaryUserLogoutEnabled by
+ collectLastValue(underTest.isSecondaryUserLogoutEnabled)
+
+ assertThat(secondaryUserLogoutEnabled).isFalse()
+
+ setUpUsers(count = 2, selectedIndex = 1)
+ tracker.onProfileChanged()
+ assertThat(secondaryUserLogoutEnabled).isFalse()
+ }
+
+ @Test
+ fun isSecondaryUserLogoutEnabled_secondaryLogoutEnabled_NonSystemLogoutUser_trueWhenNonSystem() =
+ testScope.runTest {
+ underTest = create(testScope.backgroundScope)
+ mockLogoutUser(LogoutUserResult.NON_SYSTEM_CURRENT)
+ setSecondaryUserLogoutEnabled(true)
+ setUpUsers(count = 2, selectedIndex = 0)
+ tracker.onProfileChanged()
+
+ val secondaryUserLogoutEnabled by
+ collectLastValue(underTest.isSecondaryUserLogoutEnabled)
+
+ assertThat(secondaryUserLogoutEnabled).isFalse()
+
+ setUpUsers(count = 2, selectedIndex = 1)
+ tracker.onProfileChanged()
+ assertThat(secondaryUserLogoutEnabled).isTrue()
+ }
+
+ @Test
+ fun isLogoutToSystemUserEnabled_logoutThroughLoginScreenDisabled_alwaysFalse() =
+ testScope.runTest {
+ underTest = create(testScope.backgroundScope)
+ mockLogoutUser(LogoutUserResult.NON_SYSTEM_CURRENT)
+ setUserSwitchingMustGoThroughLoginScreen(false)
+ setUpUsers(count = 2, selectedIndex = 0)
+ tracker.onProfileChanged()
+
+ val logoutToSystemUserEnabled by collectLastValue(underTest.isLogoutToSystemUserEnabled)
+
+ assertThat(logoutToSystemUserEnabled).isFalse()
+
+ setUpUsers(count = 2, selectedIndex = 1)
+ tracker.onProfileChanged()
+ assertThat(logoutToSystemUserEnabled).isFalse()
+ }
+
+ @Test
+ fun isLogoutToSystemUserEnabled_logoutThroughLoginScreenEnabled_NullLogoutUser_alwaysFalse() =
+ testScope.runTest {
+ underTest = create(testScope.backgroundScope)
+ mockLogoutUser(LogoutUserResult.NONE)
+ setUserSwitchingMustGoThroughLoginScreen(true)
+ setUpUsers(count = 2, selectedIndex = 0)
+ tracker.onProfileChanged()
+
+ val logoutToSystemUserEnabled by collectLastValue(underTest.isLogoutToSystemUserEnabled)
+
+ assertThat(logoutToSystemUserEnabled).isFalse()
+
+ setUpUsers(count = 2, selectedIndex = 1)
+ tracker.onProfileChanged()
+ assertThat(logoutToSystemUserEnabled).isFalse()
+ }
+
+ @Test
+ fun isLogoutToSystemUserEnabled_logoutThroughLoginScreenEnabled_NonSystemLogoutUser_trueWhenNonSystem() =
+ testScope.runTest {
+ underTest = create(testScope.backgroundScope)
+ mockLogoutUser(LogoutUserResult.NON_SYSTEM_CURRENT)
+ setUserSwitchingMustGoThroughLoginScreen(true)
+ setUpUsers(count = 2, selectedIndex = 0)
+ tracker.onProfileChanged()
+
+ val logoutToSystemUserEnabled by collectLastValue(underTest.isLogoutToSystemUserEnabled)
+
+ assertThat(logoutToSystemUserEnabled).isFalse()
+
+ setUpUsers(count = 2, selectedIndex = 1)
+ tracker.onProfileChanged()
+ assertThat(logoutToSystemUserEnabled).isTrue()
+ }
+
private fun createUserInfo(id: Int, isGuest: Boolean): UserInfo {
val flags = 0
return UserInfo(
@@ -354,6 +464,38 @@ class UserRepositoryImplTest : SysuiTestCase() {
assertThat(model.isUserSwitcherEnabled).isEqualTo(expectedUserSwitcherEnabled)
}
+ private fun setSecondaryUserLogoutEnabled(enabled: Boolean) {
+ whenever(devicePolicyManager.isLogoutEnabled).thenReturn(enabled)
+ broadcastDispatcher.sendIntentToMatchingReceiversOnly(
+ kosmos.applicationContext,
+ Intent(DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED),
+ )
+ }
+
+ private fun setUserSwitchingMustGoThroughLoginScreen(enabled: Boolean) {
+ context.orCreateTestableResources.addOverride(
+ R.bool.config_userSwitchingMustGoThroughLoginScreen,
+ enabled,
+ )
+ }
+
+ private fun mockLogoutUser(result: LogoutUserResult) {
+ when (result) {
+ LogoutUserResult.NONE -> {
+ whenever(devicePolicyManager.logoutUser).thenReturn(null)
+ }
+ LogoutUserResult.NON_SYSTEM_CURRENT -> {
+ whenever(devicePolicyManager.logoutUser).thenAnswer {
+ if (tracker.userHandle != UserHandle.SYSTEM) {
+ tracker.userHandle
+ } else {
+ null
+ }
+ }
+ }
+ }
+ }
+
private fun create(scope: CoroutineScope): UserRepositoryImpl {
return UserRepositoryImpl(
appContext = context,
@@ -373,4 +515,9 @@ class UserRepositoryImplTest : SysuiTestCase() {
companion object {
@JvmStatic private val IMMEDIATE = Dispatchers.Main.immediate
}
+
+ private enum class LogoutUserResult {
+ NONE,
+ NON_SYSTEM_CURRENT,
+ }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/user/domain/interactor/UserSwitcherInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/user/domain/interactor/UserSwitcherInteractorTest.kt
index 3eada258f616..07706414393b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/user/domain/interactor/UserSwitcherInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/user/domain/interactor/UserSwitcherInteractorTest.kt
@@ -27,6 +27,8 @@ import android.graphics.drawable.Drawable
import android.os.Process
import android.os.UserHandle
import android.os.UserManager
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
import android.provider.Settings
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
@@ -34,6 +36,7 @@ import com.android.internal.logging.UiEventLogger
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.keyguard.KeyguardUpdateMonitorCallback
import com.android.systemui.Flags as AConfigFlags
+import com.android.systemui.Flags.FLAG_USER_SWITCHER_ADD_SIGN_OUT_OPTION
import com.android.systemui.GuestResetOrExitSessionReceiver
import com.android.systemui.GuestResumeSessionReceiver
import com.android.systemui.SysuiTestCase
@@ -68,6 +71,7 @@ import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
import junit.framework.Assert.assertNotNull
+import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.test.runCurrent
@@ -101,10 +105,13 @@ class UserSwitcherInteractorTest : SysuiTestCase() {
@Mock private lateinit var resumeSessionReceiver: GuestResumeSessionReceiver
@Mock private lateinit var resetOrExitSessionReceiver: GuestResetOrExitSessionReceiver
@Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
+ @Mock private lateinit var userLogoutInteractor: UserLogoutInteractor
private val kosmos = testKosmos()
+ private val logoutEnabledStateFlow = MutableStateFlow<Boolean>(false)
private val testScope = kosmos.testScope
private lateinit var spyContext: Context
+
private lateinit var userRepository: FakeUserRepository
private lateinit var keyguardReply: KeyguardInteractorFactory.WithDependencies
private lateinit var keyguardRepository: FakeKeyguardRepository
@@ -118,6 +125,8 @@ class UserSwitcherInteractorTest : SysuiTestCase() {
whenever(manager.getUserIcon(anyInt())).thenReturn(ICON)
whenever(manager.canAddMoreUsers(any())).thenReturn(true)
+ whenever(userLogoutInteractor.isLogoutEnabled).thenReturn(logoutEnabledStateFlow)
+
overrideResource(com.android.settingslib.R.drawable.ic_account_circle, GUEST_ICON)
overrideResource(R.dimen.max_avatar_size, 10)
overrideResource(
@@ -493,6 +502,42 @@ class UserSwitcherInteractorTest : SysuiTestCase() {
}
@Test
+ @DisableFlags(FLAG_USER_SWITCHER_ADD_SIGN_OUT_OPTION)
+ fun actions_logoutEnabled_flagDisabled_signOutIsNotShown() {
+ createUserInteractor()
+ testScope.runTest {
+ val userInfos = createUserInfos(count = 1, includeGuest = false)
+ userRepository.setUserInfos(userInfos)
+ userRepository.setSelectedUserInfo(userInfos[0])
+ userRepository.setSettings(UserSwitcherSettingsModel(isUserSwitcherEnabled = false))
+ keyguardRepository.setKeyguardShowing(true)
+ logoutEnabledStateFlow.value = true
+
+ val value = collectLastValue(underTest.actions)
+
+ assertThat(value()).isEqualTo(emptyList<UserActionModel>())
+ }
+ }
+
+ @Test
+ @EnableFlags(FLAG_USER_SWITCHER_ADD_SIGN_OUT_OPTION)
+ fun actions_logoutEnabled_flagEnabled_signOutIsShown() {
+ createUserInteractor()
+ testScope.runTest {
+ val userInfos = createUserInfos(count = 1, includeGuest = false)
+ userRepository.setUserInfos(userInfos)
+ userRepository.setSelectedUserInfo(userInfos[0])
+ userRepository.setSettings(UserSwitcherSettingsModel(isUserSwitcherEnabled = false))
+ keyguardRepository.setKeyguardShowing(true)
+ logoutEnabledStateFlow.value = true
+
+ val value = collectLastValue(underTest.actions)
+
+ assertThat(value()).isEqualTo(listOf(UserActionModel.SIGN_OUT))
+ }
+ }
+
+ @Test
fun executeAction_addUser_dismissesDialogAndStartsActivity() {
createUserInteractor()
testScope.runTest {
@@ -569,14 +614,23 @@ class UserSwitcherInteractorTest : SysuiTestCase() {
verify(uiEventLogger, times(1))
.log(MultiUserActionsEvent.CREATE_GUEST_FROM_USER_SWITCHER)
assertThat(dialogRequests)
- .contains(
- ShowDialogRequestModel.ShowUserCreationDialog(isGuest = true),
- )
+ .contains(ShowDialogRequestModel.ShowUserCreationDialog(isGuest = true))
verify(activityManager).switchUser(guestUserInfo.id)
}
}
@Test
+ fun executeAction_signOut() {
+ createUserInteractor()
+ testScope.runTest {
+ underTest.executeAction(UserActionModel.SIGN_OUT)
+ runCurrent()
+
+ verify(userLogoutInteractor).logOut()
+ }
+ }
+
+ @Test
fun selectUser_alreadySelectedGuestReSelected_exitGuestDialog() {
createUserInteractor()
testScope.runTest {
@@ -739,7 +793,7 @@ class UserSwitcherInteractorTest : SysuiTestCase() {
fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly(
spyContext,
- Intent(Intent.ACTION_LOCALE_CHANGED)
+ Intent(Intent.ACTION_LOCALE_CHANGED),
)
runCurrent()
@@ -972,7 +1026,7 @@ class UserSwitcherInteractorTest : SysuiTestCase() {
50,
"Work Profile",
/* iconPath= */ "",
- /* flags= */ UserInfo.FLAG_MANAGED_PROFILE
+ /* flags= */ UserInfo.FLAG_MANAGED_PROFILE,
)
)
userRepository.setUserInfos(userInfos)
@@ -1010,7 +1064,7 @@ class UserSwitcherInteractorTest : SysuiTestCase() {
userRepository.setSettings(
UserSwitcherSettingsModel(
isUserSwitcherEnabled = true,
- isAddUsersFromLockscreen = true
+ isAddUsersFromLockscreen = true,
)
)
@@ -1034,7 +1088,7 @@ class UserSwitcherInteractorTest : SysuiTestCase() {
userRepository.setSettings(
UserSwitcherSettingsModel(
isUserSwitcherEnabled = true,
- isAddUsersFromLockscreen = true
+ isAddUsersFromLockscreen = true,
)
)
@@ -1068,7 +1122,7 @@ class UserSwitcherInteractorTest : SysuiTestCase() {
whenever(
manager.hasUserRestrictionForUser(
UserManager.DISALLOW_ADD_USER,
- UserHandle.of(id)
+ UserHandle.of(id),
)
)
.thenReturn(true)
@@ -1170,7 +1224,7 @@ class UserSwitcherInteractorTest : SysuiTestCase() {
whenever(
manager.hasUserRestrictionForUser(
UserManager.DISALLOW_ADD_USER,
- UserHandle.of(0)
+ UserHandle.of(0),
)
)
.thenReturn(true)
@@ -1195,7 +1249,7 @@ class UserSwitcherInteractorTest : SysuiTestCase() {
model = model,
id = index,
isSelected = index == selectedIndex,
- isGuest = includeGuest && index == count - 1
+ isGuest = includeGuest && index == count - 1,
)
}
}
@@ -1263,14 +1317,12 @@ class UserSwitcherInteractorTest : SysuiTestCase() {
assertThat(record.isSwitchToEnabled).isEqualTo(isSwitchToEnabled)
}
- private fun assertRecordForAction(
- record: UserRecord,
- type: UserActionModel,
- ) {
+ private fun assertRecordForAction(record: UserRecord, type: UserActionModel) {
assertThat(record.isGuest).isEqualTo(type == UserActionModel.ENTER_GUEST_MODE)
assertThat(record.isAddUser).isEqualTo(type == UserActionModel.ADD_USER)
assertThat(record.isAddSupervisedUser)
.isEqualTo(type == UserActionModel.ADD_SUPERVISED_USER)
+ assertThat(record.isSignOut).isEqualTo(type === UserActionModel.SIGN_OUT)
}
private fun createUserInteractor(startAsProcessUser: Boolean = true) {
@@ -1317,13 +1369,11 @@ class UserSwitcherInteractorTest : SysuiTestCase() {
featureFlags = kosmos.fakeFeatureFlagsClassic,
userRestrictionChecker = mock(),
processWrapper = kosmos.processWrapper,
+ userLogoutInteractor = userLogoutInteractor,
)
}
- private fun createUserInfos(
- count: Int,
- includeGuest: Boolean,
- ): List<UserInfo> {
+ private fun createUserInfos(count: Int, includeGuest: Boolean): List<UserInfo> {
return (0 until count).map { index ->
val isGuest = includeGuest && index == count - 1
createUserInfo(
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/user/ui/viewmodel/StatusBarUserChipViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/user/ui/viewmodel/StatusBarUserChipViewModelTest.kt
index 5d51c6d16c5a..d51e66d6f3b0 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/user/ui/viewmodel/StatusBarUserChipViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/user/ui/viewmodel/StatusBarUserChipViewModelTest.kt
@@ -44,9 +44,12 @@ import com.android.systemui.user.data.repository.FakeUserRepository
import com.android.systemui.user.domain.interactor.GuestUserInteractor
import com.android.systemui.user.domain.interactor.HeadlessSystemUserMode
import com.android.systemui.user.domain.interactor.RefreshUsersScheduler
+import com.android.systemui.user.domain.interactor.UserLogoutInteractor
import com.android.systemui.user.domain.interactor.UserSwitcherInteractor
import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.toList
@@ -78,13 +81,13 @@ class StatusBarUserChipViewModelTest : SysuiTestCase() {
@Mock private lateinit var resumeSessionReceiver: GuestResumeSessionReceiver
@Mock private lateinit var resetOrExitSessionReceiver: GuestResetOrExitSessionReceiver
@Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
+ @Mock private lateinit var userLogoutInteractor: UserLogoutInteractor
private lateinit var underTest: StatusBarUserChipViewModel
private val userRepository = FakeUserRepository()
private lateinit var guestUserInteractor: GuestUserInteractor
private lateinit var refreshUsersScheduler: RefreshUsersScheduler
-
private val testDispatcher = UnconfinedTestDispatcher()
private val testScope = TestScope(testDispatcher)
@@ -92,6 +95,9 @@ class StatusBarUserChipViewModelTest : SysuiTestCase() {
fun setUp() {
MockitoAnnotations.initMocks(this)
+ val logoutEnabledStateFlow = MutableStateFlow<Boolean>(false)
+ whenever(userLogoutInteractor.isLogoutEnabled).thenReturn(logoutEnabledStateFlow)
+
doAnswer { invocation ->
val userId = invocation.arguments[0] as Int
when (userId) {
@@ -251,9 +257,7 @@ class StatusBarUserChipViewModelTest : SysuiTestCase() {
headlessSystemUserMode = headlessSystemUserMode,
applicationScope = testScope.backgroundScope,
telephonyInteractor =
- TelephonyInteractor(
- repository = FakeTelephonyRepository(),
- ),
+ TelephonyInteractor(repository = FakeTelephonyRepository()),
broadcastDispatcher = fakeBroadcastDispatcher,
keyguardUpdateMonitor = keyguardUpdateMonitor,
backgroundDispatcher = testDispatcher,
@@ -263,7 +267,8 @@ class StatusBarUserChipViewModelTest : SysuiTestCase() {
guestUserInteractor = guestUserInteractor,
uiEventLogger = uiEventLogger,
userRestrictionChecker = mock(),
- processWrapper = ProcessWrapperFake(activityManager)
+ processWrapper = ProcessWrapperFake(activityManager),
+ userLogoutInteractor = userLogoutInteractor,
)
)
}
@@ -293,7 +298,7 @@ class StatusBarUserChipViewModelTest : SysuiTestCase() {
USER_NAME_0.text!!,
/* iconPath */ "",
/* flags */ UserInfo.FLAG_FULL,
- /* userType */ UserManager.USER_TYPE_FULL_SYSTEM
+ /* userType */ UserManager.USER_TYPE_FULL_SYSTEM,
)
private val USER_1 =
@@ -302,7 +307,7 @@ class StatusBarUserChipViewModelTest : SysuiTestCase() {
USER_NAME_1.text!!,
/* iconPath */ "",
/* flags */ UserInfo.FLAG_FULL,
- /* userType */ UserManager.USER_TYPE_FULL_SYSTEM
+ /* userType */ UserManager.USER_TYPE_FULL_SYSTEM,
)
private val USER_2 =
@@ -311,7 +316,7 @@ class StatusBarUserChipViewModelTest : SysuiTestCase() {
USER_NAME_2.text!!,
/* iconPath */ "",
/* flags */ UserInfo.FLAG_FULL,
- /* userType */ UserManager.USER_TYPE_FULL_SYSTEM
+ /* userType */ UserManager.USER_TYPE_FULL_SYSTEM,
)
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelTest.kt
index 8ff088f5d29b..087ccb83afe5 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelTest.kt
@@ -44,6 +44,7 @@ import com.android.systemui.user.data.repository.FakeUserRepository
import com.android.systemui.user.domain.interactor.GuestUserInteractor
import com.android.systemui.user.domain.interactor.HeadlessSystemUserMode
import com.android.systemui.user.domain.interactor.RefreshUsersScheduler
+import com.android.systemui.user.domain.interactor.UserLogoutInteractor
import com.android.systemui.user.domain.interactor.UserSwitcherInteractor
import com.android.systemui.user.legacyhelper.ui.LegacyUserUiHelper
import com.android.systemui.user.shared.model.UserActionModel
@@ -51,6 +52,7 @@ import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.toList
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
@@ -79,6 +81,7 @@ class UserSwitcherViewModelTest : SysuiTestCase() {
@Mock private lateinit var resumeSessionReceiver: GuestResumeSessionReceiver
@Mock private lateinit var resetOrExitSessionReceiver: GuestResetOrExitSessionReceiver
@Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
+ @Mock private lateinit var userLogoutInteractor: UserLogoutInteractor
private lateinit var underTest: UserSwitcherViewModel
@@ -94,6 +97,10 @@ class UserSwitcherViewModelTest : SysuiTestCase() {
whenever(manager.canAddMoreUsers(any())).thenReturn(true)
whenever(manager.getUserSwitchability(any()))
.thenReturn(UserManager.SWITCHABILITY_STATUS_OK)
+
+ val logoutEnabledStateFlow = MutableStateFlow<Boolean>(false)
+ whenever(userLogoutInteractor.isLogoutEnabled).thenReturn(logoutEnabledStateFlow)
+
overrideResource(
com.android.internal.R.string.config_supervisedUserCreationPackage,
SUPERVISED_USER_CREATION_PACKAGE,
@@ -113,15 +120,11 @@ class UserSwitcherViewModelTest : SysuiTestCase() {
UserInfo.FLAG_ADMIN or
UserInfo.FLAG_FULL,
UserManager.USER_TYPE_FULL_SYSTEM,
- ),
+ )
)
userRepository.setUserInfos(userInfos)
userRepository.setSelectedUserInfo(userInfos[0])
- userRepository.setSettings(
- UserSwitcherSettingsModel(
- isUserSwitcherEnabled = true,
- )
- )
+ userRepository.setSettings(UserSwitcherSettingsModel(isUserSwitcherEnabled = true))
}
val refreshUsersScheduler =
@@ -163,9 +166,7 @@ class UserSwitcherViewModelTest : SysuiTestCase() {
headlessSystemUserMode = headlessSystemUserMode,
applicationScope = testScope.backgroundScope,
telephonyInteractor =
- TelephonyInteractor(
- repository = FakeTelephonyRepository(),
- ),
+ TelephonyInteractor(repository = FakeTelephonyRepository()),
broadcastDispatcher = fakeBroadcastDispatcher,
keyguardUpdateMonitor = keyguardUpdateMonitor,
backgroundDispatcher = testDispatcher,
@@ -175,7 +176,8 @@ class UserSwitcherViewModelTest : SysuiTestCase() {
guestUserInteractor = guestUserInteractor,
uiEventLogger = uiEventLogger,
userRestrictionChecker = mock(),
- processWrapper = ProcessWrapperFake(activityManager)
+ processWrapper = ProcessWrapperFake(activityManager),
+ userLogoutInteractor = userLogoutInteractor,
),
guestUserInteractor = guestUserInteractor,
)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogVisibilityInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogVisibilityInteractorTest.kt
index a303da0dae1a..49c06bf77fb6 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogVisibilityInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogVisibilityInteractorTest.kt
@@ -143,6 +143,7 @@ class VolumeDialogVisibilityInteractorTest : SysuiTestCase() {
assertThat(visibilityModel)
.isInstanceOf(VolumeDialogVisibilityModel.Visible::class.java)
+ assertThat(fakeVolumeDialogController.hasUserActivity).isTrue()
}
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/dialog/ui/viewmodel/VolumeDialogPluginViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/dialog/ui/viewmodel/VolumeDialogPluginViewModelTest.kt
new file mode 100644
index 000000000000..0847281d9b42
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/dialog/ui/viewmodel/VolumeDialogPluginViewModelTest.kt
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2025 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.volume.dialog.ui.viewmodel
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.accessibility.data.repository.accessibilityRepository
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.collectLastValue
+import com.android.systemui.kosmos.runTest
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.testKosmos
+import com.android.systemui.volume.Events
+import com.android.systemui.volume.dialog.data.repository.volumeDialogVisibilityRepository
+import com.android.systemui.volume.dialog.domain.interactor.volumeDialogVisibilityInteractor
+import com.android.systemui.volume.dialog.shared.model.VolumeDialogVisibilityModel
+import com.google.common.truth.Truth.assertThat
+import kotlin.time.Duration.Companion.seconds
+import kotlinx.coroutines.test.advanceTimeBy
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class VolumeDialogPluginViewModelTest : SysuiTestCase() {
+
+ private val kosmos: Kosmos = testKosmos()
+
+ private val underTest: VolumeDialogPluginViewModel by lazy {
+ kosmos.volumeDialogPluginViewModel
+ }
+
+ @Before
+ fun setUp() =
+ with(kosmos) {
+ volumeDialogVisibilityRepository.updateVisibility {
+ VolumeDialogVisibilityModel.Visible(Events.SHOW_REASON_VOLUME_CHANGED, false, 0)
+ }
+ }
+
+ @Test
+ fun safetyWarningAppears_timeoutReset() =
+ kosmos.runTest {
+ accessibilityRepository.setRecommendedTimeout(3.seconds)
+ val visibility by collectLastValue(volumeDialogVisibilityInteractor.dialogVisibility)
+ testScope.advanceTimeBy(2.seconds)
+ assertThat(visibility).isInstanceOf(VolumeDialogVisibilityModel.Visible::class.java)
+
+ underTest.onSafetyWarningDialogShown()
+ testScope.advanceTimeBy(2.seconds)
+ assertThat(visibility).isInstanceOf(VolumeDialogVisibilityModel.Visible::class.java)
+ }
+
+ @Test
+ fun csdWarningAppears_timeoutReset() =
+ kosmos.runTest {
+ accessibilityRepository.setRecommendedTimeout(3.seconds)
+ val visibility by collectLastValue(volumeDialogVisibilityInteractor.dialogVisibility)
+ testScope.advanceTimeBy(2.seconds)
+ assertThat(visibility).isInstanceOf(VolumeDialogVisibilityModel.Visible::class.java)
+
+ underTest.onCsdWarningDialogShown()
+ testScope.advanceTimeBy(2.seconds)
+ assertThat(visibility).isInstanceOf(VolumeDialogVisibilityModel.Visible::class.java)
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioSharingStreamSliderViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioSharingStreamSliderViewModelTest.kt
index 04ab98889755..b1a3caf98f09 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioSharingStreamSliderViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioSharingStreamSliderViewModelTest.kt
@@ -17,6 +17,7 @@
package com.android.systemui.volume.panel.component.volume.slider.ui.viewmodel
import android.bluetooth.BluetoothDevice
+import android.graphics.drawable.TestStubDrawable
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.settingslib.bluetooth.CachedBluetoothDevice
@@ -63,6 +64,12 @@ class AudioSharingStreamSliderViewModelTest : SysuiTestCase() {
assertThat(audioSharingSlider!!.label).isEqualTo("my headset 2")
assertThat(audioSharingSlider!!.icon)
- .isEqualTo(Icon.Resource(R.drawable.ic_volume_media_bt, null))
+ .isEqualTo(
+ Icon.Loaded(
+ drawable = TestStubDrawable(),
+ res = R.drawable.ic_volume_media_bt,
+ contentDescription = null,
+ )
+ )
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioStreamSliderViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioStreamSliderViewModelTest.kt
index 9e8cde3bc936..ffe8e923815f 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioStreamSliderViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioStreamSliderViewModelTest.kt
@@ -18,6 +18,7 @@ package com.android.systemui.volume.panel.component.volume.slider.ui.viewmodel
import android.app.Flags
import android.app.NotificationManager.INTERRUPTION_FILTER_NONE
+import android.graphics.drawable.TestStubDrawable
import android.media.AudioManager
import android.platform.test.annotations.EnableFlags
import android.service.notification.ZenPolicy
@@ -28,7 +29,6 @@ import com.android.settingslib.notification.modes.TestModeBuilder
import com.android.settingslib.volume.shared.model.AudioStream
import com.android.systemui.SysuiTestCase
import com.android.systemui.common.shared.model.Icon
-import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.applicationCoroutineScope
import com.android.systemui.kosmos.collectLastValue
@@ -39,8 +39,6 @@ import com.android.systemui.statusbar.policy.data.repository.fakeZenModeReposito
import com.android.systemui.testKosmos
import com.android.systemui.volume.data.repository.audioSharingRepository
import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.test.runCurrent
-import kotlinx.coroutines.test.runTest
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.kotlin.mock
@@ -173,6 +171,12 @@ class AudioStreamSliderViewModelTest : SysuiTestCase() {
assertThat(mediaSlider!!.label).isEqualTo("my headset 1")
assertThat(mediaSlider!!.icon)
- .isEqualTo(Icon.Resource(R.drawable.ic_volume_media_bt, null))
+ .isEqualTo(
+ Icon.Loaded(
+ drawable = TestStubDrawable(),
+ res = R.drawable.ic_volume_media_bt,
+ contentDescription = null,
+ )
+ )
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/wallpapers/ImageWallpaperTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/wallpapers/ImageWallpaperTest.java
index 60a15915fb77..046b5d701ddf 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/wallpapers/ImageWallpaperTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/wallpapers/ImageWallpaperTest.java
@@ -57,6 +57,7 @@ import com.android.systemui.SysuiTestCase;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.util.concurrency.FakeExecutor;
import com.android.systemui.util.time.FakeSystemClock;
+import com.android.systemui.utils.windowmanager.WindowManagerProvider;
import org.junit.Before;
import org.junit.Test;
@@ -89,6 +90,8 @@ public class ImageWallpaperTest extends SysuiTestCase {
private Context mMockContext;
@Mock
private UserTracker mUserTracker;
+ @Mock
+ private WindowManagerProvider mWindowManagerProvider;
@Mock
private Bitmap mWallpaperBitmap;
@@ -105,7 +108,7 @@ public class ImageWallpaperTest extends SysuiTestCase {
when(mWindowMetrics.getBounds()).thenReturn(
new Rect(0, 0, DISPLAY_WIDTH, DISPLAY_HEIGHT));
when(mWindowManager.getCurrentWindowMetrics()).thenReturn(mWindowMetrics);
- when(mMockContext.getSystemService(WindowManager.class)).thenReturn(mWindowManager);
+ when(mWindowManagerProvider.getWindowManager(any())).thenReturn(mWindowManager);
// set up display manager
doNothing().when(mDisplayManager).registerDisplayListener(any(), any());
@@ -182,7 +185,7 @@ public class ImageWallpaperTest extends SysuiTestCase {
}
private ImageWallpaper createImageWallpaper() {
- return new ImageWallpaper(mFakeExecutor, mUserTracker) {
+ return new ImageWallpaper(mFakeExecutor, mUserTracker, mWindowManagerProvider) {
@Override
public Engine onCreateEngine() {
return new CanvasEngine() {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/window/ui/viewmodel/WindowRootViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/window/ui/viewmodel/WindowRootViewModelTest.kt
index 3da4f29a6fcb..dc344aa1a66f 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/window/ui/viewmodel/WindowRootViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/window/ui/viewmodel/WindowRootViewModelTest.kt
@@ -73,7 +73,7 @@ class WindowRootViewModelTest : SysuiTestCase() {
assertThat(blurRadius).isEqualTo(0f)
- kosmos.windowRootViewBlurRepository.blurRadius.value = 60
+ kosmos.windowRootViewBlurRepository.blurRequestedByShade.value = 60
runCurrent()
assertThat(blurRadius).isEqualTo(0f)
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockAnimations.kt b/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockAnimations.kt
index 77c18eac289c..894327690b5e 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockAnimations.kt
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockAnimations.kt
@@ -61,4 +61,7 @@ interface ClockAnimations {
/** Runs when an animation when the view is tapped on the lockscreen */
fun onFidgetTap(x: Float, y: Float)
+
+ /** Update reactive axes for this clock */
+ fun onFontAxesChanged(style: ClockAxisStyle)
}
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockEvents.kt b/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockEvents.kt
index 0fc3470716fe..7c0e4866b00d 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockEvents.kt
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockEvents.kt
@@ -42,7 +42,4 @@ interface ClockEvents {
/** Call with zen/dnd information */
fun onZenDataChanged(data: ZenData)
-
- /** Update reactive axes for this clock */
- fun onFontAxesChanged(axes: ClockAxisStyle)
}
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockLogger.kt b/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockLogger.kt
index 5b67edda73cc..4f04aaa33b24 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockLogger.kt
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockLogger.kt
@@ -90,10 +90,11 @@ class ClockLogger(private val view: View?, buffer: MessageBuffer, tag: String) :
}
}
- fun updateAxes(lsFVar: String, aodFVar: String) {
- i({ "updateAxes(LS = $str1, AOD = $str2)" }) {
+ fun updateAxes(lsFVar: String, aodFVar: String, isAnimated: Boolean) {
+ i({ "updateAxes(LS = $str1, AOD = $str2, isAnimated=$bool1)" }) {
str1 = lsFVar
str2 = aodFVar
+ bool1 = isAnimated
}
}
diff --git a/packages/SystemUI/res-keyguard/values-bn/strings.xml b/packages/SystemUI/res-keyguard/values-bn/strings.xml
index fe6636bfa489..15471a69497d 100644
--- a/packages/SystemUI/res-keyguard/values-bn/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-bn/strings.xml
@@ -63,8 +63,8 @@
<string name="kg_bio_try_again_or_password" msgid="1473132729225398039">"আবার চেষ্টা করুন বা পাসওয়ার্ড লিখুন"</string>
<string name="kg_bio_try_again_or_pattern" msgid="4867893307468801501">"আবার চেষ্টা করুন বা প্যাটার্ন দিন"</string>
<string name="kg_bio_too_many_attempts_pin" msgid="5850845723433047605">"অনেক বেশিবার চেষ্টা করার পরে পিন দিতে হবে"</string>
- <string name="kg_bio_too_many_attempts_password" msgid="5551690347827728042">"অনেক বেশিবার চেষ্টা করার পরে পাসওয়ার্ড দিতে হবে"</string>
- <string name="kg_bio_too_many_attempts_pattern" msgid="736884689355181602">"অনেক বেশিবার চেষ্টা করার পরে প্যাটার্ন দিতে হবে"</string>
+ <string name="kg_bio_too_many_attempts_password" msgid="5551690347827728042">"অনেকবার চেষ্টা করার পর পাসওয়ার্ড দিতে হয়"</string>
+ <string name="kg_bio_too_many_attempts_pattern" msgid="736884689355181602">"অনেকবার চেষ্টা করার পর প্যাটার্ন দিতে হয়"</string>
<string name="kg_unlock_with_pin_or_fp" msgid="5635161174698729890">"পিন বা ফিঙ্গারপ্রিন্টের সাহায্যে আনলক করুন"</string>
<string name="kg_unlock_with_password_or_fp" msgid="2251295907826814237">"পাসওয়ার্ড বা ফিঙ্গারপ্রিন্টের সাহায্যে আনলক করুন"</string>
<string name="kg_unlock_with_pattern_or_fp" msgid="2391870539909135046">"প্যাটার্ন বা ফিঙ্গারপ্রিন্টের সাহায্যে আনলক করুন"</string>
diff --git a/packages/SystemUI/res/color/brightness_slider_track.xml b/packages/SystemUI/res/color/brightness_slider_track.xml
new file mode 100644
index 000000000000..c6e9b65fb7e8
--- /dev/null
+++ b/packages/SystemUI/res/color/brightness_slider_track.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2025 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.
+ -->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:color="@android:color/system_neutral2_500" android:lStar="40" />
+</selector> \ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/brightness_progress_drawable.xml b/packages/SystemUI/res/drawable/brightness_progress_drawable.xml
index 88d3ecb3f423..d38da7b766c1 100644
--- a/packages/SystemUI/res/drawable/brightness_progress_drawable.xml
+++ b/packages/SystemUI/res/drawable/brightness_progress_drawable.xml
@@ -25,7 +25,7 @@
<shape>
<size android:height="@dimen/rounded_slider_track_width" />
<corners android:radius="@dimen/rounded_slider_track_corner_radius" />
- <solid android:color="@androidprv:color/customColorShadeInactive" />
+ <solid android:color="@color/brightness_slider_track" />
</shape>
</inset>
</item>
diff --git a/packages/SystemUI/res/drawable/hearing_device_ambient_expand_icon_background.xml b/packages/SystemUI/res/drawable/hearing_device_ambient_expand_icon_background.xml
new file mode 100644
index 000000000000..2173641376fa
--- /dev/null
+++ b/packages/SystemUI/res/drawable/hearing_device_ambient_expand_icon_background.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2025 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.
+-->
+
+<inset xmlns:android="http://schemas.android.com/apk/res/android"
+ android:drawable="@drawable/hearing_device_ambient_icon_background"
+ android:insetTop="10dp"
+ android:insetBottom="10dp"/>
diff --git a/packages/SystemUI/res/drawable/hearing_device_ambient_icon_background.xml b/packages/SystemUI/res/drawable/hearing_device_ambient_icon_background.xml
new file mode 100644
index 000000000000..81ef3d7c11b7
--- /dev/null
+++ b/packages/SystemUI/res/drawable/hearing_device_ambient_icon_background.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2025 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.
+-->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+ android:shape="rectangle">
+ <solid android:color="@androidprv:color/materialColorSurfaceContainerHighest" />
+ <corners
+ android:bottomLeftRadius="?android:attr/dialogCornerRadius"
+ android:topLeftRadius="?android:attr/dialogCornerRadius"
+ android:bottomRightRadius="?android:attr/dialogCornerRadius"
+ android:topRightRadius="?android:attr/dialogCornerRadius" />
+</shape> \ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/rear_display_dialog_seekbar.xml b/packages/SystemUI/res/drawable/rear_display_dialog_seekbar.xml
index 73704f823033..f17cc96329c6 100644
--- a/packages/SystemUI/res/drawable/rear_display_dialog_seekbar.xml
+++ b/packages/SystemUI/res/drawable/rear_display_dialog_seekbar.xml
@@ -21,7 +21,7 @@
android:height="2dp"
android:width="@dimen/rear_display_progress_width">
<shape android:shape="rectangle">
- <solid android:color="@androidprv:color/materialColorSurfaceContainer" />
+ <solid android:color="?android:attr/colorAccent"/>
</shape>
</item>
<item
@@ -29,4 +29,4 @@
android:gravity="center_vertical|fill_horizontal">
<com.android.systemui.util.RoundedCornerProgressDrawable android:drawable="@drawable/rear_display_dialog_seekbar_progress" />
</item>
-</layer-list> \ No newline at end of file
+</layer-list>
diff --git a/packages/SystemUI/res/drawable/unpin_icon.xml b/packages/SystemUI/res/drawable/unpin_icon.xml
new file mode 100644
index 000000000000..4e2e15893884
--- /dev/null
+++ b/packages/SystemUI/res/drawable/unpin_icon.xml
@@ -0,0 +1,10 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="960"
+ android:viewportHeight="960"
+ android:tint="?attr/colorControlNormal">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M680,120L680,200L640,200L640,527L560,447L560,200L400,200L400,287L313,200L280,167L280,167L280,120L680,120ZM480,920L440,880L440,640L240,640L240,560L320,480L320,434L56,168L112,112L848,848L790,904L526,640L520,640L520,880L480,920ZM354,560L446,560L402,516L400,514L354,560ZM480,367L480,367L480,367L480,367ZM402,516L402,516L402,516L402,516Z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/vector_drawable_progress_indeterminate_horizontal_trimmed.xml b/packages/SystemUI/res/drawable/vector_drawable_progress_indeterminate_horizontal_trimmed.xml
index aec204f45aa7..7f6dc49505bb 100644
--- a/packages/SystemUI/res/drawable/vector_drawable_progress_indeterminate_horizontal_trimmed.xml
+++ b/packages/SystemUI/res/drawable/vector_drawable_progress_indeterminate_horizontal_trimmed.xml
@@ -38,7 +38,7 @@
<path
android:name="rect"
android:pathData="M -144.0,-5.0 l 288.0,0 l 0,10.0 l -288.0,0 Z"
- android:fillColor="?androidprv:attr/colorAccentPrimaryVariant" />
+ android:fillColor="@androidprv:color/materialColorPrimary" />
</group>
</group>
</vector> \ No newline at end of file
diff --git a/packages/SystemUI/res/layout/activity_rear_display_enabled.xml b/packages/SystemUI/res/layout/activity_rear_display_enabled.xml
index f900626b4da6..6b633e03f1f2 100644
--- a/packages/SystemUI/res/layout/activity_rear_display_enabled.xml
+++ b/packages/SystemUI/res/layout/activity_rear_display_enabled.xml
@@ -56,6 +56,7 @@
android:gravity="center_horizontal" />
<TextView
+ android:id="@+id/seekbar_instructions"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/rear_display_unfolded_front_screen_on_slide_to_cancel"
@@ -73,4 +74,13 @@
android:background="@null"
android:gravity="center_horizontal" />
+ <Button
+ android:id="@+id/cancel_button"
+ android:text="@string/cancel"
+ android:layout_width="@dimen/rear_display_animation_width_opened"
+ android:layout_height="wrap_content"
+ android:gravity="center_horizontal"
+ android:visibility="gone"
+ style="@style/Widget.Dialog.Button.BorderButton"/>
+
</LinearLayout>
diff --git a/packages/SystemUI/res/layout/biometric_prompt_one_pane_layout.xml b/packages/SystemUI/res/layout/biometric_prompt_one_pane_layout.xml
index 43808f215a81..4002f7808637 100644
--- a/packages/SystemUI/res/layout/biometric_prompt_one_pane_layout.xml
+++ b/packages/SystemUI/res/layout/biometric_prompt_one_pane_layout.xml
@@ -21,6 +21,9 @@
style="@style/AuthCredentialPanelStyle"
android:layout_width="0dp"
android:layout_height="0dp"
+ android:accessibilityLiveRegion="assertive"
+ android:importantForAccessibility="yes"
+ android:clickable="false"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@id/rightGuideline"
app:layout_constraintStart_toStartOf="@id/leftGuideline"
diff --git a/packages/SystemUI/res/layout/biometric_prompt_two_pane_layout.xml b/packages/SystemUI/res/layout/biometric_prompt_two_pane_layout.xml
index 51117a7845df..3c8cb6860a41 100644
--- a/packages/SystemUI/res/layout/biometric_prompt_two_pane_layout.xml
+++ b/packages/SystemUI/res/layout/biometric_prompt_two_pane_layout.xml
@@ -22,6 +22,9 @@ android:layout_height="match_parent">
style="@style/AuthCredentialPanelStyle"
android:layout_width="0dp"
android:layout_height="0dp"
+ android:accessibilityLiveRegion="assertive"
+ android:importantForAccessibility="yes"
+ android:clickable="false"
android:paddingHorizontal="16dp"
android:paddingVertical="16dp"
android:visibility="visible"
diff --git a/packages/SystemUI/res/layout/hearing_device_ambient_volume_layout.xml b/packages/SystemUI/res/layout/hearing_device_ambient_volume_layout.xml
index fd409a5a8bb1..d3e9db15dfdd 100644
--- a/packages/SystemUI/res/layout/hearing_device_ambient_volume_layout.xml
+++ b/packages/SystemUI/res/layout/hearing_device_ambient_volume_layout.xml
@@ -36,7 +36,8 @@
android:padding="12dp"
android:contentDescription="@string/hearing_devices_ambient_unmute"
android:src="@drawable/ic_ambient_volume"
- android:tint="@androidprv:color/materialColorOnSurface" />
+ android:tint="@androidprv:color/materialColorOnSurface"
+ android:background="@drawable/hearing_device_ambient_icon_background"/>
<TextView
android:id="@+id/ambient_title"
android:layout_width="0dp"
@@ -56,7 +57,8 @@
android:padding="10dp"
android:contentDescription="@string/hearing_devices_ambient_expand_controls"
android:src="@drawable/ic_hearing_device_expand"
- android:tint="@androidprv:color/materialColorOnSurface" />
+ android:tint="@androidprv:color/materialColorOnSurface"
+ android:background="@drawable/hearing_device_ambient_expand_icon_background"/>
</LinearLayout>
<LinearLayout
android:id="@+id/ambient_control_container"
diff --git a/packages/SystemUI/res/layout/promoted_permission_guts.xml b/packages/SystemUI/res/layout/promoted_permission_guts.xml
new file mode 100644
index 000000000000..50e5ae3c05ed
--- /dev/null
+++ b/packages/SystemUI/res/layout/promoted_permission_guts.xml
@@ -0,0 +1,73 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 2017, 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.
+-->
+
+<com.android.systemui.statusbar.notification.row.PromotedPermissionGutsContent
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:paddingTop="2dp"
+ android:paddingBottom="2dp"
+ android:background="@androidprv:color/materialColorSurfaceContainerHigh"
+ android:theme="@style/Theme.SystemUI"
+ >
+
+ <RelativeLayout
+ android:id="@+id/promoted_guts"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:minHeight="@dimen/notification_2025_min_height">
+
+ <ImageView
+ android:id="@+id/unpin_icon"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:src="@drawable/unpin_icon"
+ android:layout_alignParentTop="true"
+ android:layout_centerHorizontal="true"
+ android:padding="@dimen/notification_importance_button_padding"
+ />
+
+ <TextView
+ android:id="@+id/demote_explain"
+ android:layout_width="400dp"
+ android:layout_height="wrap_content"
+ android:layout_alignParentLeft="true"
+ android:layout_below="@id/unpin_icon"
+ android:layout_toLeftOf="@id/undo"
+ android:padding="@*android:dimen/notification_content_margin_end"
+ android:textColor="@androidprv:color/materialColorOnSurface"
+ android:minWidth="@dimen/min_clickable_item_size"
+ android:minHeight="@dimen/min_clickable_item_size"
+ style="@style/TextAppearance.NotificationInfo.Button" />
+
+ <TextView
+ android:id="@+id/undo"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_below="@id/unpin_icon"
+ android:layout_alignParentRight="true"
+ android:padding="@*android:dimen/notification_content_margin_end"
+ android:textColor="@androidprv:color/materialColorOnSurface"
+ android:minWidth="@dimen/min_clickable_item_size"
+ android:minHeight="@dimen/min_clickable_item_size"
+ android:text="@string/snooze_undo"
+ style="@style/TextAppearance.NotificationInfo.Button" />
+ </RelativeLayout>
+
+</com.android.systemui.statusbar.notification.row.PromotedPermissionGutsContent>
diff --git a/packages/SystemUI/res/layout/volume_dialog.xml b/packages/SystemUI/res/layout/volume_dialog.xml
index a1fa54cf592d..e094155f9041 100644
--- a/packages/SystemUI/res/layout/volume_dialog.xml
+++ b/packages/SystemUI/res/layout/volume_dialog.xml
@@ -16,17 +16,18 @@
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/volume_dialog"
- android:layout_width="match_parent"
+ android:layout_width="wrap_content"
android:layout_height="match_parent"
android:alpha="0"
- android:clipChildren="false">
+ android:clipChildren="false"
+ android:minWidth="@dimen/volume_dialog_window_width">
<View
android:id="@+id/volume_dialog_background"
android:layout_width="@dimen/volume_dialog_width"
android:layout_height="0dp"
android:layout_marginTop="@dimen/volume_dialog_background_top_margin"
- android:layout_marginBottom="@dimen/volume_dialog_background_vertical_margin"
+ android:layout_marginBottom="@dimen/volume_dialog_background_margin_negative"
android:background="@drawable/volume_dialog_background"
app:layout_constraintBottom_toBottomOf="@id/volume_dialog_bottom_section_container"
app:layout_constraintEnd_toEndOf="@id/volume_dialog_main_slider_container"
diff --git a/packages/SystemUI/res/layout/volume_dialog_slider_floating.xml b/packages/SystemUI/res/layout/volume_dialog_slider_floating.xml
index db800aa4a873..1dba96cafb70 100644
--- a/packages/SystemUI/res/layout/volume_dialog_slider_floating.xml
+++ b/packages/SystemUI/res/layout/volume_dialog_slider_floating.xml
@@ -17,8 +17,8 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/volume_dialog_floating_slider_background"
- android:paddingHorizontal="@dimen/volume_dialog_floating_sliders_horizontal_padding"
- android:paddingVertical="@dimen/volume_dialog_floating_sliders_vertical_padding">
+ android:paddingHorizontal="@dimen/volume_dialog_floating_sliders_padding"
+ android:paddingVertical="@dimen/volume_dialog_floating_sliders_padding">
<include layout="@layout/volume_dialog_slider" />
</FrameLayout> \ No newline at end of file
diff --git a/packages/SystemUI/res/layout/volume_dialog_top_section.xml b/packages/SystemUI/res/layout/volume_dialog_top_section.xml
index b7455471d9a6..9d3598800bbf 100644
--- a/packages/SystemUI/res/layout/volume_dialog_top_section.xml
+++ b/packages/SystemUI/res/layout/volume_dialog_top_section.xml
@@ -22,15 +22,15 @@
android:clipChildren="false"
android:clipToPadding="false"
android:gravity="center"
- android:paddingEnd="@dimen/volume_dialog_buttons_margin"
+ android:paddingEnd="@dimen/volume_dialog_background_margin"
app:layoutDescription="@xml/volume_dialog_ringer_drawer_motion_scene">
<View
android:id="@+id/ringer_buttons_background"
android:layout_width="@dimen/volume_dialog_width"
android:layout_height="0dp"
- android:layout_marginTop="@dimen/volume_dialog_background_vertical_margin"
- android:layout_marginBottom="@dimen/volume_dialog_background_vertical_margin"
+ android:layout_marginTop="@dimen/volume_dialog_background_margin_negative"
+ android:layout_marginBottom="@dimen/volume_dialog_background_margin_negative"
android:background="@drawable/volume_dialog_ringer_background"
android:visibility="gone" />
diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml
index bc08a3e2c628..5ed44a804a0d 100644
--- a/packages/SystemUI/res/values-af/strings.xml
+++ b/packages/SystemUI/res/values-af/strings.xml
@@ -419,6 +419,11 @@
<string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Klik om nuwe toestel saam te bind"</string>
<string name="hearing_devices_presets_error" msgid="350363093458408536">"Kon nie voorafstelling opdateer nie"</string>
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"Voorafstelling"</string>
+ <string name="hearing_devices_input_routing_label" msgid="730396728151232306">"Verstekmikrofoon vir oproepe"</string>
+ <string-array name="hearing_device_input_routing_options">
+ <item msgid="4582190415045337003">"Gehoortoestelmikrofoon"</item>
+ <item msgid="8501466270452446450">"Hierdie foon se mikrofoon"</item>
+ </string-array>
<string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"Gekies"</string>
<string name="hearing_devices_ambient_label" msgid="629440938614895797">"Omgewing"</string>
<string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Links"</string>
@@ -582,6 +587,8 @@
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Wanneer jy ’n app deel, is enigiets wat in die app wys of speel, sigbaar aan <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Wees dus versigtig met dinge soos wagwoorde, betalingbesonderhede, boodskappe, foto’s, en oudio en video."</string>
<string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Deel skerm"</string>
<string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g> het hierdie opsie gedeaktiveer"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_not_supported (4860247304058870233) -->
+ <skip />
<string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"Kies app om te deel"</string>
<string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Saai jou skerm uit?"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Saai een app uit"</string>
@@ -794,8 +801,7 @@
<string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Aan – gesiggegrond"</string>
<string name="inline_done_button" msgid="6043094985588909584">"Klaar"</string>
<string name="inline_ok_button" msgid="603075490581280343">"Pas toe"</string>
- <!-- no translation found for inline_turn_off_notifications (2653064779176881329) -->
- <skip />
+ <string name="inline_turn_off_notifications" msgid="2653064779176881329">"Skakel af"</string>
<string name="notification_silence_title" msgid="8608090968400832335">"Stil"</string>
<string name="notification_alert_title" msgid="3656229781017543655">"Verstek"</string>
<string name="notification_automatic_title" msgid="3745465364578762652">"Outomaties"</string>
@@ -977,9 +983,13 @@
<string name="right_icon" msgid="1103955040645237425">"Regs-ikoon"</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"Hou en sleep om teëls by te voeg"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"Hou en sleep om teëls te herrangskik"</string>
+ <!-- no translation found for tap_to_position_tile (6282815817773342757) -->
+ <skip />
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"Sleep hierheen om te verwyder"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"Jy moet minstens <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> teëls hê"</string>
<string name="qs_edit" msgid="5583565172803472437">"Wysig"</string>
+ <!-- no translation found for qs_edit_tiles (2105215324060865035) -->
+ <skip />
<string name="tuner_time" msgid="2450785840990529997">"Tyd"</string>
<string-array name="clock_options">
<item msgid="3986445361435142273">"Wys ure, minute en sekondes"</item>
@@ -995,6 +1005,10 @@
<string name="other" msgid="429768510980739978">"Ander"</string>
<string name="accessibility_qs_edit_toggle_tile_size_action" msgid="1485194410119733586">"wissel die teël se grootte"</string>
<string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"verwyder teël"</string>
+ <!-- no translation found for accessibility_qs_edit_toggle_placement_mode (3870429389210569610) -->
+ <skip />
+ <!-- no translation found for accessibility_qs_edit_toggle_selection (2201248304072372239) -->
+ <skip />
<string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"voeg teël by die laaste posisie"</string>
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Skuif teël"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"Voeg teël by die verlangde posisie"</string>
@@ -1570,5 +1584,7 @@
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Onbekend"</string>
<string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"Stel alle teëls terug?"</string>
<string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Alle Kitsinstellingsteëls sal na die toestel se oorspronklike instellings teruggestel word"</string>
+ <!-- no translation found for demote_explain_text (1600426458580544250) -->
+ <skip />
<string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
</resources>
diff --git a/packages/SystemUI/res/values-af/tiles_states_strings.xml b/packages/SystemUI/res/values-af/tiles_states_strings.xml
index 64912d4ff9c1..36830c9178c2 100644
--- a/packages/SystemUI/res/values-af/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-af/tiles_states_strings.xml
@@ -56,9 +56,11 @@
<item msgid="5376619709702103243">"Af"</item>
<item msgid="4875147066469902392">"Aan"</item>
</string-array>
- <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) -->
- <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) -->
- <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) -->
+ <string-array name="tile_states_modes_dnd">
+ <item msgid="6509540227356524582">"Onbeskikbaar"</item>
+ <item msgid="8589336868985358191">"Af"</item>
+ <item msgid="726072717827778234">"Aan"</item>
+ </string-array>
<string-array name="tile_states_flashlight">
<item msgid="3465257127433353857">"Onbeskikbaar"</item>
<item msgid="5044688398303285224">"Af"</item>
diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml
index 1c158b388d74..de3e4370f4f7 100644
--- a/packages/SystemUI/res/values-am/strings.xml
+++ b/packages/SystemUI/res/values-am/strings.xml
@@ -419,6 +419,11 @@
<string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"አዲስ መሣሪያ ለማጣመር ጠቅ ያድርጉ"</string>
<string name="hearing_devices_presets_error" msgid="350363093458408536">"ቅድመ-ቅምጥን ማዘመን አልተቻለም"</string>
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"ቅድመ-ቅምጥ"</string>
+ <string name="hearing_devices_input_routing_label" msgid="730396728151232306">"ለጥሪዎች ነባሪ ማይክሮፎን"</string>
+ <string-array name="hearing_device_input_routing_options">
+ <item msgid="4582190415045337003">"የመስሚያ አጋዥ መሣሪያ ማይክሮፎን"</item>
+ <item msgid="8501466270452446450">"የዚህ ስልክ ማይክሮፎን"</item>
+ </string-array>
<string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"ተመርጧል"</string>
<string name="hearing_devices_ambient_label" msgid="629440938614895797">"በዙሪያ ያሉ"</string>
<string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"ግራ"</string>
@@ -582,6 +587,8 @@
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"መተግበሪያን ሲያጋሩ በዚያ መተግበሪያ ውስጥ የሚታይ ወይም የሚጫወት ማንኛውም ነገር ለ<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ይታያል። ስለዚህ እንደ የይለፍ ቃላት፣ የክፍያ ዝርዝሮች፣ መልዕክቶች፣ ፎቶዎች እና ኦዲዮ እና ቪድዮ ላሉ ነገሮች ጥንቃቄ ያድርጉ።"</string>
<string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"ማያ ገፅ አጋራ"</string>
<string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g> ይህን አማራጭ አሰናክሏል"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_not_supported (4860247304058870233) -->
+ <skip />
<string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"ለማጋራት መተግበሪያ ይምረጡ"</string>
<string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"ማያ ገፅዎ cast ይደረግ?"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"አንድ መተግበሪያ cast ያድርጉ"</string>
@@ -794,8 +801,7 @@
<string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"በርቷል - መልክ ላይ የተመሠረተ"</string>
<string name="inline_done_button" msgid="6043094985588909584">"ተከናውኗል"</string>
<string name="inline_ok_button" msgid="603075490581280343">"ተግብር"</string>
- <!-- no translation found for inline_turn_off_notifications (2653064779176881329) -->
- <skip />
+ <string name="inline_turn_off_notifications" msgid="2653064779176881329">"አጥፋ"</string>
<string name="notification_silence_title" msgid="8608090968400832335">"ፀጥ ያለ"</string>
<string name="notification_alert_title" msgid="3656229781017543655">"ነባሪ"</string>
<string name="notification_automatic_title" msgid="3745465364578762652">"ራስ-ሰር"</string>
@@ -977,9 +983,13 @@
<string name="right_icon" msgid="1103955040645237425">"የቀኝ አዶ"</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"ፋይሎችን ለማከል ይዘት ይጎትቱ"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"ሰድሮችን ዳግም ለማስተካከል ይያዙ እና ይጎትቱ።"</string>
+ <!-- no translation found for tap_to_position_tile (6282815817773342757) -->
+ <skip />
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"ለማስወገድ ወደዚህ ይጎትቱ"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"ቢያንስ <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> ሰቆች ያስፈልገዎታል"</string>
<string name="qs_edit" msgid="5583565172803472437">"አርትዕ"</string>
+ <!-- no translation found for qs_edit_tiles (2105215324060865035) -->
+ <skip />
<string name="tuner_time" msgid="2450785840990529997">"ሰዓት"</string>
<string-array name="clock_options">
<item msgid="3986445361435142273">"ሰዓቶችን፣ ደቂቃዎችን፣ ሴኮንዶችን አሳይ"</item>
@@ -995,6 +1005,10 @@
<string name="other" msgid="429768510980739978">"ሌላ"</string>
<string name="accessibility_qs_edit_toggle_tile_size_action" msgid="1485194410119733586">"የርዕሱን መጠን ይቀያይሩ"</string>
<string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"ሰቅ አስወግድ"</string>
+ <!-- no translation found for accessibility_qs_edit_toggle_placement_mode (3870429389210569610) -->
+ <skip />
+ <!-- no translation found for accessibility_qs_edit_toggle_selection (2201248304072372239) -->
+ <skip />
<string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"በመጨረሻው ቦታ ላይ ሰቅ ያክሉ"</string>
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"ሰቁን ውሰድ"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"ወደተፈለገው ቦታ ሰቅ ያክሉ"</string>
@@ -1570,5 +1584,7 @@
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"ያልታወቀ"</string>
<string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"ሁሉም ሰቆች ዳግም ይጀምሩ?"</string>
<string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"ሁሉም የፈጣን ቅንብሮች ሰቆች ወደ የመሣሪያው የመጀመሪያ ቅንብሮች ዳግም ይጀምራሉ"</string>
+ <!-- no translation found for demote_explain_text (1600426458580544250) -->
+ <skip />
<string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>፣ <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
</resources>
diff --git a/packages/SystemUI/res/values-am/tiles_states_strings.xml b/packages/SystemUI/res/values-am/tiles_states_strings.xml
index 24666e7c12f7..bd240651c07c 100644
--- a/packages/SystemUI/res/values-am/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-am/tiles_states_strings.xml
@@ -56,9 +56,11 @@
<item msgid="5376619709702103243">"ጠፍቷል"</item>
<item msgid="4875147066469902392">"በርቷል"</item>
</string-array>
- <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) -->
- <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) -->
- <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) -->
+ <string-array name="tile_states_modes_dnd">
+ <item msgid="6509540227356524582">"አይገኝም"</item>
+ <item msgid="8589336868985358191">"ጠፍቷል"</item>
+ <item msgid="726072717827778234">"በርቷል"</item>
+ </string-array>
<string-array name="tile_states_flashlight">
<item msgid="3465257127433353857">"አይገኝም"</item>
<item msgid="5044688398303285224">"ጠፍቷል"</item>
diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml
index b494f4ec5fad..b0f96d666255 100644
--- a/packages/SystemUI/res/values-ar/strings.xml
+++ b/packages/SystemUI/res/values-ar/strings.xml
@@ -419,6 +419,11 @@
<string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"انقر لإقران جهاز جديد"</string>
<string name="hearing_devices_presets_error" msgid="350363093458408536">"تعذَّر تعديل الإعداد المسبق"</string>
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"الإعدادات المسبقة"</string>
+ <string name="hearing_devices_input_routing_label" msgid="730396728151232306">"الميكروفون التلقائي لإجراء المكالمات"</string>
+ <string-array name="hearing_device_input_routing_options">
+ <item msgid="4582190415045337003">"ميكروفون سماعة الأذن الطبية"</item>
+ <item msgid="8501466270452446450">"ميكروفون هذا الهاتف"</item>
+ </string-array>
<string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"تمّ اختياره"</string>
<string name="hearing_devices_ambient_label" msgid="629440938614895797">"الأصوات المحيطة"</string>
<string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"اليسرى"</string>
@@ -582,6 +587,7 @@
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"ستتم مشاركة كل المحتوى المعروض أو المشغَّل على شاشة هذا التطبيق مع تطبيق \"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>\"، لذا يُرجى توخي الحذر بشأن المعلومات الظاهرة على الشاشة، مثل كلمات المرور وتفاصيل الدفع والرسائل والصور والمقاطع الصوتية والفيديوهات."</string>
<string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"مشاركة الشاشة"</string>
<string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"تم إيقاف هذا الخيار من خلال تطبيق <xliff:g id="APP_NAME">%1$s</xliff:g>."</string>
+ <string name="media_projection_entry_app_permission_dialog_single_app_not_supported" msgid="4860247304058870233">"لا يتيح التطبيق هذه الميزة"</string>
<string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"يُرجى اختيار تطبيق لمشاركة محتواه"</string>
<string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"هل تريد بث محتوى الشاشة؟"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"بث محتوى تطبيق واحد"</string>
@@ -794,8 +800,7 @@
<string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"تفعيل - استنادًا للوجه"</string>
<string name="inline_done_button" msgid="6043094985588909584">"تمّ"</string>
<string name="inline_ok_button" msgid="603075490581280343">"تطبيق"</string>
- <!-- no translation found for inline_turn_off_notifications (2653064779176881329) -->
- <skip />
+ <string name="inline_turn_off_notifications" msgid="2653064779176881329">"إيقاف"</string>
<string name="notification_silence_title" msgid="8608090968400832335">"إشعارات صامتة"</string>
<string name="notification_alert_title" msgid="3656229781017543655">"تلقائية"</string>
<string name="notification_automatic_title" msgid="3745465364578762652">"تلقائي"</string>
@@ -977,9 +982,13 @@
<string name="right_icon" msgid="1103955040645237425">"رمز اليمين"</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"اضغط باستمرار مع السحب لإضافة المربّعات"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"اضغط باستمرار مع السحب لإعادة ترتيب الميزات"</string>
+ <!-- no translation found for tap_to_position_tile (6282815817773342757) -->
+ <skip />
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"اسحب هنا للإزالة"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"الحدّ الأدنى من عدد المربعات الذي تحتاج إليه هو <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g>"</string>
<string name="qs_edit" msgid="5583565172803472437">"تعديل"</string>
+ <!-- no translation found for qs_edit_tiles (2105215324060865035) -->
+ <skip />
<string name="tuner_time" msgid="2450785840990529997">"الوقت"</string>
<string-array name="clock_options">
<item msgid="3986445361435142273">"عرض الساعات والدقائق والثواني"</item>
@@ -995,6 +1004,10 @@
<string name="other" msgid="429768510980739978">"غير ذلك"</string>
<string name="accessibility_qs_edit_toggle_tile_size_action" msgid="1485194410119733586">"تبديل حجم المربّع"</string>
<string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"إزالة بطاقة"</string>
+ <!-- no translation found for accessibility_qs_edit_toggle_placement_mode (3870429389210569610) -->
+ <skip />
+ <!-- no translation found for accessibility_qs_edit_toggle_selection (2201248304072372239) -->
+ <skip />
<string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"إضافة مربّع إلى الموضع الأخير"</string>
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"نقل بطاقة"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"إضافة مربّع إلى الموضع المطلوب"</string>
@@ -1570,5 +1583,7 @@
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"غير معروفة"</string>
<string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"هل تريد إعادة ضبط كل المربّعات؟"</string>
<string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"ستتم إعادة ضبط جميع مربّعات \"الإعدادات السريعة\" إلى الإعدادات الأصلية للجهاز"</string>
+ <!-- no translation found for demote_explain_text (1600426458580544250) -->
+ <skip />
<string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"\"<xliff:g id="STREAM_NAME">%1$s</xliff:g>\"، <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ar/tiles_states_strings.xml b/packages/SystemUI/res/values-ar/tiles_states_strings.xml
index 31607b9548a4..ba73b9bef9c1 100644
--- a/packages/SystemUI/res/values-ar/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-ar/tiles_states_strings.xml
@@ -56,9 +56,11 @@
<item msgid="5376619709702103243">"الميزة غير مفعّلة"</item>
<item msgid="4875147066469902392">"الميزة مفعّلة"</item>
</string-array>
- <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) -->
- <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) -->
- <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) -->
+ <string-array name="tile_states_modes_dnd">
+ <item msgid="6509540227356524582">"غير متوفّر"</item>
+ <item msgid="8589336868985358191">"غير مفعَّل"</item>
+ <item msgid="726072717827778234">"مفعّل"</item>
+ </string-array>
<string-array name="tile_states_flashlight">
<item msgid="3465257127433353857">"الميزة غير متاحة"</item>
<item msgid="5044688398303285224">"الميزة غير مفعّلة"</item>
diff --git a/packages/SystemUI/res/values-as/strings.xml b/packages/SystemUI/res/values-as/strings.xml
index d3ec875711f5..ccec3ddd1711 100644
--- a/packages/SystemUI/res/values-as/strings.xml
+++ b/packages/SystemUI/res/values-as/strings.xml
@@ -419,6 +419,11 @@
<string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"নতুন ডিভাইচ পেয়াৰ কৰিবলৈ ক্লিক কৰক"</string>
<string name="hearing_devices_presets_error" msgid="350363093458408536">"প্ৰিছেট আপডে’ট কৰিব পৰা নগ’ল"</string>
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"প্ৰিছেট"</string>
+ <string name="hearing_devices_input_routing_label" msgid="730396728151232306">"কলৰ বাবে ডিফ’ল্ট মাইক্ৰ’ফ’ন"</string>
+ <string-array name="hearing_device_input_routing_options">
+ <item msgid="4582190415045337003">"শ্ৰৱণ যন্ত্ৰৰ মাইক্ৰ’ফ’ন"</item>
+ <item msgid="8501466270452446450">"এই ফ’নটোৰ মাইক্ৰ’ফ’ন"</item>
+ </string-array>
<string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"বাছনি কৰা হৈছে"</string>
<string name="hearing_devices_ambient_label" msgid="629440938614895797">"আশ-পাশ"</string>
<string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"বাওঁফাল"</string>
@@ -582,6 +587,8 @@
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"আপুনি কোনো এপ্‌ শ্বেয়াৰ কৰি থাকোঁতে সেই এপ্‌টোত দেখুওৱা বা প্লে’ কৰা যিকোনো বস্তু <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>ত দৃশ্যমান হয়। সেয়ে পাছৱৰ্ড, পৰিশোধৰ সবিশেষ, বাৰ্তা, ফট’ আৰু অডিঅ’ আৰু ভিডিঅ’ৰ ক্ষেত্ৰত সাৱধান হওক।"</string>
<string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"স্ক্ৰীন শ্বেয়াৰ কৰক"</string>
<string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g>এ এই বিকল্পটো অক্ষম কৰিছে"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_not_supported (4860247304058870233) -->
+ <skip />
<string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"শ্বেয়াৰ কৰিবলৈ এপ্ বাছনি কৰক"</string>
<string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"আপোনাৰ স্ক্ৰীনখন কাষ্ট কৰিবনে?"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"এটা এপ্‌ কাষ্ট কৰক"</string>
@@ -794,8 +801,7 @@
<string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"অন আছে - মুখাৱয়ব ভিত্তিক"</string>
<string name="inline_done_button" msgid="6043094985588909584">"কৰা হ’ল"</string>
<string name="inline_ok_button" msgid="603075490581280343">"প্ৰয়োগ কৰক"</string>
- <!-- no translation found for inline_turn_off_notifications (2653064779176881329) -->
- <skip />
+ <string name="inline_turn_off_notifications" msgid="2653064779176881329">"অফ কৰক"</string>
<string name="notification_silence_title" msgid="8608090968400832335">"নীৰৱ"</string>
<string name="notification_alert_title" msgid="3656229781017543655">"ডিফ’ল্ট"</string>
<string name="notification_automatic_title" msgid="3745465364578762652">"স্বয়ংক্ৰিয়"</string>
@@ -977,9 +983,13 @@
<string name="right_icon" msgid="1103955040645237425">"সোঁ আইকন"</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"টাইল যোগ কৰিবলৈ হেঁচি ধৰি টানি আনক"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"টাইলসমূহ পুনৰ সজাবলৈ ধৰি টানক"</string>
+ <!-- no translation found for tap_to_position_tile (6282815817773342757) -->
+ <skip />
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"আঁতৰাবৰ বাবে টানি আনি ইয়াত এৰি দিয়ক"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"আপোনাক অতিকমেও <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g>খন টাইল লাগিব"</string>
<string name="qs_edit" msgid="5583565172803472437">"সম্পাদনা কৰক"</string>
+ <!-- no translation found for qs_edit_tiles (2105215324060865035) -->
+ <skip />
<string name="tuner_time" msgid="2450785840990529997">"সময়"</string>
<string-array name="clock_options">
<item msgid="3986445361435142273">"ঘন্টা, মিনিট আৰু ছেকেণ্ড দেখুৱাওক"</item>
@@ -995,6 +1005,10 @@
<string name="other" msgid="429768510980739978">"অন্যান্য"</string>
<string name="accessibility_qs_edit_toggle_tile_size_action" msgid="1485194410119733586">"টাইলৰ আকাৰ ট’গল কৰক"</string>
<string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"টাইল আঁতৰাবলৈ"</string>
+ <!-- no translation found for accessibility_qs_edit_toggle_placement_mode (3870429389210569610) -->
+ <skip />
+ <!-- no translation found for accessibility_qs_edit_toggle_selection (2201248304072372239) -->
+ <skip />
<string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"অন্তিম স্থানত টাইল যোগ দিয়ক"</string>
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"টাইল স্থানান্তৰ কৰক"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"বিচৰা স্থানত টাইল যোগ দিয়ক"</string>
@@ -1570,5 +1584,7 @@
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"অজ্ঞাত"</string>
<string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"আটাইবোৰ টাইল ৰিছেট কৰিবনে?"</string>
<string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"আটাইবোৰ ক্ষিপ্ৰ ছেটিঙৰ টাইল ডিভাইচৰ মূল ছেটিংছলৈ ৰিছেট হ’ব"</string>
+ <!-- no translation found for demote_explain_text (1600426458580544250) -->
+ <skip />
<string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
</resources>
diff --git a/packages/SystemUI/res/values-as/tiles_states_strings.xml b/packages/SystemUI/res/values-as/tiles_states_strings.xml
index 39bd63e5d27a..d07f11b205cc 100644
--- a/packages/SystemUI/res/values-as/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-as/tiles_states_strings.xml
@@ -56,9 +56,11 @@
<item msgid="5376619709702103243">"অফ আছে"</item>
<item msgid="4875147066469902392">"অন কৰা আছে"</item>
</string-array>
- <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) -->
- <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) -->
- <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) -->
+ <string-array name="tile_states_modes_dnd">
+ <item msgid="6509540227356524582">"উপলব্ধ নহয়"</item>
+ <item msgid="8589336868985358191">"অফ আছে"</item>
+ <item msgid="726072717827778234">"অন আছে"</item>
+ </string-array>
<string-array name="tile_states_flashlight">
<item msgid="3465257127433353857">"উপলব্ধ নহয়"</item>
<item msgid="5044688398303285224">"অফ আছে"</item>
diff --git a/packages/SystemUI/res/values-az/strings.xml b/packages/SystemUI/res/values-az/strings.xml
index 9655ab75e61b..37091c38fba2 100644
--- a/packages/SystemUI/res/values-az/strings.xml
+++ b/packages/SystemUI/res/values-az/strings.xml
@@ -419,6 +419,11 @@
<string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Yeni cihaz birləşdirmək üçün klikləyin"</string>
<string name="hearing_devices_presets_error" msgid="350363093458408536">"Hazır ayar güncəllənmədi"</string>
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"Hazır Ayar"</string>
+ <string name="hearing_devices_input_routing_label" msgid="730396728151232306">"Zənglər üçün defolt mikrofon"</string>
+ <string-array name="hearing_device_input_routing_options">
+ <item msgid="4582190415045337003">"Eşitmə aparatı mikrofonu"</item>
+ <item msgid="8501466270452446450">"Bu telefonun mikrofonu"</item>
+ </string-array>
<string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"Seçilib"</string>
<string name="hearing_devices_ambient_label" msgid="629440938614895797">"Ətraf mühit"</string>
<string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Sol"</string>
@@ -582,6 +587,7 @@
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Tətbiq paylaşdığınız zaman həmin tətbiqdə göstərilən və ya işə salınan hər şey <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> üçün görünən olacaq. Parol, ödəniş məlumatı, mesaj, foto, habelə audio və video kimi məlumatlarla bağlı diqqətli olun."</string>
<string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Ekranı paylaşın"</string>
<string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g> bu seçimi deaktiv edib"</string>
+ <string name="media_projection_entry_app_permission_dialog_single_app_not_supported" msgid="4860247304058870233">"Tətbiq tərəfindən dəstəklənmir"</string>
<string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"Paylaşmaq üçün tətbiq seçin"</string>
<string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Ekran yayımlansın?"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Bir tətbiqi yayımlayın"</string>
@@ -794,8 +800,7 @@
<string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Aktiv - Üz əsaslı"</string>
<string name="inline_done_button" msgid="6043094985588909584">"Hazırdır"</string>
<string name="inline_ok_button" msgid="603075490581280343">"Tətbiq edin"</string>
- <!-- no translation found for inline_turn_off_notifications (2653064779176881329) -->
- <skip />
+ <string name="inline_turn_off_notifications" msgid="2653064779176881329">"Söndürün"</string>
<string name="notification_silence_title" msgid="8608090968400832335">"Səssiz"</string>
<string name="notification_alert_title" msgid="3656229781017543655">"Defolt"</string>
<string name="notification_automatic_title" msgid="3745465364578762652">"Avtomatik"</string>
@@ -977,9 +982,13 @@
<string name="right_icon" msgid="1103955040645237425">"Sağ ikona"</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"Mozaika əlavə etmək üçün basıb saxlayaraq çəkin"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"İkonları yenidən düzənləmək üçün saxlayaraq dartın"</string>
+ <!-- no translation found for tap_to_position_tile (6282815817773342757) -->
+ <skip />
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"Silmək üçün bura sürüşdürün"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"Minimum <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> mozaika lazımdır"</string>
<string name="qs_edit" msgid="5583565172803472437">"Redaktə edin"</string>
+ <!-- no translation found for qs_edit_tiles (2105215324060865035) -->
+ <skip />
<string name="tuner_time" msgid="2450785840990529997">"Vaxt"</string>
<string-array name="clock_options">
<item msgid="3986445361435142273">"Saat, dəqiqə və saniyəni göstərin"</item>
@@ -995,6 +1004,10 @@
<string name="other" msgid="429768510980739978">"Digər"</string>
<string name="accessibility_qs_edit_toggle_tile_size_action" msgid="1485194410119733586">"mozaik ölçüsünü aktiv/deaktiv edin"</string>
<string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"lövhəni silin"</string>
+ <!-- no translation found for accessibility_qs_edit_toggle_placement_mode (3870429389210569610) -->
+ <skip />
+ <!-- no translation found for accessibility_qs_edit_toggle_selection (2201248304072372239) -->
+ <skip />
<string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"son mövqeyə mozaik əlavə edin"</string>
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Lövhəni köçürün"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"İstənilən mövqeyə mozaik əlavə edin"</string>
@@ -1570,5 +1583,7 @@
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Naməlum"</string>
<string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"Bütün mozaiklər sıfırlansın?"</string>
<string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Bütün Sürətli Ayarlar mozaiki cihazın orijinal ayarlarına sıfırlanacaq"</string>
+ <!-- no translation found for demote_explain_text (1600426458580544250) -->
+ <skip />
<string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
</resources>
diff --git a/packages/SystemUI/res/values-az/tiles_states_strings.xml b/packages/SystemUI/res/values-az/tiles_states_strings.xml
index a78e67268f82..9fdca5deed05 100644
--- a/packages/SystemUI/res/values-az/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-az/tiles_states_strings.xml
@@ -56,9 +56,11 @@
<item msgid="5376619709702103243">"Deaktiv"</item>
<item msgid="4875147066469902392">"Aktiv"</item>
</string-array>
- <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) -->
- <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) -->
- <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) -->
+ <string-array name="tile_states_modes_dnd">
+ <item msgid="6509540227356524582">"Əlçatan deyil"</item>
+ <item msgid="8589336868985358191">"Deaktiv"</item>
+ <item msgid="726072717827778234">"Aktiv"</item>
+ </string-array>
<string-array name="tile_states_flashlight">
<item msgid="3465257127433353857">"Əlçatan deyil"</item>
<item msgid="5044688398303285224">"Deaktiv"</item>
diff --git a/packages/SystemUI/res/values-b+sr+Latn/strings.xml b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
index f46399659530..98aabe744722 100644
--- a/packages/SystemUI/res/values-b+sr+Latn/strings.xml
+++ b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
@@ -419,6 +419,11 @@
<string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Kliknite da biste uparili nov uređaj"</string>
<string name="hearing_devices_presets_error" msgid="350363093458408536">"Ažuriranje zadatih podešavanja nije uspelo"</string>
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"Unapred određena podešavanja"</string>
+ <string name="hearing_devices_input_routing_label" msgid="730396728151232306">"Podrazumevani mikrofon za pozive"</string>
+ <string-array name="hearing_device_input_routing_options">
+ <item msgid="4582190415045337003">"Mikrofon slušnog aparata"</item>
+ <item msgid="8501466270452446450">"Mikrofon ovog telefona"</item>
+ </string-array>
<string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"Izabrano"</string>
<string name="hearing_devices_ambient_label" msgid="629440938614895797">"Okruženje"</string>
<string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Levo"</string>
@@ -582,6 +587,7 @@
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Kada delite aplikaciju, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> vidi sav sadržaj koji se prikazuje ili pušta u njoj. Zato pazite na lozinke, informacije o plaćanju, poruke, slike, audio i video sadržaj."</string>
<string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Deli ekran"</string>
<string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"Aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> je onemogućila ovu opciju"</string>
+ <string name="media_projection_entry_app_permission_dialog_single_app_not_supported" msgid="4860247304058870233">"Aplikacija to ne podržava"</string>
<string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"Odaberite aplikaciju za deljenje"</string>
<string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Želite da prebacite ekran?"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Prebaci jednu aplikaciju"</string>
@@ -794,8 +800,7 @@
<string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Uključeno – na osnovu lica"</string>
<string name="inline_done_button" msgid="6043094985588909584">"Gotovo"</string>
<string name="inline_ok_button" msgid="603075490581280343">"Primeni"</string>
- <!-- no translation found for inline_turn_off_notifications (2653064779176881329) -->
- <skip />
+ <string name="inline_turn_off_notifications" msgid="2653064779176881329">"Isključi"</string>
<string name="notification_silence_title" msgid="8608090968400832335">"Nečujno"</string>
<string name="notification_alert_title" msgid="3656229781017543655">"Podrazumevano"</string>
<string name="notification_automatic_title" msgid="3745465364578762652">"Automatska"</string>
@@ -977,9 +982,13 @@
<string name="right_icon" msgid="1103955040645237425">"Desna ikona"</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"Zadržite i prevucite da biste dodali pločice"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"Zadržite i prevucite da biste promenili raspored pločica"</string>
+ <!-- no translation found for tap_to_position_tile (6282815817773342757) -->
+ <skip />
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"Prevucite ovde da biste uklonili"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"Minimalan broj pločica je <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g>"</string>
<string name="qs_edit" msgid="5583565172803472437">"Izmeni"</string>
+ <!-- no translation found for qs_edit_tiles (2105215324060865035) -->
+ <skip />
<string name="tuner_time" msgid="2450785840990529997">"Vreme"</string>
<string-array name="clock_options">
<item msgid="3986445361435142273">"Prikaži sate, minute i sekunde"</item>
@@ -995,6 +1004,10 @@
<string name="other" msgid="429768510980739978">"Drugo"</string>
<string name="accessibility_qs_edit_toggle_tile_size_action" msgid="1485194410119733586">"uključivanje ili isključivanje veličine pločice"</string>
<string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"uklonili pločicu"</string>
+ <!-- no translation found for accessibility_qs_edit_toggle_placement_mode (3870429389210569610) -->
+ <skip />
+ <!-- no translation found for accessibility_qs_edit_toggle_selection (2201248304072372239) -->
+ <skip />
<string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"dodali pločicu na poslednju poziciju"</string>
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Premestite pločicu"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"Dodajte pločicu na željenu poziciju"</string>
@@ -1570,5 +1583,7 @@
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Nepoznato"</string>
<string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"Želite da resetujete sve pločice?"</string>
<string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Sve pločice Brzih podešavanja će se resetovati na prvobitna podešavanja uređaja"</string>
+ <!-- no translation found for demote_explain_text (1600426458580544250) -->
+ <skip />
<string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
</resources>
diff --git a/packages/SystemUI/res/values-b+sr+Latn/tiles_states_strings.xml b/packages/SystemUI/res/values-b+sr+Latn/tiles_states_strings.xml
index bbfcf840e53f..5784d4735ede 100644
--- a/packages/SystemUI/res/values-b+sr+Latn/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-b+sr+Latn/tiles_states_strings.xml
@@ -56,9 +56,11 @@
<item msgid="5376619709702103243">"Isključeno"</item>
<item msgid="4875147066469902392">"Uključeno"</item>
</string-array>
- <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) -->
- <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) -->
- <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) -->
+ <string-array name="tile_states_modes_dnd">
+ <item msgid="6509540227356524582">"Nedostupno"</item>
+ <item msgid="8589336868985358191">"Isključeno"</item>
+ <item msgid="726072717827778234">"Uključeno"</item>
+ </string-array>
<string-array name="tile_states_flashlight">
<item msgid="3465257127433353857">"Nedostupno"</item>
<item msgid="5044688398303285224">"Isključeno"</item>
diff --git a/packages/SystemUI/res/values-be/strings.xml b/packages/SystemUI/res/values-be/strings.xml
index 2ece7bd3900e..bf9929e53ad4 100644
--- a/packages/SystemUI/res/values-be/strings.xml
+++ b/packages/SystemUI/res/values-be/strings.xml
@@ -419,6 +419,11 @@
<string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Націсніце, каб спалучыць новую прыладу"</string>
<string name="hearing_devices_presets_error" msgid="350363093458408536">"Не ўдалося абнавіць набор налад"</string>
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"Набор налад"</string>
+ <string name="hearing_devices_input_routing_label" msgid="730396728151232306">"Стандартны мікрафон для выклікаў"</string>
+ <string-array name="hearing_device_input_routing_options">
+ <item msgid="4582190415045337003">"Мікрафон слыхавога апарата"</item>
+ <item msgid="8501466270452446450">"Мікрафон гэтага тэлефона"</item>
+ </string-array>
<string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"Выбрана"</string>
<string name="hearing_devices_ambient_label" msgid="629440938614895797">"Навакольныя гукі"</string>
<string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Левы бок"</string>
@@ -582,6 +587,7 @@
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Калі вы абагульваеце праграму, праграма \"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>\" можа бачыць усё, што паказваецца ці прайграецца ў гэтай праграме. Таму прадухіліце паказ пароляў, плацежных рэквізітаў, паведамленняў, фота, відэа і аўдыя."</string>
<string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Абагуліць экран"</string>
<string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"Праграма \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" адключыла гэты параметр"</string>
+ <string name="media_projection_entry_app_permission_dialog_single_app_not_supported" msgid="4860247304058870233">"Не падтрымліваецца праграмай"</string>
<string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"Выберыце праграму для абагульвання"</string>
<string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Уключыць трансляцыю экрана?"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Трансліраваць адну праграму"</string>
@@ -794,8 +800,7 @@
<string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Уключана – З улікам паставы галавы"</string>
<string name="inline_done_button" msgid="6043094985588909584">"Гатова"</string>
<string name="inline_ok_button" msgid="603075490581280343">"Прымяніць"</string>
- <!-- no translation found for inline_turn_off_notifications (2653064779176881329) -->
- <skip />
+ <string name="inline_turn_off_notifications" msgid="2653064779176881329">"Выключыць"</string>
<string name="notification_silence_title" msgid="8608090968400832335">"Бязгучны рэжым"</string>
<string name="notification_alert_title" msgid="3656229781017543655">"Стандартна"</string>
<string name="notification_automatic_title" msgid="3745465364578762652">"Аўтаматычна"</string>
@@ -977,9 +982,13 @@
<string name="right_icon" msgid="1103955040645237425">"Значок \"управа\""</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"Перацягніце патрэбныя пліткі"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"Каб змяніць парадак плітак, утрымлівайце і перацягвайце іх"</string>
+ <!-- no translation found for tap_to_position_tile (6282815817773342757) -->
+ <skip />
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"Перацягніце сюды, каб выдаліць"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"Мінімальная колькасць плітак: <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g>"</string>
<string name="qs_edit" msgid="5583565172803472437">"Рэдагаваць"</string>
+ <!-- no translation found for qs_edit_tiles (2105215324060865035) -->
+ <skip />
<string name="tuner_time" msgid="2450785840990529997">"Час"</string>
<string-array name="clock_options">
<item msgid="3986445361435142273">"Паказваць гадзіны, хвіліны і секунды"</item>
@@ -995,6 +1004,10 @@
<string name="other" msgid="429768510980739978">"Іншае"</string>
<string name="accessibility_qs_edit_toggle_tile_size_action" msgid="1485194410119733586">"змяніць памер пліткі"</string>
<string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"выдаліць плітку"</string>
+ <!-- no translation found for accessibility_qs_edit_toggle_placement_mode (3870429389210569610) -->
+ <skip />
+ <!-- no translation found for accessibility_qs_edit_toggle_selection (2201248304072372239) -->
+ <skip />
<string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"дадаць плітку ў апошнюю пазіцыю"</string>
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Перамясціць плітку"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"Дадаць плітку ў патрэбную пазіцыю"</string>
@@ -1570,5 +1583,7 @@
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Невядома"</string>
<string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"Скінуць усе пліткі?"</string>
<string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Усе пліткі хуткіх налад будуць скінуты да першапачатковых налад прылады"</string>
+ <!-- no translation found for demote_explain_text (1600426458580544250) -->
+ <skip />
<string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
</resources>
diff --git a/packages/SystemUI/res/values-be/tiles_states_strings.xml b/packages/SystemUI/res/values-be/tiles_states_strings.xml
index e6b35439ce44..67468b4a983e 100644
--- a/packages/SystemUI/res/values-be/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-be/tiles_states_strings.xml
@@ -56,9 +56,11 @@
<item msgid="5376619709702103243">"Выключана"</item>
<item msgid="4875147066469902392">"Уключана"</item>
</string-array>
- <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) -->
- <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) -->
- <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) -->
+ <string-array name="tile_states_modes_dnd">
+ <item msgid="6509540227356524582">"Недаступна"</item>
+ <item msgid="8589336868985358191">"Выключана"</item>
+ <item msgid="726072717827778234">"Уключана"</item>
+ </string-array>
<string-array name="tile_states_flashlight">
<item msgid="3465257127433353857">"Недаступна"</item>
<item msgid="5044688398303285224">"Выключана"</item>
diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml
index 3ab943a658e6..4a0b80bf29cb 100644
--- a/packages/SystemUI/res/values-bg/strings.xml
+++ b/packages/SystemUI/res/values-bg/strings.xml
@@ -419,6 +419,11 @@
<string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Кликнете за сдвояване на ново устройство"</string>
<string name="hearing_devices_presets_error" msgid="350363093458408536">"Предварително зададените настройки не бяха актуализирани"</string>
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"Предварително зададено"</string>
+ <string name="hearing_devices_input_routing_label" msgid="730396728151232306">"Микрофон по подразбиране за обажданията"</string>
+ <string-array name="hearing_device_input_routing_options">
+ <item msgid="4582190415045337003">"Микрофонът на слуховия апарат"</item>
+ <item msgid="8501466270452446450">"Микрофонът на този телефон"</item>
+ </string-array>
<string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"Избрано"</string>
<string name="hearing_devices_ambient_label" msgid="629440938614895797">"Околни звуци"</string>
<string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Ляво"</string>
@@ -582,6 +587,7 @@
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Когато споделяте приложение, всичко, което се показва или възпроизвежда в него, е видимо за <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Затова бъдете внимателни с неща като пароли, подробности за начини на плащане, съобщения, снимки, аудио и видео."</string>
<string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Споделяне на екрана"</string>
<string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g> деактивира тази опция"</string>
+ <string name="media_projection_entry_app_permission_dialog_single_app_not_supported" msgid="4860247304058870233">"Не се поддържа от приложението"</string>
<string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"Изберете приложение за споделяне"</string>
<string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Искате ли да предавате екрана си?"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Предаване на едно приложение"</string>
@@ -794,8 +800,7 @@
<string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Вкл. – въз основа на лицето"</string>
<string name="inline_done_button" msgid="6043094985588909584">"Готово"</string>
<string name="inline_ok_button" msgid="603075490581280343">"Прилагане"</string>
- <!-- no translation found for inline_turn_off_notifications (2653064779176881329) -->
- <skip />
+ <string name="inline_turn_off_notifications" msgid="2653064779176881329">"Изключване"</string>
<string name="notification_silence_title" msgid="8608090968400832335">"Тих режим"</string>
<string name="notification_alert_title" msgid="3656229781017543655">"Стандартно"</string>
<string name="notification_automatic_title" msgid="3745465364578762652">"Автоматично"</string>
@@ -977,9 +982,13 @@
<string name="right_icon" msgid="1103955040645237425">"Дясна икона"</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"Задръжте и плъзнете, за да добавите плочки"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"Задръжте и плъзнете, за да пренаредите плочките"</string>
+ <!-- no translation found for tap_to_position_tile (6282815817773342757) -->
+ <skip />
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"Преместете тук с плъзгане за премахване"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"Трябва да останат поне <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> плочки"</string>
<string name="qs_edit" msgid="5583565172803472437">"Редактиране"</string>
+ <!-- no translation found for qs_edit_tiles (2105215324060865035) -->
+ <skip />
<string name="tuner_time" msgid="2450785840990529997">"Час"</string>
<string-array name="clock_options">
<item msgid="3986445361435142273">"Показване на часовете, минутите и секундите"</item>
@@ -995,6 +1004,10 @@
<string name="other" msgid="429768510980739978">"Друго"</string>
<string name="accessibility_qs_edit_toggle_tile_size_action" msgid="1485194410119733586">"превключване на размера на панела"</string>
<string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"премахване на панел"</string>
+ <!-- no translation found for accessibility_qs_edit_toggle_placement_mode (3870429389210569610) -->
+ <skip />
+ <!-- no translation found for accessibility_qs_edit_toggle_selection (2201248304072372239) -->
+ <skip />
<string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"добавяне на панела на последната позиция"</string>
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Преместване на панел"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"Добавяне на панела на желаната позиция"</string>
@@ -1570,5 +1583,7 @@
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Неизвестно"</string>
<string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"Да се нулират ли всички панели?"</string>
<string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Всички панели с бързи настройки ще бъдат нулирани до първоначалните настройки на устройството"</string>
+ <!-- no translation found for demote_explain_text (1600426458580544250) -->
+ <skip />
<string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g> – <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
</resources>
diff --git a/packages/SystemUI/res/values-bg/tiles_states_strings.xml b/packages/SystemUI/res/values-bg/tiles_states_strings.xml
index b46d4dffb5fd..535ddcdcc7b6 100644
--- a/packages/SystemUI/res/values-bg/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-bg/tiles_states_strings.xml
@@ -56,9 +56,11 @@
<item msgid="5376619709702103243">"Изкл."</item>
<item msgid="4875147066469902392">"Вкл."</item>
</string-array>
- <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) -->
- <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) -->
- <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) -->
+ <string-array name="tile_states_modes_dnd">
+ <item msgid="6509540227356524582">"Не е налице"</item>
+ <item msgid="8589336868985358191">"Изкл."</item>
+ <item msgid="726072717827778234">"Вкл."</item>
+ </string-array>
<string-array name="tile_states_flashlight">
<item msgid="3465257127433353857">"Не е налице"</item>
<item msgid="5044688398303285224">"Изкл."</item>
diff --git a/packages/SystemUI/res/values-bn/strings.xml b/packages/SystemUI/res/values-bn/strings.xml
index 4ab650edc56d..ac365f8356d1 100644
--- a/packages/SystemUI/res/values-bn/strings.xml
+++ b/packages/SystemUI/res/values-bn/strings.xml
@@ -419,6 +419,11 @@
<string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"নতুন ডিভাইস পেয়ার করতে ক্লিক করুন"</string>
<string name="hearing_devices_presets_error" msgid="350363093458408536">"প্রিসেট আপডেট করা যায়নি"</string>
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"প্রিসেট"</string>
+ <string name="hearing_devices_input_routing_label" msgid="730396728151232306">"কলের জন্য ডিফল্ট মাইক্রোফোন"</string>
+ <string-array name="hearing_device_input_routing_options">
+ <item msgid="4582190415045337003">"হিয়ারিং এড মাইক্রোফোন"</item>
+ <item msgid="8501466270452446450">"এই ফোনের মাইক্রোফোন"</item>
+ </string-array>
<string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"বেছে নেওয়া হয়েছে"</string>
<string name="hearing_devices_ambient_label" msgid="629440938614895797">"সারাউন্ডিং"</string>
<string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"বাঁদিক"</string>
@@ -582,6 +587,8 @@
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"কোনও অ্যাপ শেয়ার করার সময়, সেই অ্যাপে দেখা ও চালানো হয় এমন সব কিছু <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> দেখতে পাবে। তাই পাসওয়ার্ড, পেমেন্টের বিবরণ, মেসেজ, ফটো এবং অডিও ও ভিডিওর মতো বিষয়ের ক্ষেত্রে সতর্ক থাকুন।"</string>
<string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"স্ক্রিন শেয়ার করুন"</string>
<string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g> এই বিকল্পটি বন্ধ করে দিয়েছে"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_not_supported (4860247304058870233) -->
+ <skip />
<string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"শেয়ার করার জন্য অ্যাপ বেছে নিন"</string>
<string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"আপনার স্ক্রিন কাস্ট করতে চান?"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"একটি অ্যাপ কাস্ট করুন"</string>
@@ -794,8 +801,7 @@
<string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"চালু আছে - মুখের হিসেবে"</string>
<string name="inline_done_button" msgid="6043094985588909584">"হয়ে গেছে"</string>
<string name="inline_ok_button" msgid="603075490581280343">"প্রয়োগ করুন"</string>
- <!-- no translation found for inline_turn_off_notifications (2653064779176881329) -->
- <skip />
+ <string name="inline_turn_off_notifications" msgid="2653064779176881329">"বন্ধ করুন"</string>
<string name="notification_silence_title" msgid="8608090968400832335">"সাইলেন্ট"</string>
<string name="notification_alert_title" msgid="3656229781017543655">"ডিফল্ট"</string>
<string name="notification_automatic_title" msgid="3745465364578762652">"অটোমেটিক"</string>
@@ -977,9 +983,13 @@
<string name="right_icon" msgid="1103955040645237425">"ডানদিকের আইকন"</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"টাইল যোগ করতে ধরে থেকে টেনে আনুন"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"টাইলগুলি আবার সাজানোর জন্য ধরে থেকে টেনে আনুন"</string>
+ <!-- no translation found for tap_to_position_tile (6282815817773342757) -->
+ <skip />
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"সরানোর জন্য এখানে টেনে আনুন"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"আপনাকে কমপক্ষে <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g>টি টাইল রাখতে হবে"</string>
<string name="qs_edit" msgid="5583565172803472437">"এডিট করুন"</string>
+ <!-- no translation found for qs_edit_tiles (2105215324060865035) -->
+ <skip />
<string name="tuner_time" msgid="2450785840990529997">"সময়"</string>
<string-array name="clock_options">
<item msgid="3986445361435142273">"ঘণ্টা, মিনিট, এবং সেকেন্ড দেখান"</item>
@@ -995,6 +1005,10 @@
<string name="other" msgid="429768510980739978">"অন্যান্য"</string>
<string name="accessibility_qs_edit_toggle_tile_size_action" msgid="1485194410119733586">"টাইলের সাইজ টগল করুন"</string>
<string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"টাইল সরান"</string>
+ <!-- no translation found for accessibility_qs_edit_toggle_placement_mode (3870429389210569610) -->
+ <skip />
+ <!-- no translation found for accessibility_qs_edit_toggle_selection (2201248304072372239) -->
+ <skip />
<string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"শেষ জায়গাতে টাইল যোগ করুন"</string>
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"টাইল সরান"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"আপনার পছন্দের জায়গাতে টাইল যোগ করুন"</string>
@@ -1570,5 +1584,7 @@
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"অজানা"</string>
<string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"সব টাইল রিসেট করবেন?"</string>
<string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"সব কুইক সেটিংস টাইল, ডিভাইসের আসল সেটিংসে রিসেট হয়ে যাবে"</string>
+ <!-- no translation found for demote_explain_text (1600426458580544250) -->
+ <skip />
<string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
</resources>
diff --git a/packages/SystemUI/res/values-bn/tiles_states_strings.xml b/packages/SystemUI/res/values-bn/tiles_states_strings.xml
index 852986ccb0fd..95a567a8d47b 100644
--- a/packages/SystemUI/res/values-bn/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-bn/tiles_states_strings.xml
@@ -56,9 +56,11 @@
<item msgid="5376619709702103243">"বন্ধ আছে"</item>
<item msgid="4875147066469902392">"চালু আছে"</item>
</string-array>
- <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) -->
- <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) -->
- <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) -->
+ <string-array name="tile_states_modes_dnd">
+ <item msgid="6509540227356524582">"উপলভ্য নেই"</item>
+ <item msgid="8589336868985358191">"বন্ধ করুন"</item>
+ <item msgid="726072717827778234">"চালু করুন"</item>
+ </string-array>
<string-array name="tile_states_flashlight">
<item msgid="3465257127433353857">"উপলভ্য নেই"</item>
<item msgid="5044688398303285224">"বন্ধ আছে"</item>
diff --git a/packages/SystemUI/res/values-bs/strings.xml b/packages/SystemUI/res/values-bs/strings.xml
index afa877ee5c0a..c026090f991d 100644
--- a/packages/SystemUI/res/values-bs/strings.xml
+++ b/packages/SystemUI/res/values-bs/strings.xml
@@ -419,6 +419,11 @@
<string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Kliknite da uparite novi uređaj"</string>
<string name="hearing_devices_presets_error" msgid="350363093458408536">"Ažuriranje zadane postavke nije uspjelo"</string>
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"Zadana postavka"</string>
+ <string name="hearing_devices_input_routing_label" msgid="730396728151232306">"Zadani mikrofon za pozive"</string>
+ <string-array name="hearing_device_input_routing_options">
+ <item msgid="4582190415045337003">"Mikrofon slušnog aparata"</item>
+ <item msgid="8501466270452446450">"Mikrofon telefona"</item>
+ </string-array>
<string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"Odabrano"</string>
<string name="hearing_devices_ambient_label" msgid="629440938614895797">"Okruženje"</string>
<string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Lijevo"</string>
@@ -582,6 +587,8 @@
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Kada dijelite aplikaciju, sve što se prikazuje ili reproducira u toj aplikaciji će biti vidljivo aplikaciji <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Stoga budite oprezni s informacijama kao što su lozinke, podaci o plaćanju, poruke, fotografije, audio i videozapisi."</string>
<string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Dijeli ekran"</string>
<string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"Aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> je onemogućila tu opciju"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_not_supported (4860247304058870233) -->
+ <skip />
<string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"Odaberite aplikaciju koju želite dijeliti"</string>
<string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Emitirati ekran?"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Emitiraj jednu aplikaciju"</string>
@@ -794,8 +801,7 @@
<string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Uključeno – na osnovu lica"</string>
<string name="inline_done_button" msgid="6043094985588909584">"Gotovo"</string>
<string name="inline_ok_button" msgid="603075490581280343">"Primijeni"</string>
- <!-- no translation found for inline_turn_off_notifications (2653064779176881329) -->
- <skip />
+ <string name="inline_turn_off_notifications" msgid="2653064779176881329">"Isključi"</string>
<string name="notification_silence_title" msgid="8608090968400832335">"Nečujno"</string>
<string name="notification_alert_title" msgid="3656229781017543655">"Zadano"</string>
<string name="notification_automatic_title" msgid="3745465364578762652">"Automatski"</string>
@@ -977,9 +983,13 @@
<string name="right_icon" msgid="1103955040645237425">"Ikona desno"</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"Držite i prevucite da dodate polja"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"Držite i prevucite da preuredite polja"</string>
+ <!-- no translation found for tap_to_position_tile (6282815817773342757) -->
+ <skip />
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"Prevucite ovdje za uklanjanje"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"Broj polja mora biti najmanje <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g>"</string>
<string name="qs_edit" msgid="5583565172803472437">"Uredite"</string>
+ <!-- no translation found for qs_edit_tiles (2105215324060865035) -->
+ <skip />
<string name="tuner_time" msgid="2450785840990529997">"Vrijeme"</string>
<string-array name="clock_options">
<item msgid="3986445361435142273">"Prikaži sate, minute i sekunde"</item>
@@ -995,6 +1005,10 @@
<string name="other" msgid="429768510980739978">"Ostalo"</string>
<string name="accessibility_qs_edit_toggle_tile_size_action" msgid="1485194410119733586">"uključivanje/isključivanje veličine kartice"</string>
<string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"uklanjanje kartice"</string>
+ <!-- no translation found for accessibility_qs_edit_toggle_placement_mode (3870429389210569610) -->
+ <skip />
+ <!-- no translation found for accessibility_qs_edit_toggle_selection (2201248304072372239) -->
+ <skip />
<string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"dodavanje kartice na posljednji položaj"</string>
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Pomjeranje kartice"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"Dodavanje kartice na željeni položaj"</string>
@@ -1570,5 +1584,7 @@
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Nepoznato"</string>
<string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"Vratiti sve kartice na zadano?"</string>
<string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Sve kartice Brze postavke će se vratiti na originalne postavke uređaja"</string>
+ <!-- no translation found for demote_explain_text (1600426458580544250) -->
+ <skip />
<string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
</resources>
diff --git a/packages/SystemUI/res/values-bs/tiles_states_strings.xml b/packages/SystemUI/res/values-bs/tiles_states_strings.xml
index bbfcf840e53f..5784d4735ede 100644
--- a/packages/SystemUI/res/values-bs/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-bs/tiles_states_strings.xml
@@ -56,9 +56,11 @@
<item msgid="5376619709702103243">"Isključeno"</item>
<item msgid="4875147066469902392">"Uključeno"</item>
</string-array>
- <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) -->
- <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) -->
- <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) -->
+ <string-array name="tile_states_modes_dnd">
+ <item msgid="6509540227356524582">"Nedostupno"</item>
+ <item msgid="8589336868985358191">"Isključeno"</item>
+ <item msgid="726072717827778234">"Uključeno"</item>
+ </string-array>
<string-array name="tile_states_flashlight">
<item msgid="3465257127433353857">"Nedostupno"</item>
<item msgid="5044688398303285224">"Isključeno"</item>
diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml
index 3e43c7924678..ad1482490449 100644
--- a/packages/SystemUI/res/values-ca/strings.xml
+++ b/packages/SystemUI/res/values-ca/strings.xml
@@ -419,6 +419,11 @@
<string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Fes clic per vincular un dispositiu nou"</string>
<string name="hearing_devices_presets_error" msgid="350363093458408536">"No s\'ha pogut actualitzar el valor predefinit"</string>
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"Valors predefinits"</string>
+ <string name="hearing_devices_input_routing_label" msgid="730396728151232306">"Micròfon predeterminat per a trucades"</string>
+ <string-array name="hearing_device_input_routing_options">
+ <item msgid="4582190415045337003">"Micròfon d\'audiòfon"</item>
+ <item msgid="8501466270452446450">"El micròfon d\'aquest telèfon"</item>
+ </string-array>
<string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"Seleccionat"</string>
<string name="hearing_devices_ambient_label" msgid="629440938614895797">"Entorn"</string>
<string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Esquerra"</string>
@@ -582,6 +587,8 @@
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Quan comparteixes una aplicació, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> pot veure qualsevol cosa que s\'hi mostra o que s\'hi reprodueix. Per aquest motiu, ves amb compte amb les contrasenyes, les dades de pagament, els missatges, les fotos i l\'àudio i el vídeo."</string>
<string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Comparteix la pantalla"</string>
<string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g> ha desactivat aquesta opció"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_not_supported (4860247304058870233) -->
+ <skip />
<string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"Tria una aplicació per compartir"</string>
<string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Vols emetre la pantalla?"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Emet una aplicació"</string>
@@ -794,8 +801,7 @@
<string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Activat: basat en cares"</string>
<string name="inline_done_button" msgid="6043094985588909584">"Fet"</string>
<string name="inline_ok_button" msgid="603075490581280343">"Aplica"</string>
- <!-- no translation found for inline_turn_off_notifications (2653064779176881329) -->
- <skip />
+ <string name="inline_turn_off_notifications" msgid="2653064779176881329">"Desactiva"</string>
<string name="notification_silence_title" msgid="8608090968400832335">"Silenci"</string>
<string name="notification_alert_title" msgid="3656229781017543655">"Predeterminat"</string>
<string name="notification_automatic_title" msgid="3745465364578762652">"Automàtic"</string>
@@ -977,9 +983,13 @@
<string name="right_icon" msgid="1103955040645237425">"Icona de la dreta"</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"Mantén premudes les icones i arrossega-les per afegir-les"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"Mantén premudes les icones i arrossega-les per reordenar-les"</string>
+ <!-- no translation found for tap_to_position_tile (6282815817773342757) -->
+ <skip />
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"Arrossega aquí per suprimir"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"Necessites com a mínim <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> mosaics"</string>
<string name="qs_edit" msgid="5583565172803472437">"Edita"</string>
+ <!-- no translation found for qs_edit_tiles (2105215324060865035) -->
+ <skip />
<string name="tuner_time" msgid="2450785840990529997">"Hora"</string>
<string-array name="clock_options">
<item msgid="3986445361435142273">"Mostra les hores, els minuts i els segons"</item>
@@ -995,6 +1005,10 @@
<string name="other" msgid="429768510980739978">"Altres"</string>
<string name="accessibility_qs_edit_toggle_tile_size_action" msgid="1485194410119733586">"commutar la mida de la icona"</string>
<string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"suprimir el mosaic"</string>
+ <!-- no translation found for accessibility_qs_edit_toggle_placement_mode (3870429389210569610) -->
+ <skip />
+ <!-- no translation found for accessibility_qs_edit_toggle_selection (2201248304072372239) -->
+ <skip />
<string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"afegir una icona a la darrera posició"</string>
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Mou el mosaic"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"Afegeix una icona a la posició que vulguis"</string>
@@ -1570,5 +1584,7 @@
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Desconegut"</string>
<string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"Vols restablir totes les icones?"</string>
<string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Totes les icones de configuració ràpida es restabliran a les opcions originals del dispositiu"</string>
+ <!-- no translation found for demote_explain_text (1600426458580544250) -->
+ <skip />
<string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ca/tiles_states_strings.xml b/packages/SystemUI/res/values-ca/tiles_states_strings.xml
index 5094d57a0ecf..7afc00f8c149 100644
--- a/packages/SystemUI/res/values-ca/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-ca/tiles_states_strings.xml
@@ -56,9 +56,11 @@
<item msgid="5376619709702103243">"Desactivat"</item>
<item msgid="4875147066469902392">"Activat"</item>
</string-array>
- <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) -->
- <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) -->
- <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) -->
+ <string-array name="tile_states_modes_dnd">
+ <item msgid="6509540227356524582">"No disponible"</item>
+ <item msgid="8589336868985358191">"Desactivat"</item>
+ <item msgid="726072717827778234">"Activat"</item>
+ </string-array>
<string-array name="tile_states_flashlight">
<item msgid="3465257127433353857">"No disponible"</item>
<item msgid="5044688398303285224">"Desactivat"</item>
diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml
index f374913a56cf..9fde63c12ffc 100644
--- a/packages/SystemUI/res/values-cs/strings.xml
+++ b/packages/SystemUI/res/values-cs/strings.xml
@@ -419,6 +419,11 @@
<string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Kliknutím spárujete nové zařízení"</string>
<string name="hearing_devices_presets_error" msgid="350363093458408536">"Předvolbu nelze aktualizovat"</string>
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"Předvolba"</string>
+ <string name="hearing_devices_input_routing_label" msgid="730396728151232306">"Výchozí mikrofon pro hovory"</string>
+ <string-array name="hearing_device_input_routing_options">
+ <item msgid="4582190415045337003">"Mikrofon naslouchátka"</item>
+ <item msgid="8501466270452446450">"Mikrofon tohohle telefonu"</item>
+ </string-array>
<string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"Vybráno"</string>
<string name="hearing_devices_ambient_label" msgid="629440938614895797">"Okolí"</string>
<string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Vlevo"</string>
@@ -582,6 +587,8 @@
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Při sdílení aplikace vidí <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> vše, co se ve sdílené aplikaci nachází nebo děje. Buďte proto opatrní, když jde o hesla, platební údaje, zprávy, fotografie, zvuk a video."</string>
<string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Sdílet obrazovku"</string>
<string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"Aplikace <xliff:g id="APP_NAME">%1$s</xliff:g> tuto možnost zakázala"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_not_supported (4860247304058870233) -->
+ <skip />
<string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"Vyberte aplikaci ke sdílení"</string>
<string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Odeslat obrazovku?"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Odeslat jednu aplikaci"</string>
@@ -794,8 +801,7 @@
<string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Zapnuto – podle obličeje"</string>
<string name="inline_done_button" msgid="6043094985588909584">"Hotovo"</string>
<string name="inline_ok_button" msgid="603075490581280343">"Použít"</string>
- <!-- no translation found for inline_turn_off_notifications (2653064779176881329) -->
- <skip />
+ <string name="inline_turn_off_notifications" msgid="2653064779176881329">"Vypnout"</string>
<string name="notification_silence_title" msgid="8608090968400832335">"Tichý režim"</string>
<string name="notification_alert_title" msgid="3656229781017543655">"Výchozí"</string>
<string name="notification_automatic_title" msgid="3745465364578762652">"Automaticky"</string>
@@ -977,9 +983,13 @@
<string name="right_icon" msgid="1103955040645237425">"Ikona vpravo"</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"Dlaždice přidáte podržením a přetažením"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"Dlaždice můžete uspořádat podržením a přetažením"</string>
+ <!-- no translation found for tap_to_position_tile (6282815817773342757) -->
+ <skip />
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"Přetažením sem dlaždice odstraníte"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"Potřebujete alespoň tento počet dlaždic: <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g>"</string>
<string name="qs_edit" msgid="5583565172803472437">"Upravit"</string>
+ <!-- no translation found for qs_edit_tiles (2105215324060865035) -->
+ <skip />
<string name="tuner_time" msgid="2450785840990529997">"Čas"</string>
<string-array name="clock_options">
<item msgid="3986445361435142273">"Zobrazovat hodiny, minuty a sekundy"</item>
@@ -995,6 +1005,10 @@
<string name="other" msgid="429768510980739978">"Jiné"</string>
<string name="accessibility_qs_edit_toggle_tile_size_action" msgid="1485194410119733586">"přepnout velikost dlaždice"</string>
<string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"odstranit dlaždici"</string>
+ <!-- no translation found for accessibility_qs_edit_toggle_placement_mode (3870429389210569610) -->
+ <skip />
+ <!-- no translation found for accessibility_qs_edit_toggle_selection (2201248304072372239) -->
+ <skip />
<string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"přidat dlaždici na poslední pozici"</string>
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Přesunout dlaždici"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"Přidat dlaždici na požadované místo"</string>
@@ -1570,5 +1584,7 @@
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Neznámé"</string>
<string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"Resetovat všechny dlaždice?"</string>
<string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Všechny dlaždice Rychlého nastavení se resetují do původní konfigurace zařízení"</string>
+ <!-- no translation found for demote_explain_text (1600426458580544250) -->
+ <skip />
<string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
</resources>
diff --git a/packages/SystemUI/res/values-cs/tiles_states_strings.xml b/packages/SystemUI/res/values-cs/tiles_states_strings.xml
index 0c7db6763eeb..46ef4ead74f0 100644
--- a/packages/SystemUI/res/values-cs/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-cs/tiles_states_strings.xml
@@ -56,9 +56,11 @@
<item msgid="5376619709702103243">"Vypnuto"</item>
<item msgid="4875147066469902392">"Zapnuto"</item>
</string-array>
- <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) -->
- <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) -->
- <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) -->
+ <string-array name="tile_states_modes_dnd">
+ <item msgid="6509540227356524582">"Nedostupné"</item>
+ <item msgid="8589336868985358191">"Vypnuto"</item>
+ <item msgid="726072717827778234">"Zapnuto"</item>
+ </string-array>
<string-array name="tile_states_flashlight">
<item msgid="3465257127433353857">"Nedostupné"</item>
<item msgid="5044688398303285224">"Vypnuto"</item>
diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml
index 0875aad0986e..fbb293b29e20 100644
--- a/packages/SystemUI/res/values-da/strings.xml
+++ b/packages/SystemUI/res/values-da/strings.xml
@@ -419,6 +419,11 @@
<string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Klik for at parre en ny enhed"</string>
<string name="hearing_devices_presets_error" msgid="350363093458408536">"Forindstillingen kunne ikke opdateres"</string>
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"Forindstilling"</string>
+ <string name="hearing_devices_input_routing_label" msgid="730396728151232306">"Standardmikrofon til opkald"</string>
+ <string-array name="hearing_device_input_routing_options">
+ <item msgid="4582190415045337003">"Mikrofon i høreapparat"</item>
+ <item msgid="8501466270452446450">"Denne telefons mikrofon"</item>
+ </string-array>
<string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"Valgt"</string>
<string name="hearing_devices_ambient_label" msgid="629440938614895797">"Omgivelser"</string>
<string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Venstre"</string>
@@ -582,6 +587,8 @@
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Når du deler en app, er alt, der vises eller afspilles i den pågældende app, synligt for <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Vær derfor forsigtig med f.eks. adgangskoder, betalingsoplysninger, beskeder, billeder, lyd og video."</string>
<string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Del skærm"</string>
<string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g> har deaktiveret denne valgmulighed"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_not_supported (4860247304058870233) -->
+ <skip />
<string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"Vælg den app, du vil dele fra"</string>
<string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Vil du caste din skærm?"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Cast én app"</string>
@@ -794,8 +801,7 @@
<string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Til – ansigtsbaseret"</string>
<string name="inline_done_button" msgid="6043094985588909584">"Udfør"</string>
<string name="inline_ok_button" msgid="603075490581280343">"Anvend"</string>
- <!-- no translation found for inline_turn_off_notifications (2653064779176881329) -->
- <skip />
+ <string name="inline_turn_off_notifications" msgid="2653064779176881329">"Deaktiver"</string>
<string name="notification_silence_title" msgid="8608090968400832335">"Lydløs"</string>
<string name="notification_alert_title" msgid="3656229781017543655">"Standard"</string>
<string name="notification_automatic_title" msgid="3745465364578762652">"Automatisk"</string>
@@ -906,7 +912,7 @@
<string name="system_multitasking_rhs" msgid="8779289852395243004">"Brug opdelt skærm med appen til højre"</string>
<string name="system_multitasking_lhs" msgid="7348595296208696452">"Brug opdelt skærm med appen til venstre"</string>
<string name="system_multitasking_full_screen" msgid="4221409316059910349">"Brug fuld skærm"</string>
- <string name="system_multitasking_desktop_view" msgid="8871367687089347180">"Brug vinduer på skrivebordet"</string>
+ <string name="system_multitasking_desktop_view" msgid="8871367687089347180">"Brug skrivebordsvinduer"</string>
<string name="system_multitasking_splitscreen_focus_rhs" msgid="3838578650313318508">"Skift til en app til højre eller nedenfor, når du bruger opdelt skærm"</string>
<string name="system_multitasking_splitscreen_focus_lhs" msgid="3164261844398662518">"Skift til en app til venstre eller ovenfor, når du bruger opdelt skærm"</string>
<string name="system_multitasking_replace" msgid="7410071959803642125">"Ved opdelt skærm: Udskift én app med en anden"</string>
@@ -977,9 +983,13 @@
<string name="right_icon" msgid="1103955040645237425">"Højre ikon"</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"Tilføj felter ved at holde dem nede og trække dem"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"Flyt rundt på felterne ved at holde dem nede og trække"</string>
+ <!-- no translation found for tap_to_position_tile (6282815817773342757) -->
+ <skip />
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"Træk herhen for at fjerne"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"Du skal bruge mindst <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> felter"</string>
<string name="qs_edit" msgid="5583565172803472437">"Rediger"</string>
+ <!-- no translation found for qs_edit_tiles (2105215324060865035) -->
+ <skip />
<string name="tuner_time" msgid="2450785840990529997">"Tid"</string>
<string-array name="clock_options">
<item msgid="3986445361435142273">"Vis timer, minutter og sekunder"</item>
@@ -995,6 +1005,10 @@
<string name="other" msgid="429768510980739978">"Andet"</string>
<string name="accessibility_qs_edit_toggle_tile_size_action" msgid="1485194410119733586">"ændre feltets størrelse"</string>
<string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"fjern felt"</string>
+ <!-- no translation found for accessibility_qs_edit_toggle_placement_mode (3870429389210569610) -->
+ <skip />
+ <!-- no translation found for accessibility_qs_edit_toggle_selection (2201248304072372239) -->
+ <skip />
<string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"føj handlingsfeltet til den sidste position"</string>
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Flyt felt"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"Føj handlingsfeltet til den ønskede placering"</string>
@@ -1570,5 +1584,7 @@
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Ukendt"</string>
<string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"Vil du nulstille alle handlingsfelter?"</string>
<string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Alle handlingsfelter i kvikmenuen nulstilles til enhedens oprindelige indstillinger"</string>
+ <!-- no translation found for demote_explain_text (1600426458580544250) -->
+ <skip />
<string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
</resources>
diff --git a/packages/SystemUI/res/values-da/tiles_states_strings.xml b/packages/SystemUI/res/values-da/tiles_states_strings.xml
index 5558b0b5433f..b4a3eaffda8d 100644
--- a/packages/SystemUI/res/values-da/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-da/tiles_states_strings.xml
@@ -56,9 +56,11 @@
<item msgid="5376619709702103243">"Fra"</item>
<item msgid="4875147066469902392">"Til"</item>
</string-array>
- <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) -->
- <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) -->
- <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) -->
+ <string-array name="tile_states_modes_dnd">
+ <item msgid="6509540227356524582">"Ikke tilgængelig"</item>
+ <item msgid="8589336868985358191">"Fra"</item>
+ <item msgid="726072717827778234">"Til"</item>
+ </string-array>
<string-array name="tile_states_flashlight">
<item msgid="3465257127433353857">"Ikke tilgængelig"</item>
<item msgid="5044688398303285224">"Fra"</item>
diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml
index 8b783fdcc161..86304f7b651c 100644
--- a/packages/SystemUI/res/values-de/strings.xml
+++ b/packages/SystemUI/res/values-de/strings.xml
@@ -419,6 +419,11 @@
<string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Klicken, um neues Gerät zu koppeln"</string>
<string name="hearing_devices_presets_error" msgid="350363093458408536">"Voreinstellung konnte nicht aktualisiert werden"</string>
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"Voreinstellung"</string>
+ <string name="hearing_devices_input_routing_label" msgid="730396728151232306">"Standardmikrofon für Anrufe"</string>
+ <string-array name="hearing_device_input_routing_options">
+ <item msgid="4582190415045337003">"Mikrofon des Hörgeräts"</item>
+ <item msgid="8501466270452446450">"Mikrofon dieses Smartphones"</item>
+ </string-array>
<string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"Ausgewählt"</string>
<string name="hearing_devices_ambient_label" msgid="629440938614895797">"Umgebungsgeräusche"</string>
<string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Links"</string>
@@ -582,6 +587,8 @@
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Wenn du eine App streamst, ist für <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> alles sichtbar, was in dieser App angezeigt oder abgespielt wird. Sei also vorsichtig mit Informationen wie Passwörtern, Zahlungsdetails, Nachrichten, Fotos sowie Audio- und Videoinhalten."</string>
<string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Bildschirm teilen"</string>
<string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g> hat diese Option deaktiviert"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_not_supported (4860247304058870233) -->
+ <skip />
<string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"App zum Teilen auswählen"</string>
<string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Bildschirm streamen?"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Eine App streamen"</string>
@@ -794,8 +801,7 @@
<string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"An – gesichtsbasiert"</string>
<string name="inline_done_button" msgid="6043094985588909584">"Fertig"</string>
<string name="inline_ok_button" msgid="603075490581280343">"Anwenden"</string>
- <!-- no translation found for inline_turn_off_notifications (2653064779176881329) -->
- <skip />
+ <string name="inline_turn_off_notifications" msgid="2653064779176881329">"Deaktivieren"</string>
<string name="notification_silence_title" msgid="8608090968400832335">"Lautlos"</string>
<string name="notification_alert_title" msgid="3656229781017543655">"Standard"</string>
<string name="notification_automatic_title" msgid="3745465364578762652">"Automatisch"</string>
@@ -977,9 +983,13 @@
<string name="right_icon" msgid="1103955040645237425">"Rechtes Symbol"</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"Zum Hinzufügen Kachel halten und ziehen"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"Zum Verschieben Kachel halten und ziehen"</string>
+ <!-- no translation found for tap_to_position_tile (6282815817773342757) -->
+ <skip />
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"Zum Entfernen hierher ziehen"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"Du brauchst mindestens <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> Kacheln"</string>
<string name="qs_edit" msgid="5583565172803472437">"Bearbeiten"</string>
+ <!-- no translation found for qs_edit_tiles (2105215324060865035) -->
+ <skip />
<string name="tuner_time" msgid="2450785840990529997">"Uhrzeit"</string>
<string-array name="clock_options">
<item msgid="3986445361435142273">"Stunden, Minuten und Sekunden anzeigen"</item>
@@ -995,6 +1005,10 @@
<string name="other" msgid="429768510980739978">"Sonstiges"</string>
<string name="accessibility_qs_edit_toggle_tile_size_action" msgid="1485194410119733586">"Größe der Kachel umschalten"</string>
<string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"Entfernen der Kachel"</string>
+ <!-- no translation found for accessibility_qs_edit_toggle_placement_mode (3870429389210569610) -->
+ <skip />
+ <!-- no translation found for accessibility_qs_edit_toggle_selection (2201248304072372239) -->
+ <skip />
<string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"Kachel an letzter Position hinzufügen"</string>
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Kachel verschieben"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"Kachel an gewünschter Position hinzufügen"</string>
@@ -1570,5 +1584,7 @@
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Unbekannt"</string>
<string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"Alle Kacheln zurücksetzen?"</string>
<string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Alle Schnelleinstellungen-Kacheln werden auf die Standardeinstellungen des Geräts zurückgesetzt"</string>
+ <!-- no translation found for demote_explain_text (1600426458580544250) -->
+ <skip />
<string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
</resources>
diff --git a/packages/SystemUI/res/values-de/tiles_states_strings.xml b/packages/SystemUI/res/values-de/tiles_states_strings.xml
index 2d4d1b6eb3da..ecae8dc4e0dd 100644
--- a/packages/SystemUI/res/values-de/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-de/tiles_states_strings.xml
@@ -56,9 +56,11 @@
<item msgid="5376619709702103243">"Aus"</item>
<item msgid="4875147066469902392">"An"</item>
</string-array>
- <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) -->
- <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) -->
- <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) -->
+ <string-array name="tile_states_modes_dnd">
+ <item msgid="6509540227356524582">"Nicht verfügbar"</item>
+ <item msgid="8589336868985358191">"Aus"</item>
+ <item msgid="726072717827778234">"An"</item>
+ </string-array>
<string-array name="tile_states_flashlight">
<item msgid="3465257127433353857">"Nicht verfügbar"</item>
<item msgid="5044688398303285224">"Aus"</item>
diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml
index c1a9a3a4c19d..478ac05eaa65 100644
--- a/packages/SystemUI/res/values-el/strings.xml
+++ b/packages/SystemUI/res/values-el/strings.xml
@@ -49,23 +49,23 @@
<string name="label_view" msgid="6815442985276363364">"Προβολή"</string>
<string name="always_use_device" msgid="210535878779644679">"Να ανοίγει πάντα η εφ. <xliff:g id="APPLICATION">%1$s</xliff:g> όταν είναι συνδεδεμένo το <xliff:g id="USB_DEVICE">%2$s</xliff:g>"</string>
<string name="always_use_accessory" msgid="1977225429341838444">"Να ανοίγει πάντα η εφ. <xliff:g id="APPLICATION">%1$s</xliff:g> όταν είναι συνδεδεμένο το <xliff:g id="USB_ACCESSORY">%2$s</xliff:g>"</string>
- <string name="usb_debugging_title" msgid="8274884945238642726">"Να επιτρέπεται ο εντοπισμός σφαλμάτων USB;"</string>
+ <string name="usb_debugging_title" msgid="8274884945238642726">"Να επιτρέπεται η αποσφαλμάτωση USB;"</string>
<string name="usb_debugging_message" msgid="5794616114463921773">"Το μοναδικό χαρακτηριστικό του κλειδιού RSA είναι:\n<xliff:g id="FINGERPRINT">%1$s</xliff:g>"</string>
<string name="usb_debugging_always" msgid="4003121804294739548">"Να επιτρέπεται πάντα από αυτόν τον υπολογιστή"</string>
<string name="usb_debugging_allow" msgid="1722643858015321328">"Να επιτρέπεται"</string>
- <string name="usb_debugging_secondary_user_title" msgid="7843050591380107998">"Δεν επιτρέπεται ο εντοπισμός σφαλμάτων USB"</string>
- <string name="usb_debugging_secondary_user_message" msgid="1888835696965417845">"Ο χρήστης που είναι συνδεδεμένος αυτήν τη στιγμή σε αυτήν τη συσκευή δεν μπορεί να ενεργοποιήσει τον εντοπισμό σφαλμάτων USB. Για να χρησιμοποιήσετε αυτήν τη λειτουργία, κάντε εναλλαγή σε έναν χρήστη με δικαιώματα διαχειριστή."</string>
+ <string name="usb_debugging_secondary_user_title" msgid="7843050591380107998">"Δεν επιτρέπεται η αποσφαλμάτωση USB"</string>
+ <string name="usb_debugging_secondary_user_message" msgid="1888835696965417845">"Ο χρήστης που είναι συνδεδεμένος αυτήν τη στιγμή σε αυτήν τη συσκευή δεν μπορεί να ενεργοποιήσει την αποσφαλμάτωση USB. Για να χρησιμοποιήσετε αυτήν τη λειτουργία, κάντε εναλλαγή σε έναν χρήστη με δικαιώματα διαχειριστή."</string>
<string name="hdmi_cec_set_menu_language_title" msgid="1259765420091503742">"Θέλετε να αλλάξετε τη γλώσσα συστήματος σε <xliff:g id="LANGUAGE">%1$s</xliff:g>;"</string>
<string name="hdmi_cec_set_menu_language_description" msgid="8176716678074126619">"Ζητήθηκε αλλαγή της γλώσσας συστήματος από άλλη συσκευή"</string>
<string name="hdmi_cec_set_menu_language_accept" msgid="2513689457281009578">"Αλλαγή γλώσσας"</string>
<string name="hdmi_cec_set_menu_language_decline" msgid="7650721096558646011">"Διατήρ. τρέχουσας γλώσσας"</string>
<string name="share_wifi_button_text" msgid="1285273973812029240">"Μοιραστείτε το Wi‑Fi"</string>
- <string name="wifi_debugging_title" msgid="7300007687492186076">"Να επιτρέπεται ασύρματος εντοπ. σφαλ. στο δίκτυο;"</string>
+ <string name="wifi_debugging_title" msgid="7300007687492186076">"Να επιτρέπεται ασύρματη αποσφαλμάτωση στο δίκτυο;"</string>
<string name="wifi_debugging_message" msgid="5461204211731802995">"Όνομα δικτύου (SSID)\n<xliff:g id="SSID_0">%1$s</xliff:g>\n\nΔιεύθυνση Wi‑Fi (BSSID)\n<xliff:g id="BSSID_1">%2$s</xliff:g>"</string>
<string name="wifi_debugging_always" msgid="2968383799517975155">"Να επιτρέπεται πάντα σε αυτό το δίκτυο"</string>
<string name="wifi_debugging_allow" msgid="4573224609684957886">"Να επιτρέπεται"</string>
- <string name="wifi_debugging_secondary_user_title" msgid="2493201475880517725">"Ο ασύρματος εντοπισμός σφαλμάτων δεν επιτρέπεται"</string>
- <string name="wifi_debugging_secondary_user_message" msgid="9085779370142222881">"Ο χρήστης που είναι συνδεδεμένος αυτήν τη στιγμή στη συγκεκριμένη συσκευή δεν μπορεί να ενεργοποιήσει τον ασύρματο εντοπισμό σφαλμάτων. Για να χρησιμοποιήσετε αυτήν τη λειτουργία, κάντε εναλλαγή σε έναν χρήστη με δικαιώματα διαχειριστή."</string>
+ <string name="wifi_debugging_secondary_user_title" msgid="2493201475880517725">"Η ασύρματη αποσφαλμάτωση δεν επιτρέπεται"</string>
+ <string name="wifi_debugging_secondary_user_message" msgid="9085779370142222881">"Ο χρήστης που είναι συνδεδεμένος αυτήν τη στιγμή στη συγκεκριμένη συσκευή δεν μπορεί να ενεργοποιήσει την ασύρματη αποσφαλμάτωση. Για να χρησιμοποιήσετε αυτήν τη λειτουργία, κάντε εναλλαγή σε έναν χρήστη με δικαιώματα διαχειριστή."</string>
<string name="usb_contaminant_title" msgid="894052515034594113">"Η θύρα USB απενεργοποιήθηκε"</string>
<string name="usb_contaminant_message" msgid="7730476585174719805">"Για την προστασία της συσκευής σας από υγρασία ή ακαθαρσίες, η θύρα USB έχει απενεργοποιηθεί και δεν θα εντοπίζει τυχόν αξεσουάρ.\n\nΘα ειδοποιηθείτε όταν θα μπορείτε να χρησιμοποιήσετε ξανά τη θύρα USB."</string>
<string name="usb_port_enabled" msgid="531823867664717018">"Η θύρα USB ενεργοποιήθηκε για τον εντοπισμό φορτιστών και αξεσουάρ"</string>
@@ -419,6 +419,11 @@
<string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Κάντε κλικ για σύζευξη νέας συσκευής"</string>
<string name="hearing_devices_presets_error" msgid="350363093458408536">"Δεν ήταν δυνατή η ενημέρωση της προεπιλογής"</string>
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"Προεπιλογή"</string>
+ <string name="hearing_devices_input_routing_label" msgid="730396728151232306">"Προεπιλεγμένο μικρόφωνο για κλήσεις"</string>
+ <string-array name="hearing_device_input_routing_options">
+ <item msgid="4582190415045337003">"Μικρόφωνο του βοηθήματος ακοής"</item>
+ <item msgid="8501466270452446450">"Μικρόφωνο αυτού του τηλεφώνου"</item>
+ </string-array>
<string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"Έχει επιλεγεί"</string>
<string name="hearing_devices_ambient_label" msgid="629440938614895797">"Ήχοι περιβάλλοντος"</string>
<string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Αριστερά"</string>
@@ -582,6 +587,7 @@
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Όταν μοιράζεστε μια εφαρμογή, οτιδήποτε εμφανίζεται ή αναπαράγεται σε αυτή την εφαρμογή, είναι ορατό στην εφαρμογή <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Επομένως, να είστε προσεκτικοί με τους κωδικούς πρόσβασης, τα στοιχεία πληρωμής, τα μηνύματα, τις φωτογραφίες, τον ήχο και το βίντεο."</string>
<string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Κοινή χρήση οθόνης"</string>
<string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"Η εφαρμογή <xliff:g id="APP_NAME">%1$s</xliff:g> απενεργοποίησε αυτή την επιλογή"</string>
+ <string name="media_projection_entry_app_permission_dialog_single_app_not_supported" msgid="4860247304058870233">"Δεν υποστηρίζεται από την εφαρμογή"</string>
<string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"Επιλογή εφαρμογής για κοινή χρήση"</string>
<string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Να γίνει μετάδοση της οθόνης σας;"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Μετάδοση μίας εφαρμογής"</string>
@@ -794,8 +800,7 @@
<string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Ενεργό - Βάσει προσώπου"</string>
<string name="inline_done_button" msgid="6043094985588909584">"Τέλος"</string>
<string name="inline_ok_button" msgid="603075490581280343">"Εφαρμογή"</string>
- <!-- no translation found for inline_turn_off_notifications (2653064779176881329) -->
- <skip />
+ <string name="inline_turn_off_notifications" msgid="2653064779176881329">"Απενεργοποίηση"</string>
<string name="notification_silence_title" msgid="8608090968400832335">"Σίγαση"</string>
<string name="notification_alert_title" msgid="3656229781017543655">"Προεπιλογή"</string>
<string name="notification_automatic_title" msgid="3745465364578762652">"Αυτόματο"</string>
@@ -977,9 +982,13 @@
<string name="right_icon" msgid="1103955040645237425">"Δεξιό εικονίδιο"</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"Κρατήστε και σύρετε για την προσθήκη πλακιδίων"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"Κρατήστε και σύρετε για αναδιάταξη των πλακιδίων"</string>
+ <!-- no translation found for tap_to_position_tile (6282815817773342757) -->
+ <skip />
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"Σύρετε εδώ για κατάργηση"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"Χρειάζεστε τουλάχιστον <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> πλακίδια"</string>
<string name="qs_edit" msgid="5583565172803472437">"Επεξεργασία"</string>
+ <!-- no translation found for qs_edit_tiles (2105215324060865035) -->
+ <skip />
<string name="tuner_time" msgid="2450785840990529997">"Ώρα"</string>
<string-array name="clock_options">
<item msgid="3986445361435142273">"Να εμφανίζονται ώρες, λεπτά και δευτερόλεπτα"</item>
@@ -995,6 +1004,10 @@
<string name="other" msgid="429768510980739978">"Άλλο"</string>
<string name="accessibility_qs_edit_toggle_tile_size_action" msgid="1485194410119733586">"εναλλαγή μεγέθους για το πλακάκι"</string>
<string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"κατάργηση πλακιδίου"</string>
+ <!-- no translation found for accessibility_qs_edit_toggle_placement_mode (3870429389210569610) -->
+ <skip />
+ <!-- no translation found for accessibility_qs_edit_toggle_selection (2201248304072372239) -->
+ <skip />
<string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"προσθήκη πλακιδίου στην τελευταία θέση"</string>
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Μετακίνηση πλακιδίου"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"Προσθήκη πλακιδίου στην επιθυμητή θέση"</string>
@@ -1570,5 +1583,7 @@
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Άγνωστο"</string>
<string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"Επαναφορά σε όλα τα πλακάκια;"</string>
<string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Σε όλα τα πλακάκια Γρήγορων ρυθμίσεων θα γίνει επαναφορά στις αρχικές ρυθμίσεις της συσκευής"</string>
+ <!-- no translation found for demote_explain_text (1600426458580544250) -->
+ <skip />
<string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
</resources>
diff --git a/packages/SystemUI/res/values-el/tiles_states_strings.xml b/packages/SystemUI/res/values-el/tiles_states_strings.xml
index 76e70719d11f..d8910b3955b9 100644
--- a/packages/SystemUI/res/values-el/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-el/tiles_states_strings.xml
@@ -56,9 +56,11 @@
<item msgid="5376619709702103243">"Ανενεργό"</item>
<item msgid="4875147066469902392">"Ενεργό"</item>
</string-array>
- <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) -->
- <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) -->
- <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) -->
+ <string-array name="tile_states_modes_dnd">
+ <item msgid="6509540227356524582">"Μη διαθέσιμο"</item>
+ <item msgid="8589336868985358191">"Ανενεργό"</item>
+ <item msgid="726072717827778234">"Ενεργό"</item>
+ </string-array>
<string-array name="tile_states_flashlight">
<item msgid="3465257127433353857">"Μη διαθέσιμο"</item>
<item msgid="5044688398303285224">"Ανενεργός"</item>
diff --git a/packages/SystemUI/res/values-en-rAU/strings.xml b/packages/SystemUI/res/values-en-rAU/strings.xml
index 1fee916f670e..ea332cbc27c5 100644
--- a/packages/SystemUI/res/values-en-rAU/strings.xml
+++ b/packages/SystemUI/res/values-en-rAU/strings.xml
@@ -419,6 +419,11 @@
<string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Click to pair new device"</string>
<string name="hearing_devices_presets_error" msgid="350363093458408536">"Couldn\'t update preset"</string>
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"Preset"</string>
+ <string name="hearing_devices_input_routing_label" msgid="730396728151232306">"Default microphone for calls"</string>
+ <string-array name="hearing_device_input_routing_options">
+ <item msgid="4582190415045337003">"Hearing aid microphone"</item>
+ <item msgid="8501466270452446450">"This phone\'s microphone"</item>
+ </string-array>
<string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"Selected"</string>
<string name="hearing_devices_ambient_label" msgid="629440938614895797">"Surroundings"</string>
<string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Left"</string>
@@ -582,6 +587,8 @@
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"When you\'re sharing an app, anything shown or played in that app is visible to <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. So be careful with things like passwords, payment details, messages, photos, audio and video."</string>
<string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Share screen"</string>
<string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g> has disabled this option"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_not_supported (4860247304058870233) -->
+ <skip />
<string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"Choose app to share"</string>
<string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Cast your screen?"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Cast one app"</string>
@@ -794,8 +801,7 @@
<string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"On – Face-based"</string>
<string name="inline_done_button" msgid="6043094985588909584">"Done"</string>
<string name="inline_ok_button" msgid="603075490581280343">"Apply"</string>
- <!-- no translation found for inline_turn_off_notifications (2653064779176881329) -->
- <skip />
+ <string name="inline_turn_off_notifications" msgid="2653064779176881329">"Turn off"</string>
<string name="notification_silence_title" msgid="8608090968400832335">"Silent"</string>
<string name="notification_alert_title" msgid="3656229781017543655">"Default"</string>
<string name="notification_automatic_title" msgid="3745465364578762652">"Automatic"</string>
@@ -977,9 +983,13 @@
<string name="right_icon" msgid="1103955040645237425">"Right icon"</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"Hold and drag to add tiles"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"Hold and drag to rearrange tiles"</string>
+ <!-- no translation found for tap_to_position_tile (6282815817773342757) -->
+ <skip />
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"Drag here to remove"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"You need at least <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> tiles"</string>
<string name="qs_edit" msgid="5583565172803472437">"Edit"</string>
+ <!-- no translation found for qs_edit_tiles (2105215324060865035) -->
+ <skip />
<string name="tuner_time" msgid="2450785840990529997">"Time"</string>
<string-array name="clock_options">
<item msgid="3986445361435142273">"Show hours, minutes and seconds"</item>
@@ -995,6 +1005,10 @@
<string name="other" msgid="429768510980739978">"Other"</string>
<string name="accessibility_qs_edit_toggle_tile_size_action" msgid="1485194410119733586">"toggle the tile\'s size"</string>
<string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"remove tile"</string>
+ <!-- no translation found for accessibility_qs_edit_toggle_placement_mode (3870429389210569610) -->
+ <skip />
+ <!-- no translation found for accessibility_qs_edit_toggle_selection (2201248304072372239) -->
+ <skip />
<string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"add tile to the last position"</string>
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Move tile"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"Add tile to desired position"</string>
@@ -1570,5 +1584,7 @@
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Unknown"</string>
<string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"Reset all tiles?"</string>
<string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"All Quick Settings tiles will reset to the device\'s original settings"</string>
+ <!-- no translation found for demote_explain_text (1600426458580544250) -->
+ <skip />
<string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
</resources>
diff --git a/packages/SystemUI/res/values-en-rAU/tiles_states_strings.xml b/packages/SystemUI/res/values-en-rAU/tiles_states_strings.xml
index 34580ebc9b99..3014e6207e7d 100644
--- a/packages/SystemUI/res/values-en-rAU/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-en-rAU/tiles_states_strings.xml
@@ -56,9 +56,11 @@
<item msgid="5376619709702103243">"Off"</item>
<item msgid="4875147066469902392">"On"</item>
</string-array>
- <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) -->
- <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) -->
- <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) -->
+ <string-array name="tile_states_modes_dnd">
+ <item msgid="6509540227356524582">"Unavailable"</item>
+ <item msgid="8589336868985358191">"Off"</item>
+ <item msgid="726072717827778234">"On"</item>
+ </string-array>
<string-array name="tile_states_flashlight">
<item msgid="3465257127433353857">"Unavailable"</item>
<item msgid="5044688398303285224">"Off"</item>
diff --git a/packages/SystemUI/res/values-en-rCA/strings.xml b/packages/SystemUI/res/values-en-rCA/strings.xml
index 318f0b43332e..b6dd88a83936 100644
--- a/packages/SystemUI/res/values-en-rCA/strings.xml
+++ b/packages/SystemUI/res/values-en-rCA/strings.xml
@@ -419,6 +419,11 @@
<string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Click to pair new device"</string>
<string name="hearing_devices_presets_error" msgid="350363093458408536">"Couldn\'t update preset"</string>
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"Preset"</string>
+ <string name="hearing_devices_input_routing_label" msgid="730396728151232306">"Default microphone for calls"</string>
+ <string-array name="hearing_device_input_routing_options">
+ <item msgid="4582190415045337003">"Hearing aid microphone"</item>
+ <item msgid="8501466270452446450">"This phone\'s microphone"</item>
+ </string-array>
<string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"Selected"</string>
<string name="hearing_devices_ambient_label" msgid="629440938614895797">"Surroundings"</string>
<string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Left"</string>
@@ -582,6 +587,7 @@
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"When you’re sharing an app, anything shown or played in that app is visible to <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. So be careful with things like passwords, payment details, messages, photos, and audio and video."</string>
<string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Share screen"</string>
<string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g> has disabled this option"</string>
+ <string name="media_projection_entry_app_permission_dialog_single_app_not_supported" msgid="4860247304058870233">"Not supported by the app"</string>
<string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"Choose app to share"</string>
<string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Cast your screen?"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Cast one app"</string>
@@ -976,9 +982,11 @@
<string name="right_icon" msgid="1103955040645237425">"Right icon"</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"Hold and drag to add tiles"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"Hold and drag to rearrange tiles"</string>
+ <string name="tap_to_position_tile" msgid="6282815817773342757">"Tap to position tile"</string>
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"Drag here to remove"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"You need at least <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> tiles"</string>
<string name="qs_edit" msgid="5583565172803472437">"Edit"</string>
+ <string name="qs_edit_tiles" msgid="2105215324060865035">"Edit tiles"</string>
<string name="tuner_time" msgid="2450785840990529997">"Time"</string>
<string-array name="clock_options">
<item msgid="3986445361435142273">"Show hours, minutes, and seconds"</item>
@@ -994,6 +1002,8 @@
<string name="other" msgid="429768510980739978">"Other"</string>
<string name="accessibility_qs_edit_toggle_tile_size_action" msgid="1485194410119733586">"toggle the tile\'s size"</string>
<string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"remove tile"</string>
+ <string name="accessibility_qs_edit_toggle_placement_mode" msgid="3870429389210569610">"toggle placement mode"</string>
+ <string name="accessibility_qs_edit_toggle_selection" msgid="2201248304072372239">"toggle selection"</string>
<string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"add tile to the last position"</string>
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Move tile"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"Add tile to desired position"</string>
@@ -1569,5 +1579,6 @@
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Unknown"</string>
<string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"Reset all tiles?"</string>
<string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"All Quick Settings tiles will reset to the device’s original settings"</string>
+ <string name="demote_explain_text" msgid="1600426458580544250">"<xliff:g id="APPLICATION">%1$s</xliff:g> will no longer show Live Updates here. You can change this any time in Settings."</string>
<string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
</resources>
diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml
index 1fee916f670e..ea332cbc27c5 100644
--- a/packages/SystemUI/res/values-en-rGB/strings.xml
+++ b/packages/SystemUI/res/values-en-rGB/strings.xml
@@ -419,6 +419,11 @@
<string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Click to pair new device"</string>
<string name="hearing_devices_presets_error" msgid="350363093458408536">"Couldn\'t update preset"</string>
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"Preset"</string>
+ <string name="hearing_devices_input_routing_label" msgid="730396728151232306">"Default microphone for calls"</string>
+ <string-array name="hearing_device_input_routing_options">
+ <item msgid="4582190415045337003">"Hearing aid microphone"</item>
+ <item msgid="8501466270452446450">"This phone\'s microphone"</item>
+ </string-array>
<string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"Selected"</string>
<string name="hearing_devices_ambient_label" msgid="629440938614895797">"Surroundings"</string>
<string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Left"</string>
@@ -582,6 +587,8 @@
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"When you\'re sharing an app, anything shown or played in that app is visible to <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. So be careful with things like passwords, payment details, messages, photos, audio and video."</string>
<string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Share screen"</string>
<string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g> has disabled this option"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_not_supported (4860247304058870233) -->
+ <skip />
<string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"Choose app to share"</string>
<string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Cast your screen?"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Cast one app"</string>
@@ -794,8 +801,7 @@
<string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"On – Face-based"</string>
<string name="inline_done_button" msgid="6043094985588909584">"Done"</string>
<string name="inline_ok_button" msgid="603075490581280343">"Apply"</string>
- <!-- no translation found for inline_turn_off_notifications (2653064779176881329) -->
- <skip />
+ <string name="inline_turn_off_notifications" msgid="2653064779176881329">"Turn off"</string>
<string name="notification_silence_title" msgid="8608090968400832335">"Silent"</string>
<string name="notification_alert_title" msgid="3656229781017543655">"Default"</string>
<string name="notification_automatic_title" msgid="3745465364578762652">"Automatic"</string>
@@ -977,9 +983,13 @@
<string name="right_icon" msgid="1103955040645237425">"Right icon"</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"Hold and drag to add tiles"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"Hold and drag to rearrange tiles"</string>
+ <!-- no translation found for tap_to_position_tile (6282815817773342757) -->
+ <skip />
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"Drag here to remove"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"You need at least <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> tiles"</string>
<string name="qs_edit" msgid="5583565172803472437">"Edit"</string>
+ <!-- no translation found for qs_edit_tiles (2105215324060865035) -->
+ <skip />
<string name="tuner_time" msgid="2450785840990529997">"Time"</string>
<string-array name="clock_options">
<item msgid="3986445361435142273">"Show hours, minutes and seconds"</item>
@@ -995,6 +1005,10 @@
<string name="other" msgid="429768510980739978">"Other"</string>
<string name="accessibility_qs_edit_toggle_tile_size_action" msgid="1485194410119733586">"toggle the tile\'s size"</string>
<string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"remove tile"</string>
+ <!-- no translation found for accessibility_qs_edit_toggle_placement_mode (3870429389210569610) -->
+ <skip />
+ <!-- no translation found for accessibility_qs_edit_toggle_selection (2201248304072372239) -->
+ <skip />
<string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"add tile to the last position"</string>
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Move tile"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"Add tile to desired position"</string>
@@ -1570,5 +1584,7 @@
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Unknown"</string>
<string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"Reset all tiles?"</string>
<string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"All Quick Settings tiles will reset to the device\'s original settings"</string>
+ <!-- no translation found for demote_explain_text (1600426458580544250) -->
+ <skip />
<string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
</resources>
diff --git a/packages/SystemUI/res/values-en-rGB/tiles_states_strings.xml b/packages/SystemUI/res/values-en-rGB/tiles_states_strings.xml
index 34580ebc9b99..3014e6207e7d 100644
--- a/packages/SystemUI/res/values-en-rGB/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-en-rGB/tiles_states_strings.xml
@@ -56,9 +56,11 @@
<item msgid="5376619709702103243">"Off"</item>
<item msgid="4875147066469902392">"On"</item>
</string-array>
- <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) -->
- <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) -->
- <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) -->
+ <string-array name="tile_states_modes_dnd">
+ <item msgid="6509540227356524582">"Unavailable"</item>
+ <item msgid="8589336868985358191">"Off"</item>
+ <item msgid="726072717827778234">"On"</item>
+ </string-array>
<string-array name="tile_states_flashlight">
<item msgid="3465257127433353857">"Unavailable"</item>
<item msgid="5044688398303285224">"Off"</item>
diff --git a/packages/SystemUI/res/values-en-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml
index 1fee916f670e..ea332cbc27c5 100644
--- a/packages/SystemUI/res/values-en-rIN/strings.xml
+++ b/packages/SystemUI/res/values-en-rIN/strings.xml
@@ -419,6 +419,11 @@
<string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Click to pair new device"</string>
<string name="hearing_devices_presets_error" msgid="350363093458408536">"Couldn\'t update preset"</string>
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"Preset"</string>
+ <string name="hearing_devices_input_routing_label" msgid="730396728151232306">"Default microphone for calls"</string>
+ <string-array name="hearing_device_input_routing_options">
+ <item msgid="4582190415045337003">"Hearing aid microphone"</item>
+ <item msgid="8501466270452446450">"This phone\'s microphone"</item>
+ </string-array>
<string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"Selected"</string>
<string name="hearing_devices_ambient_label" msgid="629440938614895797">"Surroundings"</string>
<string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Left"</string>
@@ -582,6 +587,8 @@
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"When you\'re sharing an app, anything shown or played in that app is visible to <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. So be careful with things like passwords, payment details, messages, photos, audio and video."</string>
<string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Share screen"</string>
<string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g> has disabled this option"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_not_supported (4860247304058870233) -->
+ <skip />
<string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"Choose app to share"</string>
<string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Cast your screen?"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Cast one app"</string>
@@ -794,8 +801,7 @@
<string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"On – Face-based"</string>
<string name="inline_done_button" msgid="6043094985588909584">"Done"</string>
<string name="inline_ok_button" msgid="603075490581280343">"Apply"</string>
- <!-- no translation found for inline_turn_off_notifications (2653064779176881329) -->
- <skip />
+ <string name="inline_turn_off_notifications" msgid="2653064779176881329">"Turn off"</string>
<string name="notification_silence_title" msgid="8608090968400832335">"Silent"</string>
<string name="notification_alert_title" msgid="3656229781017543655">"Default"</string>
<string name="notification_automatic_title" msgid="3745465364578762652">"Automatic"</string>
@@ -977,9 +983,13 @@
<string name="right_icon" msgid="1103955040645237425">"Right icon"</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"Hold and drag to add tiles"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"Hold and drag to rearrange tiles"</string>
+ <!-- no translation found for tap_to_position_tile (6282815817773342757) -->
+ <skip />
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"Drag here to remove"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"You need at least <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> tiles"</string>
<string name="qs_edit" msgid="5583565172803472437">"Edit"</string>
+ <!-- no translation found for qs_edit_tiles (2105215324060865035) -->
+ <skip />
<string name="tuner_time" msgid="2450785840990529997">"Time"</string>
<string-array name="clock_options">
<item msgid="3986445361435142273">"Show hours, minutes and seconds"</item>
@@ -995,6 +1005,10 @@
<string name="other" msgid="429768510980739978">"Other"</string>
<string name="accessibility_qs_edit_toggle_tile_size_action" msgid="1485194410119733586">"toggle the tile\'s size"</string>
<string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"remove tile"</string>
+ <!-- no translation found for accessibility_qs_edit_toggle_placement_mode (3870429389210569610) -->
+ <skip />
+ <!-- no translation found for accessibility_qs_edit_toggle_selection (2201248304072372239) -->
+ <skip />
<string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"add tile to the last position"</string>
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Move tile"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"Add tile to desired position"</string>
@@ -1570,5 +1584,7 @@
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Unknown"</string>
<string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"Reset all tiles?"</string>
<string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"All Quick Settings tiles will reset to the device\'s original settings"</string>
+ <!-- no translation found for demote_explain_text (1600426458580544250) -->
+ <skip />
<string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
</resources>
diff --git a/packages/SystemUI/res/values-en-rIN/tiles_states_strings.xml b/packages/SystemUI/res/values-en-rIN/tiles_states_strings.xml
index 34580ebc9b99..3014e6207e7d 100644
--- a/packages/SystemUI/res/values-en-rIN/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-en-rIN/tiles_states_strings.xml
@@ -56,9 +56,11 @@
<item msgid="5376619709702103243">"Off"</item>
<item msgid="4875147066469902392">"On"</item>
</string-array>
- <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) -->
- <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) -->
- <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) -->
+ <string-array name="tile_states_modes_dnd">
+ <item msgid="6509540227356524582">"Unavailable"</item>
+ <item msgid="8589336868985358191">"Off"</item>
+ <item msgid="726072717827778234">"On"</item>
+ </string-array>
<string-array name="tile_states_flashlight">
<item msgid="3465257127433353857">"Unavailable"</item>
<item msgid="5044688398303285224">"Off"</item>
diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml
index 486b43a2e867..e9e58e12c286 100644
--- a/packages/SystemUI/res/values-es-rUS/strings.xml
+++ b/packages/SystemUI/res/values-es-rUS/strings.xml
@@ -419,6 +419,11 @@
<string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Haz clic para vincular un dispositivo nuevo"</string>
<string name="hearing_devices_presets_error" msgid="350363093458408536">"No se pudo actualizar el ajuste predeterminado"</string>
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"Ajuste predeterminado"</string>
+ <string name="hearing_devices_input_routing_label" msgid="730396728151232306">"Micrófono predeterminado para llamadas"</string>
+ <string-array name="hearing_device_input_routing_options">
+ <item msgid="4582190415045337003">"Micrófono de audífonos"</item>
+ <item msgid="8501466270452446450">"Micrófono de este teléfono"</item>
+ </string-array>
<string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"Seleccionado"</string>
<string name="hearing_devices_ambient_label" msgid="629440938614895797">"Sonido envolvente"</string>
<string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Izquierda"</string>
@@ -582,6 +587,8 @@
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Cuando compartes una app, todo lo que se muestre o reproduzca en ella será visible en <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Por lo tanto, debes tener cuidado con contraseñas, detalles de pagos, mensajes, fotos, audios y videos."</string>
<string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Compartir pantalla"</string>
<string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g> inhabilitó esta opción"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_not_supported (4860247304058870233) -->
+ <skip />
<string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"Elige la app para compartir"</string>
<string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"¿Quieres transmitir la pantalla?"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Transmitir una app"</string>
@@ -794,8 +801,7 @@
<string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Activa - En función del rostro"</string>
<string name="inline_done_button" msgid="6043094985588909584">"Listo"</string>
<string name="inline_ok_button" msgid="603075490581280343">"Aplicar"</string>
- <!-- no translation found for inline_turn_off_notifications (2653064779176881329) -->
- <skip />
+ <string name="inline_turn_off_notifications" msgid="2653064779176881329">"Desactivar"</string>
<string name="notification_silence_title" msgid="8608090968400832335">"Silenciada"</string>
<string name="notification_alert_title" msgid="3656229781017543655">"Predeterminada"</string>
<string name="notification_automatic_title" msgid="3745465364578762652">"Automática"</string>
@@ -977,9 +983,13 @@
<string name="right_icon" msgid="1103955040645237425">"Ícono derecho"</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"Mantén presionada una tarjeta y arrástrala para agregarla"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"Mantén presionada una tarjeta y arrástrala para reubicarla"</string>
+ <!-- no translation found for tap_to_position_tile (6282815817773342757) -->
+ <skip />
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"Arrastra aquí para quitar"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"Necesitas al menos <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> tarjetas"</string>
<string name="qs_edit" msgid="5583565172803472437">"Editar"</string>
+ <!-- no translation found for qs_edit_tiles (2105215324060865035) -->
+ <skip />
<string name="tuner_time" msgid="2450785840990529997">"Hora"</string>
<string-array name="clock_options">
<item msgid="3986445361435142273">"Mostrar horas, minutos y segundos"</item>
@@ -995,6 +1005,10 @@
<string name="other" msgid="429768510980739978">"Otros"</string>
<string name="accessibility_qs_edit_toggle_tile_size_action" msgid="1485194410119733586">"alternar el tamaño del mosaico"</string>
<string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"quitar tarjeta"</string>
+ <!-- no translation found for accessibility_qs_edit_toggle_placement_mode (3870429389210569610) -->
+ <skip />
+ <!-- no translation found for accessibility_qs_edit_toggle_selection (2201248304072372239) -->
+ <skip />
<string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"agregar tarjeta a la última posición"</string>
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Mover la tarjeta"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"Agregar tarjeta a la posición deseada"</string>
@@ -1570,5 +1584,7 @@
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Desconocido"</string>
<string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"¿Quieres restablecer todas las tarjetas?"</string>
<string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Se restablecerán todas las tarjeta de Configuración rápida a la configuración original del dispositivo"</string>
+ <!-- no translation found for demote_explain_text (1600426458580544250) -->
+ <skip />
<string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
</resources>
diff --git a/packages/SystemUI/res/values-es-rUS/tiles_states_strings.xml b/packages/SystemUI/res/values-es-rUS/tiles_states_strings.xml
index 3310d2bc0fb0..2eb8435ce803 100644
--- a/packages/SystemUI/res/values-es-rUS/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-es-rUS/tiles_states_strings.xml
@@ -56,9 +56,11 @@
<item msgid="5376619709702103243">"Desactivado"</item>
<item msgid="4875147066469902392">"Activado"</item>
</string-array>
- <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) -->
- <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) -->
- <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) -->
+ <string-array name="tile_states_modes_dnd">
+ <item msgid="6509540227356524582">"No disponible"</item>
+ <item msgid="8589336868985358191">"Desactivado"</item>
+ <item msgid="726072717827778234">"Activado"</item>
+ </string-array>
<string-array name="tile_states_flashlight">
<item msgid="3465257127433353857">"No disponible"</item>
<item msgid="5044688398303285224">"Desactivada"</item>
diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml
index ecd3fdab13aa..011b9cce3cf0 100644
--- a/packages/SystemUI/res/values-es/strings.xml
+++ b/packages/SystemUI/res/values-es/strings.xml
@@ -419,6 +419,11 @@
<string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Haz clic para emparejar un nuevo dispositivo"</string>
<string name="hearing_devices_presets_error" msgid="350363093458408536">"No se ha podido actualizar el preajuste"</string>
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"Preajuste"</string>
+ <string name="hearing_devices_input_routing_label" msgid="730396728151232306">"Micrófono predeterminado para llamadas"</string>
+ <string-array name="hearing_device_input_routing_options">
+ <item msgid="4582190415045337003">"Micrófono del audífono"</item>
+ <item msgid="8501466270452446450">"El micrófono de este teléfono"</item>
+ </string-array>
<string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"Seleccionado"</string>
<string name="hearing_devices_ambient_label" msgid="629440938614895797">"Alrededores"</string>
<string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Izquierda"</string>
@@ -582,6 +587,8 @@
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Cuando compartes una aplicación, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> puede ver todo lo que se muestra o reproduce en ella. Debes tener cuidado con elementos como contraseñas, detalles de pagos, mensajes, fotos, audio y vídeo."</string>
<string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Compartir pantalla"</string>
<string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g> ha inhabilitado esta opción"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_not_supported (4860247304058870233) -->
+ <skip />
<string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"Elige la aplicación que quieres compartir"</string>
<string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"¿Enviar tu pantalla?"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Enviar una aplicación"</string>
@@ -794,8 +801,7 @@
<string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Activado: basado en caras"</string>
<string name="inline_done_button" msgid="6043094985588909584">"Hecho"</string>
<string name="inline_ok_button" msgid="603075490581280343">"Aplicar"</string>
- <!-- no translation found for inline_turn_off_notifications (2653064779176881329) -->
- <skip />
+ <string name="inline_turn_off_notifications" msgid="2653064779176881329">"Desactivar"</string>
<string name="notification_silence_title" msgid="8608090968400832335">"Silencio"</string>
<string name="notification_alert_title" msgid="3656229781017543655">"Predeterminado"</string>
<string name="notification_automatic_title" msgid="3745465364578762652">"Automática"</string>
@@ -977,9 +983,13 @@
<string name="right_icon" msgid="1103955040645237425">"Icono a la derecha"</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"Mantén pulsado un recuadro y arrástralo para añadirlo"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"Mantén pulsado un recuadro y arrástralo para reubicarlo"</string>
+ <!-- no translation found for tap_to_position_tile (6282815817773342757) -->
+ <skip />
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"Arrastra aquí para quitar una función"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"Necesitas al menos <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> recuadros"</string>
<string name="qs_edit" msgid="5583565172803472437">"Editar"</string>
+ <!-- no translation found for qs_edit_tiles (2105215324060865035) -->
+ <skip />
<string name="tuner_time" msgid="2450785840990529997">"Hora"</string>
<string-array name="clock_options">
<item msgid="3986445361435142273">"Mostrar horas, minutos y segundos"</item>
@@ -995,6 +1005,10 @@
<string name="other" msgid="429768510980739978">"Otros"</string>
<string name="accessibility_qs_edit_toggle_tile_size_action" msgid="1485194410119733586">"mostrar el tamaño del recuadro"</string>
<string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"quitar recuadro"</string>
+ <!-- no translation found for accessibility_qs_edit_toggle_placement_mode (3870429389210569610) -->
+ <skip />
+ <!-- no translation found for accessibility_qs_edit_toggle_selection (2201248304072372239) -->
+ <skip />
<string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"añadir el recuadro a la última posición"</string>
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Mover recuadro"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"Añadir recuadro a la posición deseada"</string>
@@ -1570,5 +1584,7 @@
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Desconocido"</string>
<string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"¿Borrar todos los recuadros?"</string>
<string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Todos los recuadros de ajustes rápidos se restablecerán a los ajustes originales del dispositivo"</string>
+ <!-- no translation found for demote_explain_text (1600426458580544250) -->
+ <skip />
<string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
</resources>
diff --git a/packages/SystemUI/res/values-es/tiles_states_strings.xml b/packages/SystemUI/res/values-es/tiles_states_strings.xml
index 546190fc7702..57b140db744e 100644
--- a/packages/SystemUI/res/values-es/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-es/tiles_states_strings.xml
@@ -56,9 +56,11 @@
<item msgid="5376619709702103243">"Desactivado"</item>
<item msgid="4875147066469902392">"Activado"</item>
</string-array>
- <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) -->
- <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) -->
- <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) -->
+ <string-array name="tile_states_modes_dnd">
+ <item msgid="6509540227356524582">"No disponible"</item>
+ <item msgid="8589336868985358191">"Desactivado"</item>
+ <item msgid="726072717827778234">"Activado"</item>
+ </string-array>
<string-array name="tile_states_flashlight">
<item msgid="3465257127433353857">"No disponible"</item>
<item msgid="5044688398303285224">"Desactivado"</item>
diff --git a/packages/SystemUI/res/values-et/strings.xml b/packages/SystemUI/res/values-et/strings.xml
index 2928c96b9689..2412cfbef1f9 100644
--- a/packages/SystemUI/res/values-et/strings.xml
+++ b/packages/SystemUI/res/values-et/strings.xml
@@ -419,6 +419,11 @@
<string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Uue seadme sidumiseks klõpsake"</string>
<string name="hearing_devices_presets_error" msgid="350363093458408536">"Eelseadistust ei saanud värskendada"</string>
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"Eelseadistus"</string>
+ <string name="hearing_devices_input_routing_label" msgid="730396728151232306">"Kõnede vaikemikrofon"</string>
+ <string-array name="hearing_device_input_routing_options">
+ <item msgid="4582190415045337003">"Kuuldeaparaadi mikrofon"</item>
+ <item msgid="8501466270452446450">"Selle telefoni mikrofon"</item>
+ </string-array>
<string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"Valitud"</string>
<string name="hearing_devices_ambient_label" msgid="629440938614895797">"Ümbritsevad helid"</string>
<string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Vasakule"</string>
@@ -582,6 +587,8 @@
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Rakenduse jagamisel on kogu rakenduses kuvatav või esitatav sisu nähtav rakendusele <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Seega olge ettevaatlik selliste andmetega nagu paroolid, makseteave, sõnumid, fotod ning heli ja video."</string>
<string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Jaga ekraani"</string>
<string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g> on selle valiku keelanud"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_not_supported (4860247304058870233) -->
+ <skip />
<string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"Vali jagamiseks rakendus"</string>
<string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Kas kanda ekraanikuva üle?"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Ühe rakenduse ülekandmine"</string>
@@ -794,8 +801,7 @@
<string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Sees – näopõhine"</string>
<string name="inline_done_button" msgid="6043094985588909584">"Valmis"</string>
<string name="inline_ok_button" msgid="603075490581280343">"Rakenda"</string>
- <!-- no translation found for inline_turn_off_notifications (2653064779176881329) -->
- <skip />
+ <string name="inline_turn_off_notifications" msgid="2653064779176881329">"Lülita välja"</string>
<string name="notification_silence_title" msgid="8608090968400832335">"Hääletu"</string>
<string name="notification_alert_title" msgid="3656229781017543655">"Vaikeseade"</string>
<string name="notification_automatic_title" msgid="3745465364578762652">"Automaatne"</string>
@@ -977,9 +983,13 @@
<string name="right_icon" msgid="1103955040645237425">"Parem ikoon"</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"Paanide lisamiseks hoidke all ja lohistage"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"Paanide ümberpaigutamiseks hoidke neid all ja lohistage"</string>
+ <!-- no translation found for tap_to_position_tile (6282815817773342757) -->
+ <skip />
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"Lohistage eemaldamiseks siia"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"Teil on vaja vähemalt <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> paani"</string>
<string name="qs_edit" msgid="5583565172803472437">"Muutmine"</string>
+ <!-- no translation found for qs_edit_tiles (2105215324060865035) -->
+ <skip />
<string name="tuner_time" msgid="2450785840990529997">"Kellaaeg"</string>
<string-array name="clock_options">
<item msgid="3986445361435142273">"Kuva tunnid, minutid ja sekundid"</item>
@@ -995,6 +1005,10 @@
<string name="other" msgid="429768510980739978">"Muu"</string>
<string name="accessibility_qs_edit_toggle_tile_size_action" msgid="1485194410119733586">"muutke paani suurust"</string>
<string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"paani eemaldamiseks"</string>
+ <!-- no translation found for accessibility_qs_edit_toggle_placement_mode (3870429389210569610) -->
+ <skip />
+ <!-- no translation found for accessibility_qs_edit_toggle_selection (2201248304072372239) -->
+ <skip />
<string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"lisage paan viimasesse asukohta"</string>
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Teisalda paan"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"Lisage paan soovitud asukohta"</string>
@@ -1570,5 +1584,7 @@
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Teadmata"</string>
<string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"Kas lähtestada kõik paanid?"</string>
<string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Kõik kiirseadete paanid lähtestatakse seadme algseadetele"</string>
+ <!-- no translation found for demote_explain_text (1600426458580544250) -->
+ <skip />
<string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
</resources>
diff --git a/packages/SystemUI/res/values-et/tiles_states_strings.xml b/packages/SystemUI/res/values-et/tiles_states_strings.xml
index 1defe925cf75..819e9761225c 100644
--- a/packages/SystemUI/res/values-et/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-et/tiles_states_strings.xml
@@ -56,9 +56,11 @@
<item msgid="5376619709702103243">"Väljas"</item>
<item msgid="4875147066469902392">"Sees"</item>
</string-array>
- <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) -->
- <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) -->
- <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) -->
+ <string-array name="tile_states_modes_dnd">
+ <item msgid="6509540227356524582">"Pole saadaval"</item>
+ <item msgid="8589336868985358191">"Väljas"</item>
+ <item msgid="726072717827778234">"Sees"</item>
+ </string-array>
<string-array name="tile_states_flashlight">
<item msgid="3465257127433353857">"Pole saadaval"</item>
<item msgid="5044688398303285224">"Väljas"</item>
diff --git a/packages/SystemUI/res/values-eu/strings.xml b/packages/SystemUI/res/values-eu/strings.xml
index 99dec017c0f4..1a960ec47e3c 100644
--- a/packages/SystemUI/res/values-eu/strings.xml
+++ b/packages/SystemUI/res/values-eu/strings.xml
@@ -419,6 +419,11 @@
<string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Egin klik beste gailu bat parekatzeko"</string>
<string name="hearing_devices_presets_error" msgid="350363093458408536">"Ezin izan da eguneratu aurrezarpena"</string>
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"Aurrezarpena"</string>
+ <string name="hearing_devices_input_routing_label" msgid="730396728151232306">"Deiak egiteko mikrofono lehenetsia"</string>
+ <string-array name="hearing_device_input_routing_options">
+ <item msgid="4582190415045337003">"Audifonoaren mikrofonoa"</item>
+ <item msgid="8501466270452446450">"Telefono honen mikrofonoa"</item>
+ </string-array>
<string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"Hautatuta"</string>
<string name="hearing_devices_ambient_label" msgid="629440938614895797">"Ingurunea"</string>
<string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Ezkerrekoa"</string>
@@ -582,6 +587,8 @@
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Aplikazio bat partekatzen ari zarenean, aplikazio horretan agertzen den edo bertan erreproduzitzen ari den guztia ikusi dezake <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> aplikazioak. Beraz, kontuz ibili pasahitzekin, ordainketen xehetasunekin, mezuekin, argazkiekin, audioekin eta bideoekin, besteak beste."</string>
<string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Partekatu pantaila"</string>
<string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g> aplikazioak aukera desgaitu du"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_not_supported (4860247304058870233) -->
+ <skip />
<string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"Aukeratu zein aplikazio partekatu nahi duzun"</string>
<string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Pantaila igorri nahi duzu?"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Igorri aplikazio bat"</string>
@@ -794,8 +801,7 @@
<string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Aktibatuta: aurpegian oinarrituta"</string>
<string name="inline_done_button" msgid="6043094985588909584">"Eginda"</string>
<string name="inline_ok_button" msgid="603075490581280343">"Aplikatu"</string>
- <!-- no translation found for inline_turn_off_notifications (2653064779176881329) -->
- <skip />
+ <string name="inline_turn_off_notifications" msgid="2653064779176881329">"Desaktibatu"</string>
<string name="notification_silence_title" msgid="8608090968400832335">"Isila"</string>
<string name="notification_alert_title" msgid="3656229781017543655">"Lehenetsia"</string>
<string name="notification_automatic_title" msgid="3745465364578762652">"Automatikoa"</string>
@@ -977,9 +983,13 @@
<string name="right_icon" msgid="1103955040645237425">"Eskuineko ikonoa"</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"Lauzak gehitzeko, eduki itzazu sakatuta, eta arrastatu"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"Lauzak antolatzeko, eduki itzazu sakatuta, eta arrastatu"</string>
+ <!-- no translation found for tap_to_position_tile (6282815817773342757) -->
+ <skip />
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"Kentzeko, arrastatu hona"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"<xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> lauza behar dituzu gutxienez"</string>
<string name="qs_edit" msgid="5583565172803472437">"Editatu"</string>
+ <!-- no translation found for qs_edit_tiles (2105215324060865035) -->
+ <skip />
<string name="tuner_time" msgid="2450785840990529997">"Ordua"</string>
<string-array name="clock_options">
<item msgid="3986445361435142273">"Erakutsi orduak, minutuak eta segundoak"</item>
@@ -995,6 +1005,10 @@
<string name="other" msgid="429768510980739978">"Beste bat"</string>
<string name="accessibility_qs_edit_toggle_tile_size_action" msgid="1485194410119733586">"aldatu lauzaren tamaina"</string>
<string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"kendu lauza"</string>
+ <!-- no translation found for accessibility_qs_edit_toggle_placement_mode (3870429389210569610) -->
+ <skip />
+ <!-- no translation found for accessibility_qs_edit_toggle_selection (2201248304072372239) -->
+ <skip />
<string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"gehitu lauza azken posizioan"</string>
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Mugitu lauza"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"Gehitu lauza nahi duzun posizioan"</string>
@@ -1570,5 +1584,7 @@
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Ezezagunak"</string>
<string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"Lauza guztiak berrezarri nahi dituzu?"</string>
<string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Gailuaren jatorrizko ezarpenak berrezarriko dira ezarpen bizkorren lauza guztietan"</string>
+ <!-- no translation found for demote_explain_text (1600426458580544250) -->
+ <skip />
<string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
</resources>
diff --git a/packages/SystemUI/res/values-eu/tiles_states_strings.xml b/packages/SystemUI/res/values-eu/tiles_states_strings.xml
index 923d84f23025..b37154e2d1e8 100644
--- a/packages/SystemUI/res/values-eu/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-eu/tiles_states_strings.xml
@@ -56,9 +56,11 @@
<item msgid="5376619709702103243">"Desaktibatuta"</item>
<item msgid="4875147066469902392">"Aktibatuta"</item>
</string-array>
- <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) -->
- <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) -->
- <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) -->
+ <string-array name="tile_states_modes_dnd">
+ <item msgid="6509540227356524582">"Ez dago erabilgarri"</item>
+ <item msgid="8589336868985358191">"Desaktibatuta"</item>
+ <item msgid="726072717827778234">"Aktibatuta"</item>
+ </string-array>
<string-array name="tile_states_flashlight">
<item msgid="3465257127433353857">"Ez dago erabilgarri"</item>
<item msgid="5044688398303285224">"Desaktibatuta"</item>
diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml
index db76009629c4..3b214ba71022 100644
--- a/packages/SystemUI/res/values-fa/strings.xml
+++ b/packages/SystemUI/res/values-fa/strings.xml
@@ -419,6 +419,11 @@
<string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"برای جفت کردن دستگاه جدید، کلیک کنید"</string>
<string name="hearing_devices_presets_error" msgid="350363093458408536">"پیش‌تنظیم به‌روزرسانی نشد"</string>
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"پیش‌تنظیم"</string>
+ <string name="hearing_devices_input_routing_label" msgid="730396728151232306">"میکروفون پیش‌فرض برای تماس‌ها"</string>
+ <string-array name="hearing_device_input_routing_options">
+ <item msgid="4582190415045337003">"میکروفون سمعک"</item>
+ <item msgid="8501466270452446450">"میکروفون این تلفن"</item>
+ </string-array>
<string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"انتخاب‌شده"</string>
<string name="hearing_devices_ambient_label" msgid="629440938614895797">"پیرامون"</string>
<string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"چپ"</string>
@@ -582,6 +587,7 @@
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"وقتی برنامه‌ای را هم‌رسانی می‌کنید، هر چیزی که در آن برنامه نمایش داده شود یا پخش شود برای <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> قابل‌مشاهده خواهد بود. درنتیجه مراقب چیزهایی مثل گذرواژه‌ها، جزئیات پرداخت، پیام‌ها، عکس‌ها، و صدا و تصویر باشید."</string>
<string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"هم‌رسانی صفحه‌نمایش"</string>
<string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g>این گزینه را غیرفعال کرده است"</string>
+ <string name="media_projection_entry_app_permission_dialog_single_app_not_supported" msgid="4860247304058870233">"توسط برنامه پشتیبانی نمی‌شود"</string>
<string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"برنامه‌ای را برای هم‌رسانی انتخاب کنید"</string>
<string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"محتوای صفحه‌نمایش شما پخش شود؟"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"پخش کردن محتوای یک برنامه"</string>
@@ -794,8 +800,7 @@
<string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"روشن - براساس چهره"</string>
<string name="inline_done_button" msgid="6043094985588909584">"تمام"</string>
<string name="inline_ok_button" msgid="603075490581280343">"اعمال"</string>
- <!-- no translation found for inline_turn_off_notifications (2653064779176881329) -->
- <skip />
+ <string name="inline_turn_off_notifications" msgid="2653064779176881329">"خاموش کردن"</string>
<string name="notification_silence_title" msgid="8608090968400832335">"بی‌صدا"</string>
<string name="notification_alert_title" msgid="3656229781017543655">"پیش‌فرض"</string>
<string name="notification_automatic_title" msgid="3745465364578762652">"خودکار"</string>
@@ -977,9 +982,13 @@
<string name="right_icon" msgid="1103955040645237425">"نماد راست"</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"برای افزودن کاشی، نگه دارید و بکشید"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"برای تغییر دادن ترتیب کاشی‌ها، آن‌ها را نگه دارید و بکشید"</string>
+ <!-- no translation found for tap_to_position_tile (6282815817773342757) -->
+ <skip />
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"برای حذف، به اینجا بکشید"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"حداقل به <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> کاشی نیاز دارید"</string>
<string name="qs_edit" msgid="5583565172803472437">"ویرایش"</string>
+ <!-- no translation found for qs_edit_tiles (2105215324060865035) -->
+ <skip />
<string name="tuner_time" msgid="2450785840990529997">"زمان"</string>
<string-array name="clock_options">
<item msgid="3986445361435142273">"ساعت، دقیقه و ثانیه نشان داده شود"</item>
@@ -995,6 +1004,10 @@
<string name="other" msgid="429768510980739978">"موارد دیگر"</string>
<string name="accessibility_qs_edit_toggle_tile_size_action" msgid="1485194410119733586">"تغییر اندازه کاشی"</string>
<string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"برداشتن کاشی"</string>
+ <!-- no translation found for accessibility_qs_edit_toggle_placement_mode (3870429389210569610) -->
+ <skip />
+ <!-- no translation found for accessibility_qs_edit_toggle_selection (2201248304072372239) -->
+ <skip />
<string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"افزودن کاشی به آخرین جایگاه"</string>
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"انتقال کاشی"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"افزودن کاشی به جایگاه دلخواه"</string>
@@ -1570,5 +1583,7 @@
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"نامشخص"</string>
<string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"همه کاشی‌ها بازنشانی شود؟"</string>
<string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"همه کاشی‌های «تنظیمات فوری» به تنظیمات اصلی دستگاه بازنشانی خواهد شد"</string>
+ <!-- no translation found for demote_explain_text (1600426458580544250) -->
+ <skip />
<string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>، <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
</resources>
diff --git a/packages/SystemUI/res/values-fa/tiles_states_strings.xml b/packages/SystemUI/res/values-fa/tiles_states_strings.xml
index ee9429402779..68ca663eadc1 100644
--- a/packages/SystemUI/res/values-fa/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-fa/tiles_states_strings.xml
@@ -56,9 +56,11 @@
<item msgid="5376619709702103243">"خاموش"</item>
<item msgid="4875147066469902392">"روشن"</item>
</string-array>
- <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) -->
- <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) -->
- <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) -->
+ <string-array name="tile_states_modes_dnd">
+ <item msgid="6509540227356524582">"دردسترس نیست"</item>
+ <item msgid="8589336868985358191">"خاموش"</item>
+ <item msgid="726072717827778234">"روشن"</item>
+ </string-array>
<string-array name="tile_states_flashlight">
<item msgid="3465257127433353857">"دردسترس نیست"</item>
<item msgid="5044688398303285224">"خاموش"</item>
diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml
index 8df9aeb04f92..383830342480 100644
--- a/packages/SystemUI/res/values-fi/strings.xml
+++ b/packages/SystemUI/res/values-fi/strings.xml
@@ -421,6 +421,11 @@
<string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Muodosta uusi laitepari klikkaamalla"</string>
<string name="hearing_devices_presets_error" msgid="350363093458408536">"Esiasetusta ei voitu muuttaa"</string>
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"Esiasetus"</string>
+ <string name="hearing_devices_input_routing_label" msgid="730396728151232306">"Puhelujen oletusmikrofoni"</string>
+ <string-array name="hearing_device_input_routing_options">
+ <item msgid="4582190415045337003">"Kuulolaitteen mikrofoni"</item>
+ <item msgid="8501466270452446450">"Tämän puhelimen mikrofoni"</item>
+ </string-array>
<string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"Valittu"</string>
<string name="hearing_devices_ambient_label" msgid="629440938614895797">"Ympäristö"</string>
<string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Vasen"</string>
@@ -584,6 +589,8 @@
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Kun jaat sovelluksen, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> näkee kaiken sovelluksessa näkyvän tai toistetun sisällön. Ole siis varovainen, kun lisäät salasanoja, maksutietoja, viestejä, kuvia, audiota tai videoita."</string>
<string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Jaa näyttö"</string>
<string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g> on poistanut vaihtoehdon käytöstä"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_not_supported (4860247304058870233) -->
+ <skip />
<string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"Valitse jaettava sovellus"</string>
<string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Striimataanko näyttö?"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Striimaa yksi sovellus"</string>
@@ -796,8 +803,7 @@
<string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Päällä – kasvojen perusteella"</string>
<string name="inline_done_button" msgid="6043094985588909584">"Valmis"</string>
<string name="inline_ok_button" msgid="603075490581280343">"Käytä"</string>
- <!-- no translation found for inline_turn_off_notifications (2653064779176881329) -->
- <skip />
+ <string name="inline_turn_off_notifications" msgid="2653064779176881329">"Laita pois päältä"</string>
<string name="notification_silence_title" msgid="8608090968400832335">"Äänetön"</string>
<string name="notification_alert_title" msgid="3656229781017543655">"Oletus"</string>
<string name="notification_automatic_title" msgid="3745465364578762652">"Automaattinen"</string>
@@ -979,9 +985,13 @@
<string name="right_icon" msgid="1103955040645237425">"Oikea kuvake"</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"Lisää osioita koskettamalla pitkään ja vetämällä"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"Järjestele koskettamalla pitkään ja vetämällä"</string>
+ <!-- no translation found for tap_to_position_tile (6282815817773342757) -->
+ <skip />
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"Poista vetämällä tähän."</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"<xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> kiekkoa on vähimmäismäärä"</string>
<string name="qs_edit" msgid="5583565172803472437">"Muokkaa"</string>
+ <!-- no translation found for qs_edit_tiles (2105215324060865035) -->
+ <skip />
<string name="tuner_time" msgid="2450785840990529997">"Aika"</string>
<string-array name="clock_options">
<item msgid="3986445361435142273">"Näytä tunnit, minuutit ja sekunnit"</item>
@@ -997,6 +1007,10 @@
<string name="other" msgid="429768510980739978">"Muu"</string>
<string name="accessibility_qs_edit_toggle_tile_size_action" msgid="1485194410119733586">"ruudun koko päälle/pois"</string>
<string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"poista kiekko"</string>
+ <!-- no translation found for accessibility_qs_edit_toggle_placement_mode (3870429389210569610) -->
+ <skip />
+ <!-- no translation found for accessibility_qs_edit_toggle_selection (2201248304072372239) -->
+ <skip />
<string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"lisää laatta viimeiseen kohtaan"</string>
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Siirrä kiekkoa"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"Lisää laatta haluttuun kohtaan"</string>
@@ -1572,5 +1586,7 @@
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Tuntematon"</string>
<string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"Nollataanko kaikki laatat?"</string>
<string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Kaikki pika-asetuslaatat palautetaan laitteen alkuperäisiin asetuksiin"</string>
+ <!-- no translation found for demote_explain_text (1600426458580544250) -->
+ <skip />
<string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
</resources>
diff --git a/packages/SystemUI/res/values-fi/tiles_states_strings.xml b/packages/SystemUI/res/values-fi/tiles_states_strings.xml
index 76a77ad058ef..12487bd4b818 100644
--- a/packages/SystemUI/res/values-fi/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-fi/tiles_states_strings.xml
@@ -56,9 +56,11 @@
<item msgid="5376619709702103243">"Poissa päältä"</item>
<item msgid="4875147066469902392">"Päällä"</item>
</string-array>
- <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) -->
- <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) -->
- <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) -->
+ <string-array name="tile_states_modes_dnd">
+ <item msgid="6509540227356524582">"Ei saatavilla"</item>
+ <item msgid="8589336868985358191">"Pois päältä"</item>
+ <item msgid="726072717827778234">"Päällä"</item>
+ </string-array>
<string-array name="tile_states_flashlight">
<item msgid="3465257127433353857">"Ei saatavilla"</item>
<item msgid="5044688398303285224">"Poissa päältä"</item>
diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml
index 9bd16d975d74..a6395b84cb0b 100644
--- a/packages/SystemUI/res/values-fr-rCA/strings.xml
+++ b/packages/SystemUI/res/values-fr-rCA/strings.xml
@@ -419,6 +419,11 @@
<string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Cliquez ici pour associer un nouvel appareil"</string>
<string name="hearing_devices_presets_error" msgid="350363093458408536">"Impossible de mettre à jour le préréglage"</string>
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"Préréglage"</string>
+ <string name="hearing_devices_input_routing_label" msgid="730396728151232306">"Microphone par défaut pour les appels"</string>
+ <string-array name="hearing_device_input_routing_options">
+ <item msgid="4582190415045337003">"Microphone pour prothèse auditive"</item>
+ <item msgid="8501466270452446450">"Le microphone de ce téléphone"</item>
+ </string-array>
<string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"Sélectionné"</string>
<string name="hearing_devices_ambient_label" msgid="629440938614895797">"Environnement"</string>
<string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Gauche"</string>
@@ -582,6 +587,7 @@
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Lorsque vous partagez une appli, tout ce qui s\'y affiche ou s\'y joue est visible par <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Par conséquent, soyez prudent avec les mots de passe, les détails du mode de paiement, les messages, les photos et les contenus audio et vidéo."</string>
<string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Partager l\'écran"</string>
<string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g> a désactivé cette option"</string>
+ <string name="media_projection_entry_app_permission_dialog_single_app_not_supported" msgid="4860247304058870233">"Non pris en charge par l\'appli"</string>
<string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"Choisir l\'appli à partager"</string>
<string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Diffuser votre écran?"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Diffuser une appli"</string>
@@ -794,8 +800,7 @@
<string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Activé : en fonction du visage"</string>
<string name="inline_done_button" msgid="6043094985588909584">"Terminé"</string>
<string name="inline_ok_button" msgid="603075490581280343">"Appliquer"</string>
- <!-- no translation found for inline_turn_off_notifications (2653064779176881329) -->
- <skip />
+ <string name="inline_turn_off_notifications" msgid="2653064779176881329">"Désactiver"</string>
<string name="notification_silence_title" msgid="8608090968400832335">"Mode silencieux"</string>
<string name="notification_alert_title" msgid="3656229781017543655">"Par défaut"</string>
<string name="notification_automatic_title" msgid="3745465364578762652">"Automatique"</string>
@@ -977,9 +982,13 @@
<string name="right_icon" msgid="1103955040645237425">"Icône droite"</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"Sélectionnez et faites glisser les tuiles pour les ajouter"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"Faites glisser les tuiles pour les réorganiser"</string>
+ <!-- no translation found for tap_to_position_tile (6282815817773342757) -->
+ <skip />
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"Faites glisser les tuiles ici pour les retirer"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"Vous avez besoin d\'au moins <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> tuiles"</string>
<string name="qs_edit" msgid="5583565172803472437">"Modifier"</string>
+ <!-- no translation found for qs_edit_tiles (2105215324060865035) -->
+ <skip />
<string name="tuner_time" msgid="2450785840990529997">"Heure"</string>
<string-array name="clock_options">
<item msgid="3986445361435142273">"Afficher les heures, les minutes et les secondes"</item>
@@ -995,6 +1004,10 @@
<string name="other" msgid="429768510980739978">"Autre"</string>
<string name="accessibility_qs_edit_toggle_tile_size_action" msgid="1485194410119733586">"activer ou désactiver la taille de la tuile"</string>
<string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"retirer la tuile"</string>
+ <!-- no translation found for accessibility_qs_edit_toggle_placement_mode (3870429389210569610) -->
+ <skip />
+ <!-- no translation found for accessibility_qs_edit_toggle_selection (2201248304072372239) -->
+ <skip />
<string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"Ajouter une tuile à la dernière position"</string>
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Déplacer la tuile"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"Ajouter une tuile à la position désirée"</string>
@@ -1570,5 +1583,7 @@
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Inconnu"</string>
<string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"Réinitialiser toutes les tuiles?"</string>
<string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Toutes les tuiles des paramètres rapides seront réinitialisées aux paramètres par défaut de l\'appareil."</string>
+ <!-- no translation found for demote_explain_text (1600426458580544250) -->
+ <skip />
<string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
</resources>
diff --git a/packages/SystemUI/res/values-fr-rCA/tiles_states_strings.xml b/packages/SystemUI/res/values-fr-rCA/tiles_states_strings.xml
index 01f33917a979..6b94691d729f 100644
--- a/packages/SystemUI/res/values-fr-rCA/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-fr-rCA/tiles_states_strings.xml
@@ -56,9 +56,11 @@
<item msgid="5376619709702103243">"Désactivée"</item>
<item msgid="4875147066469902392">"Activé"</item>
</string-array>
- <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) -->
- <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) -->
- <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) -->
+ <string-array name="tile_states_modes_dnd">
+ <item msgid="6509540227356524582">"Non accessible"</item>
+ <item msgid="8589336868985358191">"Désactivé"</item>
+ <item msgid="726072717827778234">"Activé"</item>
+ </string-array>
<string-array name="tile_states_flashlight">
<item msgid="3465257127433353857">"Non disponible"</item>
<item msgid="5044688398303285224">"Désactivée"</item>
diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml
index 63ac0cab4b17..97181d6b338d 100644
--- a/packages/SystemUI/res/values-fr/strings.xml
+++ b/packages/SystemUI/res/values-fr/strings.xml
@@ -419,6 +419,11 @@
<string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Cliquer pour associer un nouvel appareil"</string>
<string name="hearing_devices_presets_error" msgid="350363093458408536">"Impossible de mettre à jour les préréglages"</string>
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"Préréglage"</string>
+ <string name="hearing_devices_input_routing_label" msgid="730396728151232306">"Micro par défaut pour les appels"</string>
+ <string-array name="hearing_device_input_routing_options">
+ <item msgid="4582190415045337003">"Micro de l\'appareil auditif"</item>
+ <item msgid="8501466270452446450">"Micro de ce téléphone"</item>
+ </string-array>
<string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"Sélectionné"</string>
<string name="hearing_devices_ambient_label" msgid="629440938614895797">"Sons environnants"</string>
<string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Gauche"</string>
@@ -582,6 +587,8 @@
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Lorsque vous partagez une appli, tout ce qui est affiché ou lu dans celle-ci est visible par <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Faites donc attention aux éléments tels que les mots de passe, les détails du mode de paiement, les messages, les photos et les contenus audio et vidéo."</string>
<string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Partager l\'écran"</string>
<string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g> a désactivé cette option"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_not_supported (4860247304058870233) -->
+ <skip />
<string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"Choisir l\'appli à partager"</string>
<string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Caster votre écran ?"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Caster une appli"</string>
@@ -794,8 +801,7 @@
<string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Active - En fonction du visage"</string>
<string name="inline_done_button" msgid="6043094985588909584">"OK"</string>
<string name="inline_ok_button" msgid="603075490581280343">"Appliquer"</string>
- <!-- no translation found for inline_turn_off_notifications (2653064779176881329) -->
- <skip />
+ <string name="inline_turn_off_notifications" msgid="2653064779176881329">"Désactiver"</string>
<string name="notification_silence_title" msgid="8608090968400832335">"Silencieux"</string>
<string name="notification_alert_title" msgid="3656229781017543655">"Par défaut"</string>
<string name="notification_automatic_title" msgid="3745465364578762652">"Automatique"</string>
@@ -977,9 +983,13 @@
<string name="right_icon" msgid="1103955040645237425">"Icône droite"</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"Faites glisser les blocs pour les ajouter"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"Faites glisser les blocs pour les réorganiser"</string>
+ <!-- no translation found for tap_to_position_tile (6282815817773342757) -->
+ <skip />
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"Faites glisser les blocs ici pour les supprimer"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"Au minimum <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> tuiles sont nécessaires"</string>
<string name="qs_edit" msgid="5583565172803472437">"Modifier"</string>
+ <!-- no translation found for qs_edit_tiles (2105215324060865035) -->
+ <skip />
<string name="tuner_time" msgid="2450785840990529997">"Heure"</string>
<string-array name="clock_options">
<item msgid="3986445361435142273">"Afficher les heures, les minutes et les secondes"</item>
@@ -995,6 +1005,10 @@
<string name="other" msgid="429768510980739978">"Autre"</string>
<string name="accessibility_qs_edit_toggle_tile_size_action" msgid="1485194410119733586">"activer/désactiver la taille du bloc"</string>
<string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"supprimer le bloc"</string>
+ <!-- no translation found for accessibility_qs_edit_toggle_placement_mode (3870429389210569610) -->
+ <skip />
+ <!-- no translation found for accessibility_qs_edit_toggle_selection (2201248304072372239) -->
+ <skip />
<string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"ajouter le bloc à la dernière position"</string>
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Déplacer le bloc"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"Ajouter le bloc à la position souhaitée"</string>
@@ -1570,5 +1584,7 @@
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Inconnu"</string>
<string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"Réinitialiser tous les blocs ?"</string>
<string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Tous les blocs \"Réglages rapides\" seront réinitialisés aux paramètres d\'origine de l\'appareil"</string>
+ <!-- no translation found for demote_explain_text (1600426458580544250) -->
+ <skip />
<string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
</resources>
diff --git a/packages/SystemUI/res/values-fr/tiles_states_strings.xml b/packages/SystemUI/res/values-fr/tiles_states_strings.xml
index 620e46c88cd5..ea91d2f7e016 100644
--- a/packages/SystemUI/res/values-fr/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-fr/tiles_states_strings.xml
@@ -56,9 +56,11 @@
<item msgid="5376619709702103243">"Désactivé"</item>
<item msgid="4875147066469902392">"Activé"</item>
</string-array>
- <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) -->
- <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) -->
- <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) -->
+ <string-array name="tile_states_modes_dnd">
+ <item msgid="6509540227356524582">"Indisponible"</item>
+ <item msgid="8589336868985358191">"Désactivé"</item>
+ <item msgid="726072717827778234">"Activé"</item>
+ </string-array>
<string-array name="tile_states_flashlight">
<item msgid="3465257127433353857">"Indisponible"</item>
<item msgid="5044688398303285224">"Désactivé"</item>
diff --git a/packages/SystemUI/res/values-gl/strings.xml b/packages/SystemUI/res/values-gl/strings.xml
index 80da559ba1c5..42f943f7f50b 100644
--- a/packages/SystemUI/res/values-gl/strings.xml
+++ b/packages/SystemUI/res/values-gl/strings.xml
@@ -419,6 +419,11 @@
<string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Fai clic para vincular un novo dispositivo"</string>
<string name="hearing_devices_presets_error" msgid="350363093458408536">"Non se puido actualizar a configuración predeterminada"</string>
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"Configuración predeterminada"</string>
+ <string name="hearing_devices_input_routing_label" msgid="730396728151232306">"Micrófono predefinido para as chamadas"</string>
+ <string-array name="hearing_device_input_routing_options">
+ <item msgid="4582190415045337003">"O micrófono do audiófono"</item>
+ <item msgid="8501466270452446450">"O micrófono deste teléfono"</item>
+ </string-array>
<string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"Elemento seleccionado"</string>
<string name="hearing_devices_ambient_label" msgid="629440938614895797">"Ambiente"</string>
<string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Esquerdo"</string>
@@ -582,6 +587,8 @@
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Se compartes toda a pantalla, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> poderá ver todo o contido que apareza ou se reproduza nesa aplicación. Ten coidado con determinada información, como os contrasinais, os detalles de pago, as mensaxes e as fotos, así como co contido de audio e de vídeo."</string>
<string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Compartir pantalla"</string>
<string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g> desactivou esta opción"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_not_supported (4860247304058870233) -->
+ <skip />
<string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"Escoller unha aplicación para compartir"</string>
<string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Queres emitir a túa pantalla?"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Emitir unha aplicación"</string>
@@ -794,8 +801,7 @@
<string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Activada: baseada na cara"</string>
<string name="inline_done_button" msgid="6043094985588909584">"Feito"</string>
<string name="inline_ok_button" msgid="603075490581280343">"Aplicar"</string>
- <!-- no translation found for inline_turn_off_notifications (2653064779176881329) -->
- <skip />
+ <string name="inline_turn_off_notifications" msgid="2653064779176881329">"Desactivar"</string>
<string name="notification_silence_title" msgid="8608090968400832335">"Silenciadas"</string>
<string name="notification_alert_title" msgid="3656229781017543655">"Configuración predeterminada"</string>
<string name="notification_automatic_title" msgid="3745465364578762652">"Automática"</string>
@@ -977,9 +983,13 @@
<string name="right_icon" msgid="1103955040645237425">"Icona á dereita"</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"Mantén premido e arrastra para engadir atallos"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"Para reorganizar os atallos, mantenos premidos e arrástraos"</string>
+ <!-- no translation found for tap_to_position_tile (6282815817773342757) -->
+ <skip />
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"Arrastra o elemento ata aquí para quitalo"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"Como mínimo ten que haber <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> mosaicos"</string>
<string name="qs_edit" msgid="5583565172803472437">"Editar"</string>
+ <!-- no translation found for qs_edit_tiles (2105215324060865035) -->
+ <skip />
<string name="tuner_time" msgid="2450785840990529997">"Hora"</string>
<string-array name="clock_options">
<item msgid="3986445361435142273">"Mostrar horas, minutos e segundos"</item>
@@ -995,6 +1005,10 @@
<string name="other" msgid="429768510980739978">"Outros"</string>
<string name="accessibility_qs_edit_toggle_tile_size_action" msgid="1485194410119733586">"cambiar o tamaño do atallo"</string>
<string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"quitar atallo"</string>
+ <!-- no translation found for accessibility_qs_edit_toggle_placement_mode (3870429389210569610) -->
+ <skip />
+ <!-- no translation found for accessibility_qs_edit_toggle_selection (2201248304072372239) -->
+ <skip />
<string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"engadir o atallo á última posición"</string>
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Mover atallo"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"Engadir o atallo á última posición"</string>
@@ -1570,5 +1584,7 @@
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Categoría descoñecida"</string>
<string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"Queres restablecer todos os atallos?"</string>
<string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Restablecerase a configuración orixinal do dispositivo para todos os atallos de Configuración rápida"</string>
+ <!-- no translation found for demote_explain_text (1600426458580544250) -->
+ <skip />
<string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
</resources>
diff --git a/packages/SystemUI/res/values-gl/tiles_states_strings.xml b/packages/SystemUI/res/values-gl/tiles_states_strings.xml
index ca19e0ecd24d..9b05a8705adc 100644
--- a/packages/SystemUI/res/values-gl/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-gl/tiles_states_strings.xml
@@ -56,9 +56,11 @@
<item msgid="5376619709702103243">"Non"</item>
<item msgid="4875147066469902392">"Si"</item>
</string-array>
- <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) -->
- <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) -->
- <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) -->
+ <string-array name="tile_states_modes_dnd">
+ <item msgid="6509540227356524582">"Non dispoñible"</item>
+ <item msgid="8589336868985358191">"Desactivado"</item>
+ <item msgid="726072717827778234">"Activado"</item>
+ </string-array>
<string-array name="tile_states_flashlight">
<item msgid="3465257127433353857">"Non dispoñible"</item>
<item msgid="5044688398303285224">"Non"</item>
diff --git a/packages/SystemUI/res/values-gu/strings.xml b/packages/SystemUI/res/values-gu/strings.xml
index 17bcdf091f9e..17b598198c7d 100644
--- a/packages/SystemUI/res/values-gu/strings.xml
+++ b/packages/SystemUI/res/values-gu/strings.xml
@@ -252,10 +252,8 @@
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"<xliff:g id="BLUETOOTH">%s</xliff:g> થી કનેક્ટ થયાં."</string>
<string name="accessibility_cast_name" msgid="7344437925388773685">"<xliff:g id="CAST">%s</xliff:g> થી કનેક્ટ કરેલ."</string>
<string name="accessibility_expand_group" msgid="521237935987978624">"ગ્રૂપને મોટું કરો."</string>
- <!-- no translation found for accessibility_add_device_to_group (5446422960697860806) -->
- <skip />
- <!-- no translation found for accessibility_remove_device_from_group (3114694270949142228) -->
- <skip />
+ <string name="accessibility_add_device_to_group" msgid="5446422960697860806">"ડિવાઇસને ગ્રૂપમાં ઉમેરો."</string>
+ <string name="accessibility_remove_device_from_group" msgid="3114694270949142228">"ડિવાઇસને ગ્રૂપમાંથી કાઢી નાખો."</string>
<string name="accessibility_open_application" msgid="1749126077501259712">"ઍપ્લિકેશન ખોલો."</string>
<string name="accessibility_not_connected" msgid="4061305616351042142">"કનેક્ટ થયેલું નથી."</string>
<string name="data_connection_roaming" msgid="375650836665414797">"રોમિંગ"</string>
@@ -333,8 +331,7 @@
<string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"ઇનપુટ"</string>
<string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"સાંભળવામાં મદદ આપતા યંત્રો"</string>
<string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"ચાલુ કરી રહ્યાં છીએ…"</string>
- <!-- no translation found for quick_settings_brightness_unable_adjust_msg (4124028416057617517) -->
- <skip />
+ <string name="quick_settings_brightness_unable_adjust_msg" msgid="4124028416057617517">"બ્રાઇટનેસ ગોઠવી શકાતી નથી કારણ કે તે લોકપ્રિય ઍપ દ્વારા નિયંત્રિત કરવામાં આવી રહી છે"</string>
<string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"ઑટો રોટેટ"</string>
<string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"ઑટો રોટેટ સ્ક્રીન"</string>
<string name="quick_settings_location_label" msgid="2621868789013389163">"લોકેશન"</string>
@@ -422,6 +419,11 @@
<string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"નવા ડિવાઇસ સાથે જોડાણ કરવા માટે ક્લિક કરો"</string>
<string name="hearing_devices_presets_error" msgid="350363093458408536">"પ્રીસેટ અપડેટ કરી શક્યા નથી"</string>
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"પ્રીસેટ"</string>
+ <string name="hearing_devices_input_routing_label" msgid="730396728151232306">"કૉલ માટે ડિફૉલ્ટ માઇક્રોફોન"</string>
+ <string-array name="hearing_device_input_routing_options">
+ <item msgid="4582190415045337003">"સાંભળવામાં મદદ આપતો માઇક્રોફોન"</item>
+ <item msgid="8501466270452446450">"આ ફોનનો માઇક્રોફોન"</item>
+ </string-array>
<string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"પસંદ કરી છે"</string>
<string name="hearing_devices_ambient_label" msgid="629440938614895797">"આસપાસના અવાજો"</string>
<string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"ડાબે"</string>
@@ -585,6 +587,8 @@
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"જ્યારે તમે કોઈ ઍપને શેર કરી રહ્યાં હો, ત્યારે તે ઍપ પર બતાવવામાં કે ચલાવવામાં આવતી હોય તેવી બધી વસ્તુ <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>ને દેખાય છે. તેથી પાસવર્ડ, ચુકવણીની વિગતો, મેસેજ, ફોટા અને ડિવાઇસ પર વાગી રહેલા ઑડિયો તથા વીડિયો જેવી બાબતોને લઈને સાવચેત રહો."</string>
<string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"સ્ક્રીન શેર કરો"</string>
<string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g> દ્વારા આ વિકલ્પ બંધ કરવામાં આવ્યો છે"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_not_supported (4860247304058870233) -->
+ <skip />
<string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"શેર કરવા માટે ઍપ પસંદ કરો"</string>
<string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"તમારી સ્ક્રીનને કાસ્ટ કરીએ?"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"એક ઍપને કાસ્ટ કરો"</string>
@@ -797,8 +801,7 @@
<string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"ચાલુ છે - ચહેરા આધારિત રોટેશન"</string>
<string name="inline_done_button" msgid="6043094985588909584">"થઈ ગયું"</string>
<string name="inline_ok_button" msgid="603075490581280343">"લાગુ કરો"</string>
- <!-- no translation found for inline_turn_off_notifications (2653064779176881329) -->
- <skip />
+ <string name="inline_turn_off_notifications" msgid="2653064779176881329">"બંધ કરો"</string>
<string name="notification_silence_title" msgid="8608090968400832335">"સાઇલન્ટ"</string>
<string name="notification_alert_title" msgid="3656229781017543655">"ડિફૉલ્ટ"</string>
<string name="notification_automatic_title" msgid="3745465364578762652">"ઑટોમૅટિક રીતે"</string>
@@ -980,9 +983,13 @@
<string name="right_icon" msgid="1103955040645237425">"જમણું આઇકન"</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"ટાઇલ ઉમેરવા માટે તેના પર આંગળી દબાવીને ખેંચો"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"ટાઇલને ફરીથી ગોઠવવા માટે આંગળી દબાવીને ખેંચો"</string>
+ <!-- no translation found for tap_to_position_tile (6282815817773342757) -->
+ <skip />
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"દૂર કરવા માટે અહીં ખેંચો"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"તમને ઓછામાં ઓછી <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> ટાઇલની જરૂર છે"</string>
<string name="qs_edit" msgid="5583565172803472437">"ફેરફાર કરો"</string>
+ <!-- no translation found for qs_edit_tiles (2105215324060865035) -->
+ <skip />
<string name="tuner_time" msgid="2450785840990529997">"સમય"</string>
<string-array name="clock_options">
<item msgid="3986445361435142273">"કલાક, મિનિટ અને સેકન્ડ બતાવો"</item>
@@ -998,6 +1005,10 @@
<string name="other" msgid="429768510980739978">"અન્ય"</string>
<string name="accessibility_qs_edit_toggle_tile_size_action" msgid="1485194410119733586">"ટાઇલના કદને ટૉગલ કરો"</string>
<string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"ટાઇલ કાઢી નાખો"</string>
+ <!-- no translation found for accessibility_qs_edit_toggle_placement_mode (3870429389210569610) -->
+ <skip />
+ <!-- no translation found for accessibility_qs_edit_toggle_selection (2201248304072372239) -->
+ <skip />
<string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"છેલ્લા સ્થાનમાં ટાઇલ ઉમેરો"</string>
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"ટાઇલ ખસેડો"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"ઇચ્છિત સ્થાનમાં ટાઇલ ઉમેરો"</string>
@@ -1344,7 +1355,7 @@
<string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"ટાઇલ ઉમેરો"</string>
<string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"ટાઇલ ઉમેરશો નહીં"</string>
<string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"વપરાશકર્તા પસંદ કરો"</string>
- <string name="fgs_manager_footer_label" msgid="8276763570622288231">"{count,plural, =1{# ઍપ સક્રિય છે}one{# ઍપ સક્રિય છે}other{# ઍપ સક્રિય છે}}"</string>
+ <string name="fgs_manager_footer_label" msgid="8276763570622288231">"{count,plural, =1{# ઍપ ઍક્ટિવ છે}one{# ઍપ ઍક્ટિવ છે}other{# ઍપ ઍક્ટિવ છે}}"</string>
<string name="fgs_dot_content_description" msgid="2865071539464777240">"નવી માહિતી"</string>
<string name="fgs_manager_dialog_title" msgid="5879184257257718677">"સક્રિય ઍપ"</string>
<string name="fgs_manager_dialog_message" msgid="2670045017200730076">"જ્યારે તમે આ ઍપનો ઉપયોગ ન કરતા હો, ત્યારે પણ તે સક્રિય અને ચાલતી હોય છે. આનાથી તેની કાર્યક્ષમતામાં સુધારો થાય છે, પરંતુ બૅટરીની આવરદાને અસર પણ થઈ શકે છે."</string>
@@ -1573,5 +1584,7 @@
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"અજાણ"</string>
<string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"તમામ ટાઇલ રીસેટ કરીએ?"</string>
<string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"તમામ ઝડપી સેટિંગ ટાઇલને ડિવાઇસના ઑરિજિનલ સેટિંગ પર રીસેટ કરવામાં આવશે"</string>
+ <!-- no translation found for demote_explain_text (1600426458580544250) -->
+ <skip />
<string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
</resources>
diff --git a/packages/SystemUI/res/values-gu/tiles_states_strings.xml b/packages/SystemUI/res/values-gu/tiles_states_strings.xml
index 759e43664379..c3fb94129883 100644
--- a/packages/SystemUI/res/values-gu/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-gu/tiles_states_strings.xml
@@ -56,9 +56,11 @@
<item msgid="5376619709702103243">"બંધ છે"</item>
<item msgid="4875147066469902392">"ચાલુ છે"</item>
</string-array>
- <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) -->
- <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) -->
- <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) -->
+ <string-array name="tile_states_modes_dnd">
+ <item msgid="6509540227356524582">"અનુપલબ્ધ"</item>
+ <item msgid="8589336868985358191">"બંધ"</item>
+ <item msgid="726072717827778234">"ચાલુ"</item>
+ </string-array>
<string-array name="tile_states_flashlight">
<item msgid="3465257127433353857">"ઉપલબ્ધ નથી"</item>
<item msgid="5044688398303285224">"બંધ છે"</item>
diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml
index f4c05ba2f8c5..0ce73c98f40c 100644
--- a/packages/SystemUI/res/values-hi/strings.xml
+++ b/packages/SystemUI/res/values-hi/strings.xml
@@ -419,6 +419,11 @@
<string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"नया डिवाइस जोड़ने के लिए क्लिक करें"</string>
<string name="hearing_devices_presets_error" msgid="350363093458408536">"प्रीसेट अपडेट नहीं किया जा सका"</string>
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"प्रीसेट"</string>
+ <string name="hearing_devices_input_routing_label" msgid="730396728151232306">"कॉल के लिए डिफ़ॉल्ट माइक्रोफ़ोन"</string>
+ <string-array name="hearing_device_input_routing_options">
+ <item msgid="4582190415045337003">"कान की मशीन के लिए माइक्रोफ़ोन"</item>
+ <item msgid="8501466270452446450">"इस फ़ोन का माइक्रोफ़ोन"</item>
+ </string-array>
<string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"चुना गया"</string>
<string name="hearing_devices_ambient_label" msgid="629440938614895797">"आस-पास का वॉल्यूम"</string>
<string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"बाईं ओर के वॉल्यूम के लिए"</string>
@@ -582,6 +587,8 @@
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"जब कोई ऐप्लिकेशन शेयर किया जाता है, तो उस ऐप्लिकेशन में दिख रहा या चलाया जा रहा पूरा कॉन्टेंट <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> पर दिखता है. इसलिए, पासवर्ड, पेमेंट के तरीके की जानकारी, मैसेज, फ़ोटो, ऑडियो, और वीडियो को लेकर सावधानी बरतें."</string>
<string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"स्क्रीन शेयर करें"</string>
<string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g> ने इस विकल्प को बंद कर दिया है"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_not_supported (4860247304058870233) -->
+ <skip />
<string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"शेयर करने के लिए ऐप्लिकेशन चुनें"</string>
<string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"क्या स्क्रीन को कास्ट करना है?"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"एक ऐप्लिकेशन को कास्ट करें"</string>
@@ -794,8 +801,7 @@
<string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"चालू है - चेहरे की गतिविधि के हिसाब से कैमरे को घुमाने की सुविधा"</string>
<string name="inline_done_button" msgid="6043094985588909584">"हो गया"</string>
<string name="inline_ok_button" msgid="603075490581280343">"लागू करें"</string>
- <!-- no translation found for inline_turn_off_notifications (2653064779176881329) -->
- <skip />
+ <string name="inline_turn_off_notifications" msgid="2653064779176881329">"बंद करें"</string>
<string name="notification_silence_title" msgid="8608090968400832335">"बिना आवाज़ के सूचनाएं दिखाएं"</string>
<string name="notification_alert_title" msgid="3656229781017543655">"डिफ़ॉल्ट"</string>
<string name="notification_automatic_title" msgid="3745465364578762652">"अपने-आप"</string>
@@ -977,9 +983,13 @@
<string name="right_icon" msgid="1103955040645237425">"दायां आइकॉन"</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"टाइल जोड़ने के लिए दबाकर खींचे और छोड़ें"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"टाइल का क्रम फिर से बदलने के लिए उन्हें दबाकर रखें और खींचें"</string>
+ <!-- no translation found for tap_to_position_tile (6282815817773342757) -->
+ <skip />
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"हटाने के लिए यहां खींचें और छोड़ें"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"आपके पास कम से कम <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> टाइलें होनी चाहिए"</string>
<string name="qs_edit" msgid="5583565172803472437">"बदलाव करें"</string>
+ <!-- no translation found for qs_edit_tiles (2105215324060865035) -->
+ <skip />
<string name="tuner_time" msgid="2450785840990529997">"समय"</string>
<string-array name="clock_options">
<item msgid="3986445361435142273">"घंटे, मिनट और सेकंड दिखाएं"</item>
@@ -995,6 +1005,10 @@
<string name="other" msgid="429768510980739978">"अन्य"</string>
<string name="accessibility_qs_edit_toggle_tile_size_action" msgid="1485194410119733586">"टाइल के साइज़ को टॉगल करने के लिए दो बार टैप करें"</string>
<string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"टाइल हटाएं"</string>
+ <!-- no translation found for accessibility_qs_edit_toggle_placement_mode (3870429389210569610) -->
+ <skip />
+ <!-- no translation found for accessibility_qs_edit_toggle_selection (2201248304072372239) -->
+ <skip />
<string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"आखिरी जगह पर टाइल जोड़ें"</string>
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"टाइल को किसी और पोज़िशन पर ले जाएं"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"अपनी पसंदीदा जगह पर टाइल जोड़ें"</string>
@@ -1570,5 +1584,7 @@
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"कोई जानकारी नहीं है"</string>
<string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"क्या सभी टाइल रीसेट करनी हैं?"</string>
<string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"क्विक सेटिंग टाइल, डिवाइस की ओरिजनल सेटिंग पर रीसेट हो जाएंगी"</string>
+ <!-- no translation found for demote_explain_text (1600426458580544250) -->
+ <skip />
<string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
</resources>
diff --git a/packages/SystemUI/res/values-hi/tiles_states_strings.xml b/packages/SystemUI/res/values-hi/tiles_states_strings.xml
index 410f25db0900..d331e3a8a5db 100644
--- a/packages/SystemUI/res/values-hi/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-hi/tiles_states_strings.xml
@@ -56,9 +56,11 @@
<item msgid="5376619709702103243">"बंद है"</item>
<item msgid="4875147066469902392">"चालू है"</item>
</string-array>
- <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) -->
- <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) -->
- <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) -->
+ <string-array name="tile_states_modes_dnd">
+ <item msgid="6509540227356524582">"उपलब्ध नहीं है"</item>
+ <item msgid="8589336868985358191">"बंद है"</item>
+ <item msgid="726072717827778234">"चालू है"</item>
+ </string-array>
<string-array name="tile_states_flashlight">
<item msgid="3465257127433353857">"उपलब्ध नहीं है"</item>
<item msgid="5044688398303285224">"बंद है"</item>
diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml
index 9d2509404a8c..3b2d127010af 100644
--- a/packages/SystemUI/res/values-hr/strings.xml
+++ b/packages/SystemUI/res/values-hr/strings.xml
@@ -419,6 +419,11 @@
<string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Kliknite da biste uparili novi uređaj"</string>
<string name="hearing_devices_presets_error" msgid="350363093458408536">"Ažuriranje unaprijed definiranih postavki nije uspjelo"</string>
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"Unaprijed definirana postavka"</string>
+ <string name="hearing_devices_input_routing_label" msgid="730396728151232306">"Zadani mikrofon za pozive"</string>
+ <string-array name="hearing_device_input_routing_options">
+ <item msgid="4582190415045337003">"Mikrofon slušnog pomagala"</item>
+ <item msgid="8501466270452446450">"Mikrofon ovog telefona"</item>
+ </string-array>
<string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"Odabrano"</string>
<string name="hearing_devices_ambient_label" msgid="629440938614895797">"Okruženje"</string>
<string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Lijevo"</string>
@@ -582,6 +587,8 @@
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Kada dijelite aplikaciju, sve što se prikazuje ili reproducira u toj aplikaciji bit će vidljivo aplikaciji <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Stoga pazite na stvari kao što su zaporke, podaci o plaćanju, poruke, fotografije te audio i videozapisi."</string>
<string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Dijeljenje zaslona"</string>
<string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"Aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> onemogućila je ovu opciju"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_not_supported (4860247304058870233) -->
+ <skip />
<string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"Odaberite aplikaciju za dijeljenje"</string>
<string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Želite li emitirati zaslon?"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Emitiranje jedne aplikacije"</string>
@@ -794,8 +801,7 @@
<string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Uključeno – na temelju lica"</string>
<string name="inline_done_button" msgid="6043094985588909584">"Gotovo"</string>
<string name="inline_ok_button" msgid="603075490581280343">"Primijeni"</string>
- <!-- no translation found for inline_turn_off_notifications (2653064779176881329) -->
- <skip />
+ <string name="inline_turn_off_notifications" msgid="2653064779176881329">"Isključi"</string>
<string name="notification_silence_title" msgid="8608090968400832335">"Bešumno"</string>
<string name="notification_alert_title" msgid="3656229781017543655">"Zadano"</string>
<string name="notification_automatic_title" msgid="3745465364578762652">"Automatski"</string>
@@ -977,9 +983,13 @@
<string name="right_icon" msgid="1103955040645237425">"Desna ikona"</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"Zadržite i povucite da biste dodali pločice"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"Zadržite i povucite da biste premjestili pločice"</string>
+ <!-- no translation found for tap_to_position_tile (6282815817773342757) -->
+ <skip />
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"Povucite ovdje za uklanjanje"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"Potrebno je barem <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> pločica"</string>
<string name="qs_edit" msgid="5583565172803472437">"Uređivanje"</string>
+ <!-- no translation found for qs_edit_tiles (2105215324060865035) -->
+ <skip />
<string name="tuner_time" msgid="2450785840990529997">"Vrijeme"</string>
<string-array name="clock_options">
<item msgid="3986445361435142273">"Prikaži sate, minute i sekunde"</item>
@@ -995,6 +1005,10 @@
<string name="other" msgid="429768510980739978">"Ostalo"</string>
<string name="accessibility_qs_edit_toggle_tile_size_action" msgid="1485194410119733586">"promjenu veličine pločice"</string>
<string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"uklanjanje kartice"</string>
+ <!-- no translation found for accessibility_qs_edit_toggle_placement_mode (3870429389210569610) -->
+ <skip />
+ <!-- no translation found for accessibility_qs_edit_toggle_selection (2201248304072372239) -->
+ <skip />
<string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"dodavanje kartice na posljednji položaj"</string>
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Premještanje kartice"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"Dodajte karticu na željeni položaj"</string>
@@ -1570,5 +1584,7 @@
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Nepoznato"</string>
<string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"Želite li poništiti sve pločice?"</string>
<string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Sve pločice Brze postavke vratit će se na izvorne postavke uređaja"</string>
+ <!-- no translation found for demote_explain_text (1600426458580544250) -->
+ <skip />
<string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
</resources>
diff --git a/packages/SystemUI/res/values-hr/tiles_states_strings.xml b/packages/SystemUI/res/values-hr/tiles_states_strings.xml
index bbfcf840e53f..5784d4735ede 100644
--- a/packages/SystemUI/res/values-hr/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-hr/tiles_states_strings.xml
@@ -56,9 +56,11 @@
<item msgid="5376619709702103243">"Isključeno"</item>
<item msgid="4875147066469902392">"Uključeno"</item>
</string-array>
- <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) -->
- <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) -->
- <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) -->
+ <string-array name="tile_states_modes_dnd">
+ <item msgid="6509540227356524582">"Nedostupno"</item>
+ <item msgid="8589336868985358191">"Isključeno"</item>
+ <item msgid="726072717827778234">"Uključeno"</item>
+ </string-array>
<string-array name="tile_states_flashlight">
<item msgid="3465257127433353857">"Nedostupno"</item>
<item msgid="5044688398303285224">"Isključeno"</item>
diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml
index bdeececfd9ae..b4042be66d9c 100644
--- a/packages/SystemUI/res/values-hu/strings.xml
+++ b/packages/SystemUI/res/values-hu/strings.xml
@@ -419,6 +419,11 @@
<string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Kattintson új eszköz párosításához"</string>
<string name="hearing_devices_presets_error" msgid="350363093458408536">"Nem sikerült frissíteni a beállításkészletet"</string>
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"Beállításkészlet"</string>
+ <string name="hearing_devices_input_routing_label" msgid="730396728151232306">"Alapértelmezett mikrofon a hívásokhoz"</string>
+ <string-array name="hearing_device_input_routing_options">
+ <item msgid="4582190415045337003">"Hallókészülék mikrofonja"</item>
+ <item msgid="8501466270452446450">"A telefon mikrofonja"</item>
+ </string-array>
<string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"Kiválasztva"</string>
<string name="hearing_devices_ambient_label" msgid="629440938614895797">"Környezet"</string>
<string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Bal"</string>
@@ -582,6 +587,8 @@
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Alkalmazás megosztása közben az adott appban megjelenített vagy lejátszott minden tartalom látható a(z) <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> számára. Ezért legyen elővigyázatos a jelszavakkal, a fizetési adatokkal, az üzenetekkel, a fotókkal, valamint a hang- és videófelvételekkel."</string>
<string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Képernyő megosztása"</string>
<string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g> letiltotta ezt a beállítást"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_not_supported (4860247304058870233) -->
+ <skip />
<string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"Válassza ki a megosztani kívánt alkalmazást"</string>
<string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Átküldi a képernyőt?"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Egyetlen app átküldése"</string>
@@ -794,8 +801,7 @@
<string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Be: Arcalapú"</string>
<string name="inline_done_button" msgid="6043094985588909584">"Kész"</string>
<string name="inline_ok_button" msgid="603075490581280343">"Alkalmaz"</string>
- <!-- no translation found for inline_turn_off_notifications (2653064779176881329) -->
- <skip />
+ <string name="inline_turn_off_notifications" msgid="2653064779176881329">"Kikapcsolás"</string>
<string name="notification_silence_title" msgid="8608090968400832335">"Néma"</string>
<string name="notification_alert_title" msgid="3656229781017543655">"Alapértelmezett"</string>
<string name="notification_automatic_title" msgid="3745465364578762652">"Automatikus"</string>
@@ -977,9 +983,13 @@
<string name="right_icon" msgid="1103955040645237425">"Jobb oldali ikon"</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"Tartsa lenyomva, és húzza a mozaikok hozzáadásához"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"Tartsa lenyomva, és húzza a mozaikok átrendezéséhez"</string>
+ <!-- no translation found for tap_to_position_tile (6282815817773342757) -->
+ <skip />
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"Húzza ide az eltávolításhoz"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"Legalább <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> kártya szükséges"</string>
<string name="qs_edit" msgid="5583565172803472437">"Szerkesztés"</string>
+ <!-- no translation found for qs_edit_tiles (2105215324060865035) -->
+ <skip />
<string name="tuner_time" msgid="2450785840990529997">"Idő"</string>
<string-array name="clock_options">
<item msgid="3986445361435142273">"Óra, perc és másodperc megjelenítése"</item>
@@ -995,6 +1005,10 @@
<string name="other" msgid="429768510980739978">"Egyéb"</string>
<string name="accessibility_qs_edit_toggle_tile_size_action" msgid="1485194410119733586">"a mozaik méretének módosítása"</string>
<string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"mozaik eltávolításához"</string>
+ <!-- no translation found for accessibility_qs_edit_toggle_placement_mode (3870429389210569610) -->
+ <skip />
+ <!-- no translation found for accessibility_qs_edit_toggle_selection (2201248304072372239) -->
+ <skip />
<string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"mozaik hozzáadása az utolsó pozícióhoz"</string>
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Mozaik áthelyezése"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"Mozaik hozzáadása a kívánt pozícióhoz"</string>
@@ -1570,5 +1584,7 @@
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Ismeretlen"</string>
<string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"Visszaállítja az összes mozaikot?"</string>
<string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Az összes Gyorsbeállítások mozaik visszaáll az eszköz eredeti beállításaira"</string>
+ <!-- no translation found for demote_explain_text (1600426458580544250) -->
+ <skip />
<string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
</resources>
diff --git a/packages/SystemUI/res/values-hu/tiles_states_strings.xml b/packages/SystemUI/res/values-hu/tiles_states_strings.xml
index 8bd57214a28d..168cd0686e2d 100644
--- a/packages/SystemUI/res/values-hu/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-hu/tiles_states_strings.xml
@@ -56,9 +56,11 @@
<item msgid="5376619709702103243">"Ki"</item>
<item msgid="4875147066469902392">"Be"</item>
</string-array>
- <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) -->
- <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) -->
- <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) -->
+ <string-array name="tile_states_modes_dnd">
+ <item msgid="6509540227356524582">"Nem áll rendelkezésre"</item>
+ <item msgid="8589336868985358191">"Ki"</item>
+ <item msgid="726072717827778234">"Be"</item>
+ </string-array>
<string-array name="tile_states_flashlight">
<item msgid="3465257127433353857">"Nem áll rendelkezésre"</item>
<item msgid="5044688398303285224">"Ki"</item>
diff --git a/packages/SystemUI/res/values-hy/strings.xml b/packages/SystemUI/res/values-hy/strings.xml
index d448486faf64..d606213161b8 100644
--- a/packages/SystemUI/res/values-hy/strings.xml
+++ b/packages/SystemUI/res/values-hy/strings.xml
@@ -419,6 +419,11 @@
<string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Սեղմեք՝ նոր սարք զուգակցելու համար"</string>
<string name="hearing_devices_presets_error" msgid="350363093458408536">"Չհաջողվեց թարմացնել կարգավորումների հավաքածուն"</string>
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"Կարգավորումների հավաքածու"</string>
+ <string name="hearing_devices_input_routing_label" msgid="730396728151232306">"Կանխադրված խոսափող զանգերի համար"</string>
+ <string-array name="hearing_device_input_routing_options">
+ <item msgid="4582190415045337003">"Լսողական սարքի խոսափող"</item>
+ <item msgid="8501466270452446450">"Այս հեռախոսի խոսափողը"</item>
+ </string-array>
<string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"Ընտրված է"</string>
<string name="hearing_devices_ambient_label" msgid="629440938614895797">"Շրջակայք"</string>
<string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Ձախ"</string>
@@ -582,6 +587,8 @@
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Երբ դուք որևէ հավելված եք հեռարձակում, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> հավելվածին տեսանելի կլինի այն ամենը, ինչ ցուցադրվում կամ նվագարկվում է այդ հավելվածում։ Ուստի ուշադիր եղեք այնպիսի բաների հետ, ինչպիսիք են գաղտնաբառերը, վճարային տվյալները, հաղորդագրությունները, լուսանկարները, աուդիո և վիդեո բովանդակությունը։"</string>
<string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Ցուցադրել էկրանը"</string>
<string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g>-ն անջատել է այս ընտրանքը"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_not_supported (4860247304058870233) -->
+ <skip />
<string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"Հավելվածի ընտրություն՝ կիսվելու համար"</string>
<string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Հեռարձակե՞լ ձեր էկրանը"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Հեռարձակել մեկ հավելված"</string>
@@ -794,8 +801,7 @@
<string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Միաց․ – Դիմաճանաչման հիման վրա"</string>
<string name="inline_done_button" msgid="6043094985588909584">"Փակել"</string>
<string name="inline_ok_button" msgid="603075490581280343">"Կիրառել"</string>
- <!-- no translation found for inline_turn_off_notifications (2653064779176881329) -->
- <skip />
+ <string name="inline_turn_off_notifications" msgid="2653064779176881329">"Անջատել"</string>
<string name="notification_silence_title" msgid="8608090968400832335">"Անձայն"</string>
<string name="notification_alert_title" msgid="3656229781017543655">"Կանխադրված"</string>
<string name="notification_automatic_title" msgid="3745465364578762652">"Ավտոմատ"</string>
@@ -977,9 +983,13 @@
<string name="right_icon" msgid="1103955040645237425">"Աջ պատկերակ"</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"Պահեք և քաշեք՝ սալիկներ ավելացնելու համար"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"Պահեք և քաշեք՝ սալիկները վերադասավորելու համար"</string>
+ <!-- no translation found for tap_to_position_tile (6282815817773342757) -->
+ <skip />
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"Քաշեք այստեղ՝ հեռացնելու համար"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"Հարկավոր է առնվազն <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> սալիկ"</string>
<string name="qs_edit" msgid="5583565172803472437">"Փոփոխել"</string>
+ <!-- no translation found for qs_edit_tiles (2105215324060865035) -->
+ <skip />
<string name="tuner_time" msgid="2450785840990529997">"Ժամ"</string>
<string-array name="clock_options">
<item msgid="3986445361435142273">"Ցույց տալ ժամերը, րոպեները և վայրկյանները"</item>
@@ -995,6 +1005,10 @@
<string name="other" msgid="429768510980739978">"Այլ"</string>
<string name="accessibility_qs_edit_toggle_tile_size_action" msgid="1485194410119733586">"փոխեք սալիկի չափը"</string>
<string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"հեռացնել սալիկը"</string>
+ <!-- no translation found for accessibility_qs_edit_toggle_placement_mode (3870429389210569610) -->
+ <skip />
+ <!-- no translation found for accessibility_qs_edit_toggle_selection (2201248304072372239) -->
+ <skip />
<string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"ավելացնել սալիկը վերջին դիրքում"</string>
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Տեղափոխել սալիկը"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"Ավելացնել սալիկը նախընտրած դիրքում"</string>
@@ -1570,5 +1584,7 @@
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Անհայտ"</string>
<string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"Զրոյացնե՞լ բոլոր սալիկները"</string>
<string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Արագ կարգավորումների բոլոր սալիկները կզրոյացվեն սարքի սկզբնական կարգավորումների համաձայն։"</string>
+ <!-- no translation found for demote_explain_text (1600426458580544250) -->
+ <skip />
<string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
</resources>
diff --git a/packages/SystemUI/res/values-hy/tiles_states_strings.xml b/packages/SystemUI/res/values-hy/tiles_states_strings.xml
index 3497c404600b..c1b2d71aed3b 100644
--- a/packages/SystemUI/res/values-hy/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-hy/tiles_states_strings.xml
@@ -56,9 +56,11 @@
<item msgid="5376619709702103243">"Անջատված է"</item>
<item msgid="4875147066469902392">"Միացված է"</item>
</string-array>
- <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) -->
- <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) -->
- <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) -->
+ <string-array name="tile_states_modes_dnd">
+ <item msgid="6509540227356524582">"Հասանելի չէ"</item>
+ <item msgid="8589336868985358191">"Անջատված է"</item>
+ <item msgid="726072717827778234">"Միացված է"</item>
+ </string-array>
<string-array name="tile_states_flashlight">
<item msgid="3465257127433353857">"Հասանելի չէ"</item>
<item msgid="5044688398303285224">"Անջատված է"</item>
diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml
index 5867bf7c5cc6..a117599c08cc 100644
--- a/packages/SystemUI/res/values-in/strings.xml
+++ b/packages/SystemUI/res/values-in/strings.xml
@@ -419,6 +419,11 @@
<string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Klik untuk menyambungkan perangkat baru"</string>
<string name="hearing_devices_presets_error" msgid="350363093458408536">"Tidak dapat memperbarui preset"</string>
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"Preset"</string>
+ <string name="hearing_devices_input_routing_label" msgid="730396728151232306">"Mikrofon default untuk panggilan"</string>
+ <string-array name="hearing_device_input_routing_options">
+ <item msgid="4582190415045337003">"Mikrofon alat bantu dengar"</item>
+ <item msgid="8501466270452446450">"Mikrofon ponsel ini"</item>
+ </string-array>
<string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"Dipilih"</string>
<string name="hearing_devices_ambient_label" msgid="629440938614895797">"Suara sekitar"</string>
<string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Kiri"</string>
@@ -582,6 +587,8 @@
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Jika Anda membagikan aplikasi, semua hal yang ditampilkan atau diputar di aplikasi tersebut akan terlihat oleh <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Jadi, berhati-hatilah saat memasukkan sandi, detail pembayaran, pesan, foto, audio, dan video."</string>
<string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Bagikan layar"</string>
<string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g> telah menonaktifkan opsi ini"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_not_supported (4860247304058870233) -->
+ <skip />
<string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"Pilih aplikasi yang akan dibagikan"</string>
<string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Transmisikan layar?"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Transmisikan satu aplikasi"</string>
@@ -794,8 +801,7 @@
<string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Aktif - Berbasis deteksi wajah"</string>
<string name="inline_done_button" msgid="6043094985588909584">"Selesai"</string>
<string name="inline_ok_button" msgid="603075490581280343">"Terapkan"</string>
- <!-- no translation found for inline_turn_off_notifications (2653064779176881329) -->
- <skip />
+ <string name="inline_turn_off_notifications" msgid="2653064779176881329">"Nonaktifkan"</string>
<string name="notification_silence_title" msgid="8608090968400832335">"Senyap"</string>
<string name="notification_alert_title" msgid="3656229781017543655">"Default"</string>
<string name="notification_automatic_title" msgid="3745465364578762652">"Otomatis"</string>
@@ -977,9 +983,13 @@
<string name="right_icon" msgid="1103955040645237425">"Ikon kanan"</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"Tahan dan tarik untuk menambahkan kartu"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"Tahan dan tarik untuk menata ulang kartu"</string>
+ <!-- no translation found for tap_to_position_tile (6282815817773342757) -->
+ <skip />
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"Tarik ke sini untuk menghapus"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"Anda membutuhkan setidaknya <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> kartu"</string>
<string name="qs_edit" msgid="5583565172803472437">"Edit"</string>
+ <!-- no translation found for qs_edit_tiles (2105215324060865035) -->
+ <skip />
<string name="tuner_time" msgid="2450785840990529997">"Waktu"</string>
<string-array name="clock_options">
<item msgid="3986445361435142273">"Tampilkan jam, menit, dan detik"</item>
@@ -995,6 +1005,10 @@
<string name="other" msgid="429768510980739978">"Lainnya"</string>
<string name="accessibility_qs_edit_toggle_tile_size_action" msgid="1485194410119733586">"mengubah ukuran kartu"</string>
<string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"menghapus kartu"</string>
+ <!-- no translation found for accessibility_qs_edit_toggle_placement_mode (3870429389210569610) -->
+ <skip />
+ <!-- no translation found for accessibility_qs_edit_toggle_selection (2201248304072372239) -->
+ <skip />
<string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"menambahkan kartu ke posisi terakhir"</string>
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Pindahkan kartu"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"Tambahkan kartu ke posisi yang diinginkan"</string>
@@ -1570,5 +1584,7 @@
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Tidak diketahui"</string>
<string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"Reset semua kartu?"</string>
<string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Semua kartu Setelan Cepat akan direset ke setelan asli perangkat"</string>
+ <!-- no translation found for demote_explain_text (1600426458580544250) -->
+ <skip />
<string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
</resources>
diff --git a/packages/SystemUI/res/values-in/tiles_states_strings.xml b/packages/SystemUI/res/values-in/tiles_states_strings.xml
index 7df0b0d9f34c..a00d87d8bfd7 100644
--- a/packages/SystemUI/res/values-in/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-in/tiles_states_strings.xml
@@ -56,9 +56,11 @@
<item msgid="5376619709702103243">"Nonaktif"</item>
<item msgid="4875147066469902392">"Aktif"</item>
</string-array>
- <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) -->
- <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) -->
- <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) -->
+ <string-array name="tile_states_modes_dnd">
+ <item msgid="6509540227356524582">"Tidak tersedia"</item>
+ <item msgid="8589336868985358191">"Nonaktif"</item>
+ <item msgid="726072717827778234">"Aktif"</item>
+ </string-array>
<string-array name="tile_states_flashlight">
<item msgid="3465257127433353857">"Tidak tersedia"</item>
<item msgid="5044688398303285224">"Mati"</item>
diff --git a/packages/SystemUI/res/values-is/strings.xml b/packages/SystemUI/res/values-is/strings.xml
index f949400bf09d..bd8c7d1894f6 100644
--- a/packages/SystemUI/res/values-is/strings.xml
+++ b/packages/SystemUI/res/values-is/strings.xml
@@ -419,6 +419,11 @@
<string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Smelltu til að para nýtt tæki"</string>
<string name="hearing_devices_presets_error" msgid="350363093458408536">"Tókst ekki að uppfæra forstillingu"</string>
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"Forstilling"</string>
+ <string name="hearing_devices_input_routing_label" msgid="730396728151232306">"Sjálfgefinn hljóðnemi fyrir símtöl"</string>
+ <string-array name="hearing_device_input_routing_options">
+ <item msgid="4582190415045337003">"Hljóðnemi heyrnartækis"</item>
+ <item msgid="8501466270452446450">"Hljóðnemi þessa síma"</item>
+ </string-array>
<string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"Valið"</string>
<string name="hearing_devices_ambient_label" msgid="629440938614895797">"Umhverfi"</string>
<string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Vinstri"</string>
@@ -582,6 +587,8 @@
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Þegar þú deilir forriti er allt sem sést eða er spilað í því forriti sýnilegt <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Passaðu því upp á aðgangsorð, greiðsluupplýsingar, skilaboð, myndir, hljóð og myndskeið."</string>
<string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Deila skjá"</string>
<string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g> slökkti á þessum valkosti"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_not_supported (4860247304058870233) -->
+ <skip />
<string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"Velja forrit til að deila"</string>
<string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Varpa skjánum?"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Varpa einu forriti"</string>
@@ -794,8 +801,7 @@
<string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Kveikt – út frá andliti"</string>
<string name="inline_done_button" msgid="6043094985588909584">"Lokið"</string>
<string name="inline_ok_button" msgid="603075490581280343">"Nota"</string>
- <!-- no translation found for inline_turn_off_notifications (2653064779176881329) -->
- <skip />
+ <string name="inline_turn_off_notifications" msgid="2653064779176881329">"Slökkva"</string>
<string name="notification_silence_title" msgid="8608090968400832335">"Hljóðlaust"</string>
<string name="notification_alert_title" msgid="3656229781017543655">"Sjálfgefið"</string>
<string name="notification_automatic_title" msgid="3745465364578762652">"Sjálfvirk"</string>
@@ -977,9 +983,13 @@
<string name="right_icon" msgid="1103955040645237425">"Tákn til hægri"</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"Haltu inni og dragðu til að bæta við flísum"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"Haltu og dragðu til að endurraða flísum"</string>
+ <!-- no translation found for tap_to_position_tile (6282815817773342757) -->
+ <skip />
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"Dragðu hingað til að fjarlægja"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"Flísarnar mega ekki vera færri en <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g>"</string>
<string name="qs_edit" msgid="5583565172803472437">"Breyta"</string>
+ <!-- no translation found for qs_edit_tiles (2105215324060865035) -->
+ <skip />
<string name="tuner_time" msgid="2450785840990529997">"Tími"</string>
<string-array name="clock_options">
<item msgid="3986445361435142273">"Sýna klukkustundir, mínútur og sekúndur"</item>
@@ -995,6 +1005,10 @@
<string name="other" msgid="429768510980739978">"Annað"</string>
<string name="accessibility_qs_edit_toggle_tile_size_action" msgid="1485194410119733586">"breyta stærð reitsins"</string>
<string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"fjarlægja flís"</string>
+ <!-- no translation found for accessibility_qs_edit_toggle_placement_mode (3870429389210569610) -->
+ <skip />
+ <!-- no translation found for accessibility_qs_edit_toggle_selection (2201248304072372239) -->
+ <skip />
<string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"bæta reit við síðustu stöðu"</string>
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Færa flís"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"Bæta reit við óskaða stöðu"</string>
@@ -1570,5 +1584,7 @@
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Óþekkt"</string>
<string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"Endurstilla alla reiti?"</string>
<string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Allir flýtistillingareitir munu endurstillast á upprunalegar stillingar tækisins"</string>
+ <!-- no translation found for demote_explain_text (1600426458580544250) -->
+ <skip />
<string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
</resources>
diff --git a/packages/SystemUI/res/values-is/tiles_states_strings.xml b/packages/SystemUI/res/values-is/tiles_states_strings.xml
index d1b04e05ad7f..7790b11b5fa4 100644
--- a/packages/SystemUI/res/values-is/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-is/tiles_states_strings.xml
@@ -56,9 +56,11 @@
<item msgid="5376619709702103243">"Slökkt"</item>
<item msgid="4875147066469902392">"Kveikt"</item>
</string-array>
- <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) -->
- <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) -->
- <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) -->
+ <string-array name="tile_states_modes_dnd">
+ <item msgid="6509540227356524582">"Ekki í boði"</item>
+ <item msgid="8589336868985358191">"Slökkt"</item>
+ <item msgid="726072717827778234">"Kveikt"</item>
+ </string-array>
<string-array name="tile_states_flashlight">
<item msgid="3465257127433353857">"Ekki í boði"</item>
<item msgid="5044688398303285224">"Slökkt"</item>
diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml
index 708fa78bedff..7d170a9a1980 100644
--- a/packages/SystemUI/res/values-it/strings.xml
+++ b/packages/SystemUI/res/values-it/strings.xml
@@ -419,6 +419,11 @@
<string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Fai clic per accoppiare un nuovo dispositivo"</string>
<string name="hearing_devices_presets_error" msgid="350363093458408536">"Impossibile aggiornare preset"</string>
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"Preset"</string>
+ <string name="hearing_devices_input_routing_label" msgid="730396728151232306">"Microfono predefinito per le chiamate"</string>
+ <string-array name="hearing_device_input_routing_options">
+ <item msgid="4582190415045337003">"Microfono dell\'apparecchio acustico"</item>
+ <item msgid="8501466270452446450">"Microfono di questo smartphone"</item>
+ </string-array>
<string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"Selezionato"</string>
<string name="hearing_devices_ambient_label" msgid="629440938614895797">"Audio ambientale"</string>
<string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Sinistra"</string>
@@ -582,6 +587,7 @@
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Quando condividi un\'app, tutto ciò che viene mostrato o riprodotto al suo interno è visibile a <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Presta quindi attenzione a password, dati di pagamento, messaggi, foto, audio e video."</string>
<string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Condividi schermo"</string>
<string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g> ha disattivato questa opzione"</string>
+ <string name="media_projection_entry_app_permission_dialog_single_app_not_supported" msgid="4860247304058870233">"Non supportata dall\'app"</string>
<string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"Scegli l\'app da condividere"</string>
<string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Trasmettere lo schermo?"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Trasmetti un\'app"</string>
@@ -794,8 +800,7 @@
<string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"On - Rotazione basata sul viso"</string>
<string name="inline_done_button" msgid="6043094985588909584">"Fine"</string>
<string name="inline_ok_button" msgid="603075490581280343">"Applica"</string>
- <!-- no translation found for inline_turn_off_notifications (2653064779176881329) -->
- <skip />
+ <string name="inline_turn_off_notifications" msgid="2653064779176881329">"Disattiva"</string>
<string name="notification_silence_title" msgid="8608090968400832335">"Modalità silenziosa"</string>
<string name="notification_alert_title" msgid="3656229781017543655">"Modalità predefinita"</string>
<string name="notification_automatic_title" msgid="3745465364578762652">"Automatico"</string>
@@ -977,9 +982,13 @@
<string name="right_icon" msgid="1103955040645237425">"Icona destra"</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"Tieni premuto e trascina per aggiungere riquadri"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"Tieni premuto e trascina per riordinare i riquadri"</string>
+ <!-- no translation found for tap_to_position_tile (6282815817773342757) -->
+ <skip />
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"Trascina qui per rimuovere"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"Occorrono almeno <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> schede"</string>
<string name="qs_edit" msgid="5583565172803472437">"Modifica"</string>
+ <!-- no translation found for qs_edit_tiles (2105215324060865035) -->
+ <skip />
<string name="tuner_time" msgid="2450785840990529997">"Ora"</string>
<string-array name="clock_options">
<item msgid="3986445361435142273">"Mostra ore, minuti e secondi"</item>
@@ -995,6 +1004,10 @@
<string name="other" msgid="429768510980739978">"Altro"</string>
<string name="accessibility_qs_edit_toggle_tile_size_action" msgid="1485194410119733586">"attivare/disattivare le dimensioni del riquadro"</string>
<string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"rimuovere il riquadro"</string>
+ <!-- no translation found for accessibility_qs_edit_toggle_placement_mode (3870429389210569610) -->
+ <skip />
+ <!-- no translation found for accessibility_qs_edit_toggle_selection (2201248304072372239) -->
+ <skip />
<string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"aggiungere il riquadro all\'ultima posizione"</string>
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Sposta riquadro"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"Aggiungi il riquadro alla posizione desiderata"</string>
@@ -1570,5 +1583,7 @@
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Sconosciuti"</string>
<string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"Reimpostare tutti i riquadri?"</string>
<string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Tutti i riquadri Impostazioni rapide verranno reimpostati sulle impostazioni originali del dispositivo"</string>
+ <!-- no translation found for demote_explain_text (1600426458580544250) -->
+ <skip />
<string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
</resources>
diff --git a/packages/SystemUI/res/values-it/tiles_states_strings.xml b/packages/SystemUI/res/values-it/tiles_states_strings.xml
index afbd3d9b1910..2d3f55361307 100644
--- a/packages/SystemUI/res/values-it/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-it/tiles_states_strings.xml
@@ -56,9 +56,11 @@
<item msgid="5376619709702103243">"Off"</item>
<item msgid="4875147066469902392">"On"</item>
</string-array>
- <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) -->
- <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) -->
- <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) -->
+ <string-array name="tile_states_modes_dnd">
+ <item msgid="6509540227356524582">"Non disponibile"</item>
+ <item msgid="8589336868985358191">"Off"</item>
+ <item msgid="726072717827778234">"On"</item>
+ </string-array>
<string-array name="tile_states_flashlight">
<item msgid="3465257127433353857">"Non disponibile"</item>
<item msgid="5044688398303285224">"Off"</item>
diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml
index 35500fbc4107..cf6d80acdeae 100644
--- a/packages/SystemUI/res/values-iw/strings.xml
+++ b/packages/SystemUI/res/values-iw/strings.xml
@@ -419,6 +419,10 @@
<string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"צריך ללחוץ כדי להתאים מכשיר חדש"</string>
<string name="hearing_devices_presets_error" msgid="350363093458408536">"לא ניתן לעדכן את ההגדרה הקבועה מראש"</string>
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"הגדרה קבועה מראש"</string>
+ <!-- no translation found for hearing_devices_input_routing_label (730396728151232306) -->
+ <skip />
+ <!-- no translation found for hearing_device_input_routing_options:0 (4582190415045337003) -->
+ <!-- no translation found for hearing_device_input_routing_options:1 (8501466270452446450) -->
<string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"נבחר"</string>
<string name="hearing_devices_ambient_label" msgid="629440938614895797">"הרעשים בסביבה"</string>
<string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"שמאל"</string>
@@ -582,6 +586,8 @@
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"כשמשתפים אפליקציה, כל מה שרואים או מפעילים בה מופיע גם ב-<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. מומלץ להיזהר ולא לחשוף פרטים אישיים כמו סיסמאות, פרטי תשלום, הודעות, תמונות, אודיו וסרטונים."</string>
<string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"שיתוף המסך"</string>
<string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"האפליקציה <xliff:g id="APP_NAME">%1$s</xliff:g> השביתה את האפשרות הזו"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_not_supported (4860247304058870233) -->
+ <skip />
<string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"בחירת אפליקציה לשיתוף"</string>
<string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"‏להפעיל Cast של המסך?"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"‏הפעלת Cast של אפליקציה אחת"</string>
@@ -794,8 +800,7 @@
<string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"פועל – מבוסס על זיהוי פנים"</string>
<string name="inline_done_button" msgid="6043094985588909584">"סיום"</string>
<string name="inline_ok_button" msgid="603075490581280343">"אישור"</string>
- <!-- no translation found for inline_turn_off_notifications (2653064779176881329) -->
- <skip />
+ <string name="inline_turn_off_notifications" msgid="2653064779176881329">"השבתה"</string>
<string name="notification_silence_title" msgid="8608090968400832335">"שקט"</string>
<string name="notification_alert_title" msgid="3656229781017543655">"ברירת מחדל"</string>
<string name="notification_automatic_title" msgid="3745465364578762652">"באופן אוטומטי"</string>
@@ -977,9 +982,13 @@
<string name="right_icon" msgid="1103955040645237425">"סמל ימני"</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"יש ללחוץ ולגרור כדי להוסיף לחצנים"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"יש ללחוץ ולגרור כדי לסדר מחדש את כרטיסי המידע"</string>
+ <!-- no translation found for tap_to_position_tile (6282815817773342757) -->
+ <skip />
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"אפשר לגרור לכאן כדי להסיר"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"יש צורך ב-<xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> אריחים לפחות"</string>
<string name="qs_edit" msgid="5583565172803472437">"עריכה"</string>
+ <!-- no translation found for qs_edit_tiles (2105215324060865035) -->
+ <skip />
<string name="tuner_time" msgid="2450785840990529997">"שעה"</string>
<string-array name="clock_options">
<item msgid="3986445361435142273">"הצגת שעות, דקות ושניות"</item>
@@ -995,6 +1004,10 @@
<string name="other" msgid="429768510980739978">"אחר"</string>
<string name="accessibility_qs_edit_toggle_tile_size_action" msgid="1485194410119733586">"שינוי גודל הלחצן"</string>
<string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"הסרת הלחצן"</string>
+ <!-- no translation found for accessibility_qs_edit_toggle_placement_mode (3870429389210569610) -->
+ <skip />
+ <!-- no translation found for accessibility_qs_edit_toggle_selection (2201248304072372239) -->
+ <skip />
<string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"הוספת הלחצן במיקום האחרון"</string>
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"העברת הלחצן"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"הוספת הלחצן במיקום הרצוי"</string>
@@ -1570,5 +1583,7 @@
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"לא ידוע"</string>
<string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"לאפס את כל הלחצנים?"</string>
<string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"כל הלחצנים ב\'הגדרות מהירות\' יאופסו להגדרות המקוריות של המכשיר"</string>
+ <!-- no translation found for demote_explain_text (1600426458580544250) -->
+ <skip />
<string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"‫<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
</resources>
diff --git a/packages/SystemUI/res/values-iw/tiles_states_strings.xml b/packages/SystemUI/res/values-iw/tiles_states_strings.xml
index 42aa531186f6..4ff8fe4e6f80 100644
--- a/packages/SystemUI/res/values-iw/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-iw/tiles_states_strings.xml
@@ -56,9 +56,11 @@
<item msgid="5376619709702103243">"כבוי"</item>
<item msgid="4875147066469902392">"פועל"</item>
</string-array>
- <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) -->
- <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) -->
- <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) -->
+ <string-array name="tile_states_modes_dnd">
+ <item msgid="6509540227356524582">"לא זמין"</item>
+ <item msgid="8589336868985358191">"מושבת"</item>
+ <item msgid="726072717827778234">"מופעל"</item>
+ </string-array>
<string-array name="tile_states_flashlight">
<item msgid="3465257127433353857">"לא זמין"</item>
<item msgid="5044688398303285224">"כבוי"</item>
diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml
index fc792b6f9850..ca4dfc88b6ba 100644
--- a/packages/SystemUI/res/values-ja/strings.xml
+++ b/packages/SystemUI/res/values-ja/strings.xml
@@ -419,6 +419,11 @@
<string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"クリックすると、新しいデバイスをペア設定できます"</string>
<string name="hearing_devices_presets_error" msgid="350363093458408536">"プリセットを更新できませんでした"</string>
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"プリセット"</string>
+ <string name="hearing_devices_input_routing_label" msgid="730396728151232306">"通話に使うデフォルトのマイク"</string>
+ <string-array name="hearing_device_input_routing_options">
+ <item msgid="4582190415045337003">"補聴器のマイク"</item>
+ <item msgid="8501466270452446450">"このスマートフォンのマイク"</item>
+ </string-array>
<string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"選択中"</string>
<string name="hearing_devices_ambient_label" msgid="629440938614895797">"周囲の音"</string>
<string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"左"</string>
@@ -582,6 +587,7 @@
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"アプリを共有すると、そのアプリで表示または再生される内容が <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> にすべて公開されます。パスワード、お支払い情報、メッセージ、写真、音声、動画などの情報にご注意ください。"</string>
<string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"画面を共有"</string>
<string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g> がこのオプションを無効にしています"</string>
+ <string name="media_projection_entry_app_permission_dialog_single_app_not_supported" msgid="4860247304058870233">"このアプリではサポートされていません"</string>
<string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"共有するアプリを選択"</string>
<string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"画面をキャストしますか?"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"1 つのアプリをキャスト"</string>
@@ -794,8 +800,7 @@
<string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"ON - 顔ベース"</string>
<string name="inline_done_button" msgid="6043094985588909584">"完了"</string>
<string name="inline_ok_button" msgid="603075490581280343">"適用"</string>
- <!-- no translation found for inline_turn_off_notifications (2653064779176881329) -->
- <skip />
+ <string name="inline_turn_off_notifications" msgid="2653064779176881329">"OFF にする"</string>
<string name="notification_silence_title" msgid="8608090968400832335">"サイレント"</string>
<string name="notification_alert_title" msgid="3656229781017543655">"デフォルト"</string>
<string name="notification_automatic_title" msgid="3745465364578762652">"自動"</string>
@@ -977,9 +982,13 @@
<string name="right_icon" msgid="1103955040645237425">"右アイコン"</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"タイルを追加するには長押ししてドラッグ"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"タイルを並べ替えるには長押ししてドラッグ"</string>
+ <!-- no translation found for tap_to_position_tile (6282815817773342757) -->
+ <skip />
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"削除するにはここにドラッグ"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"タイルは <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> 個以上必要です"</string>
<string name="qs_edit" msgid="5583565172803472437">"編集"</string>
+ <!-- no translation found for qs_edit_tiles (2105215324060865035) -->
+ <skip />
<string name="tuner_time" msgid="2450785840990529997">"時間"</string>
<string-array name="clock_options">
<item msgid="3986445361435142273">"時間、分、秒を表示"</item>
@@ -995,6 +1004,10 @@
<string name="other" msgid="429768510980739978">"その他"</string>
<string name="accessibility_qs_edit_toggle_tile_size_action" msgid="1485194410119733586">"タイルのサイズを切り替えます"</string>
<string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"タイルを削除"</string>
+ <!-- no translation found for accessibility_qs_edit_toggle_placement_mode (3870429389210569610) -->
+ <skip />
+ <!-- no translation found for accessibility_qs_edit_toggle_selection (2201248304072372239) -->
+ <skip />
<string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"タイルを最後の位置に追加する"</string>
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"タイルを移動"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"タイルを目的の位置に追加する"</string>
@@ -1570,5 +1583,7 @@
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"不明"</string>
<string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"すべてのタイルをリセットしますか?"</string>
<string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"すべてのクイック設定タイルがデバイスの元の設定にリセットされます"</string>
+ <!-- no translation found for demote_explain_text (1600426458580544250) -->
+ <skip />
<string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>、<xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ja/tiles_states_strings.xml b/packages/SystemUI/res/values-ja/tiles_states_strings.xml
index 4827ad38dfa7..4a6b210797f9 100644
--- a/packages/SystemUI/res/values-ja/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-ja/tiles_states_strings.xml
@@ -56,9 +56,11 @@
<item msgid="5376619709702103243">"OFF"</item>
<item msgid="4875147066469902392">"ON"</item>
</string-array>
- <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) -->
- <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) -->
- <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) -->
+ <string-array name="tile_states_modes_dnd">
+ <item msgid="6509540227356524582">"使用不可"</item>
+ <item msgid="8589336868985358191">"OFF"</item>
+ <item msgid="726072717827778234">"ON"</item>
+ </string-array>
<string-array name="tile_states_flashlight">
<item msgid="3465257127433353857">"使用不可"</item>
<item msgid="5044688398303285224">"OFF"</item>
diff --git a/packages/SystemUI/res/values-ka/strings.xml b/packages/SystemUI/res/values-ka/strings.xml
index e13a1b3a3bd8..42b9a3c7e605 100644
--- a/packages/SystemUI/res/values-ka/strings.xml
+++ b/packages/SystemUI/res/values-ka/strings.xml
@@ -419,6 +419,11 @@
<string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"დააწკაპუნეთ ახალი მოწყობილობის დასაწყვილებლად"</string>
<string name="hearing_devices_presets_error" msgid="350363093458408536">"წინასწარ დაყენებული პარამეტრების განახლება ვერ მოხერხდა"</string>
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"წინასწარ დაყენებული"</string>
+ <string name="hearing_devices_input_routing_label" msgid="730396728151232306">"ნაგულისხმევი მიკროფონი ზარებისთვის"</string>
+ <string-array name="hearing_device_input_routing_options">
+ <item msgid="4582190415045337003">"სმენის მოწყობილობის მიკროფონი"</item>
+ <item msgid="8501466270452446450">"ამ ტელეფონის მიკროფონი"</item>
+ </string-array>
<string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"არჩეულია"</string>
<string name="hearing_devices_ambient_label" msgid="629440938614895797">"გარემოცვა"</string>
<string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"მარცხენა"</string>
@@ -582,6 +587,7 @@
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"აპის გაზიარებისას <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ხედავს ყველაფერს, რაც ჩანს ან უკრავს ამ აპში. ამიტომ სიფრთხილე გამოიჩინეთ ისეთ ინფორმაციასთან, როგორიცაა პაროლები, გადახდის დეტალები, შეტყობინებები, ფოტოები, აუდიო და ვიდეო."</string>
<string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"ეკრანის გაზიარება"</string>
<string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g>-მა გათიშა ეს ვარიანტი"</string>
+ <string name="media_projection_entry_app_permission_dialog_single_app_not_supported" msgid="4860247304058870233">"არ არის მხარდაჭერილი აპის მიერ"</string>
<string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"გაზიარებისთვის აპის არჩევა"</string>
<string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"გსურთ თქვენი ეკრანის ტრანსლირება?"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"ერთი აპის ტრანსლირება"</string>
@@ -794,8 +800,7 @@
<string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"ჩართული — სახის მიხედვით"</string>
<string name="inline_done_button" msgid="6043094985588909584">"მზადაა"</string>
<string name="inline_ok_button" msgid="603075490581280343">"მისადაგება"</string>
- <!-- no translation found for inline_turn_off_notifications (2653064779176881329) -->
- <skip />
+ <string name="inline_turn_off_notifications" msgid="2653064779176881329">"გამორთვა"</string>
<string name="notification_silence_title" msgid="8608090968400832335">"ჩუმი"</string>
<string name="notification_alert_title" msgid="3656229781017543655">"ნაგულისხმევი"</string>
<string name="notification_automatic_title" msgid="3745465364578762652">"ავტომატური"</string>
@@ -977,9 +982,13 @@
<string name="right_icon" msgid="1103955040645237425">"მარჯვენა ხატულა"</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"ჩავლებით გადაიტანეთ ბლოკების დასამატებლად"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"ფილების გადაწყობა შეგიძლიათ მათი ჩავლებით გადატანით"</string>
+ <!-- no translation found for tap_to_position_tile (6282815817773342757) -->
+ <skip />
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"ამოსაშლელად, ჩავლებით გადმოიტანეთ აქ"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"თქვენ გჭირდებათ მოზაიკის <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> ფილა მაინც"</string>
<string name="qs_edit" msgid="5583565172803472437">"რედაქტირება"</string>
+ <!-- no translation found for qs_edit_tiles (2105215324060865035) -->
+ <skip />
<string name="tuner_time" msgid="2450785840990529997">"დრო"</string>
<string-array name="clock_options">
<item msgid="3986445361435142273">"საათების, წუთებისა და წამების ჩვენება"</item>
@@ -995,6 +1004,10 @@
<string name="other" msgid="429768510980739978">"სხვა"</string>
<string name="accessibility_qs_edit_toggle_tile_size_action" msgid="1485194410119733586">"მოზაიკის ფილის ზომის გადასართავად"</string>
<string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"მოზაიკის ფილის წაშლა"</string>
+ <!-- no translation found for accessibility_qs_edit_toggle_placement_mode (3870429389210569610) -->
+ <skip />
+ <!-- no translation found for accessibility_qs_edit_toggle_selection (2201248304072372239) -->
+ <skip />
<string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"მოზაიკის ფილის ბოლო პოზიციაზე დამატება"</string>
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"მოზაიკის გადატანა"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"მოზაიკის ფილის სასურველ პოზიციაზე დამატება"</string>
@@ -1249,7 +1262,7 @@
<string name="media_output_dialog_accessibility_title" msgid="4681741064190167888">"ხელმისაწვდომი მოწყობილობები გამომავალი აუდიოსთვის."</string>
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"ხმა"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
- <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"დინამიკები და დისპლეები"</string>
+ <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"დინამიკები და ეკრანები"</string>
<string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"შემოთავაზებული მოწყობილობები"</string>
<string name="media_input_group_title" msgid="2057057473860783021">"შემავალი"</string>
<string name="media_output_group_title" msgid="6789001895863332576">"გამომავალი"</string>
@@ -1570,5 +1583,7 @@
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"უცნობი"</string>
<string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"გსურთ ყველა ფილის გადაყენება?"</string>
<string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"სწრაფი პარამეტრების ყველა ფილა გადაყენდება მოწყობილობის ორიგინალ პარამეტრებზე"</string>
+ <!-- no translation found for demote_explain_text (1600426458580544250) -->
+ <skip />
<string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ka/tiles_states_strings.xml b/packages/SystemUI/res/values-ka/tiles_states_strings.xml
index ebf28c89587b..6440b5f74a94 100644
--- a/packages/SystemUI/res/values-ka/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-ka/tiles_states_strings.xml
@@ -56,9 +56,11 @@
<item msgid="5376619709702103243">"გამორთულია"</item>
<item msgid="4875147066469902392">"ჩართულია"</item>
</string-array>
- <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) -->
- <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) -->
- <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) -->
+ <string-array name="tile_states_modes_dnd">
+ <item msgid="6509540227356524582">"მიუწვდომელი"</item>
+ <item msgid="8589336868985358191">"გამორთული"</item>
+ <item msgid="726072717827778234">"ჩართული"</item>
+ </string-array>
<string-array name="tile_states_flashlight">
<item msgid="3465257127433353857">"მიუწვდომელია"</item>
<item msgid="5044688398303285224">"გამორთულია"</item>
diff --git a/packages/SystemUI/res/values-kk/strings.xml b/packages/SystemUI/res/values-kk/strings.xml
index 5fc8b47869b7..42d2874a10c7 100644
--- a/packages/SystemUI/res/values-kk/strings.xml
+++ b/packages/SystemUI/res/values-kk/strings.xml
@@ -419,6 +419,11 @@
<string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Жаңа құрылғыны жұптау үшін басыңыз."</string>
<string name="hearing_devices_presets_error" msgid="350363093458408536">"Параметрлер жинағын жаңарту мүмкін болмады."</string>
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"Параметрлер жинағы"</string>
+ <string name="hearing_devices_input_routing_label" msgid="730396728151232306">"Қоңырауларға арналған әдепкі микрофон"</string>
+ <string-array name="hearing_device_input_routing_options">
+ <item msgid="4582190415045337003">"Есту аппаратының микрофоны"</item>
+ <item msgid="8501466270452446450">"Осы телефонның микрофоны"</item>
+ </string-array>
<string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"Таңдалды"</string>
<string name="hearing_devices_ambient_label" msgid="629440938614895797">"Айнала"</string>
<string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Сол жақ"</string>
@@ -582,6 +587,8 @@
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Қолданбаны бөліскен кезде, онда көрінетін не ойнатылатын барлық контент <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> қолданбасында көрсетіледі. Сондықтан құпия сөздерді, төлем туралы мәліметті, хабарларды, фотосуреттерді және аудио мен бейнені ашқанда сақ болыңыз."</string>
<string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Экранды бөлісу"</string>
<string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g> қолданбасы осы опцияны өшірді."</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_not_supported (4860247304058870233) -->
+ <skip />
<string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"Бөлісетін қолданба экранын таңдау"</string>
<string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Экранды трансляциялау керек пе?"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Бір қолданба экранын трансляциялау"</string>
@@ -794,8 +801,7 @@
<string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Қосулы – бет негізінде"</string>
<string name="inline_done_button" msgid="6043094985588909584">"Дайын"</string>
<string name="inline_ok_button" msgid="603075490581280343">"Қолдану"</string>
- <!-- no translation found for inline_turn_off_notifications (2653064779176881329) -->
- <skip />
+ <string name="inline_turn_off_notifications" msgid="2653064779176881329">"Өшіру"</string>
<string name="notification_silence_title" msgid="8608090968400832335">"Үнсіз"</string>
<string name="notification_alert_title" msgid="3656229781017543655">"Әдепкі"</string>
<string name="notification_automatic_title" msgid="3745465364578762652">"Автоматты"</string>
@@ -977,9 +983,13 @@
<string name="right_icon" msgid="1103955040645237425">"Оң жақ белгіше"</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"Қажетті элементтерді сүйреп әкеліп қойыңыз"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"Элементтердің ретін өзгерту үшін оларды басып тұрып сүйреңіз"</string>
+ <!-- no translation found for tap_to_position_tile (6282815817773342757) -->
+ <skip />
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"Керексіздерін осы жерге сүйреңіз"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"Кемінде <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> бөлшек қажет."</string>
<string name="qs_edit" msgid="5583565172803472437">"Өзгерту"</string>
+ <!-- no translation found for qs_edit_tiles (2105215324060865035) -->
+ <skip />
<string name="tuner_time" msgid="2450785840990529997">"Уақыт"</string>
<string-array name="clock_options">
<item msgid="3986445361435142273">"Сағаттарды, минуттарды және секундтарды көрсету"</item>
@@ -995,6 +1005,10 @@
<string name="other" msgid="429768510980739978">"Басқа"</string>
<string name="accessibility_qs_edit_toggle_tile_size_action" msgid="1485194410119733586">"бөлшек өлшемін ауыстыру"</string>
<string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"бөлшекті өшіру"</string>
+ <!-- no translation found for accessibility_qs_edit_toggle_placement_mode (3870429389210569610) -->
+ <skip />
+ <!-- no translation found for accessibility_qs_edit_toggle_selection (2201248304072372239) -->
+ <skip />
<string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"соңғы позицияға бөлшек қосу"</string>
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Бөлшекті жылжыту"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"Қалаған позицияға бөлшек қосу"</string>
@@ -1570,5 +1584,7 @@
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Белгісіз"</string>
<string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"Барлық бөлшекті бастапқы күйге қайтару керек пе?"</string>
<string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Барлық \"Жылдам параметрлер\" бөлшегі құрылғының бастапқы параметрлеріне қайтарылады."</string>
+ <!-- no translation found for demote_explain_text (1600426458580544250) -->
+ <skip />
<string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
</resources>
diff --git a/packages/SystemUI/res/values-kk/tiles_states_strings.xml b/packages/SystemUI/res/values-kk/tiles_states_strings.xml
index 45316aa1391e..a177ca2862da 100644
--- a/packages/SystemUI/res/values-kk/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-kk/tiles_states_strings.xml
@@ -56,9 +56,11 @@
<item msgid="5376619709702103243">"Өшірулі"</item>
<item msgid="4875147066469902392">"Қосулы"</item>
</string-array>
- <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) -->
- <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) -->
- <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) -->
+ <string-array name="tile_states_modes_dnd">
+ <item msgid="6509540227356524582">"Қолжетімді емес"</item>
+ <item msgid="8589336868985358191">"Өшірулі"</item>
+ <item msgid="726072717827778234">"Қосулы"</item>
+ </string-array>
<string-array name="tile_states_flashlight">
<item msgid="3465257127433353857">"Қолжетімсіз"</item>
<item msgid="5044688398303285224">"Өшірулі"</item>
diff --git a/packages/SystemUI/res/values-km/strings.xml b/packages/SystemUI/res/values-km/strings.xml
index 579c509dd6db..e85b81d62ff1 100644
--- a/packages/SystemUI/res/values-km/strings.xml
+++ b/packages/SystemUI/res/values-km/strings.xml
@@ -419,6 +419,11 @@
<string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"ចុច ដើម្បីផ្គូផ្គងឧបករណ៍ថ្មី"</string>
<string name="hearing_devices_presets_error" msgid="350363093458408536">"មិនអាច​ប្ដូរ​ការកំណត់ជាមុន​បានទេ"</string>
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"កំណត់ជាមុន"</string>
+ <string name="hearing_devices_input_routing_label" msgid="730396728151232306">"មីក្រូហ្វូន​លំនាំ​ដើមសម្រាប់ការហៅទូរសព្ទ"</string>
+ <string-array name="hearing_device_input_routing_options">
+ <item msgid="4582190415045337003">"មីក្រូហ្វូនដែលមានឧបករណ៍​ជំនួយការ​ស្ដាប់"</item>
+ <item msgid="8501466270452446450">"មីក្រូហ្វូនរបស់ទូរសព្ទនេះ"</item>
+ </string-array>
<string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"បានជ្រើសរើស"</string>
<string name="hearing_devices_ambient_label" msgid="629440938614895797">"មជ្ឈដ្ឋានជុំវិញ"</string>
<string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"ឆ្វេង"</string>
@@ -582,6 +587,7 @@
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"នៅពេលអ្នកបង្ហាញកម្មវិធីណាមួយ <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> មើលឃើញអ្វីគ្រប់យ៉ាងដែលបង្ហាញ ឬចាក់ក្នុងកម្មវិធីនោះ។ ដូច្នេះ សូមប្រុងប្រយ័ត្នចំពោះអ្វីៗដូចជា ពាក្យសម្ងាត់ ព័ត៌មានលម្អិតអំពីការទូទាត់ប្រាក់ សារ រូបថត ព្រមទាំងសំឡេង និងវីដេអូ។"</string>
<string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"បង្ហាញ​អេក្រង់"</string>
<string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g> បានបិទជម្រើសនេះ"</string>
+ <string name="media_projection_entry_app_permission_dialog_single_app_not_supported" msgid="4860247304058870233">"មិនអាចប្រើបានតាមកម្មវិធីនេះទេ"</string>
<string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"ជ្រើសរើស​កម្មវិធី​ដើម្បី​ចែករំលែក"</string>
<string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"បញ្ជូនអេក្រង់របស់អ្នកឬ?"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"បញ្ជូនកម្មវិធីមួយ"</string>
@@ -794,8 +800,7 @@
<string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"បើក - ផ្អែកលើមុខ"</string>
<string name="inline_done_button" msgid="6043094985588909584">"រួចរាល់"</string>
<string name="inline_ok_button" msgid="603075490581280343">"ប្រើ"</string>
- <!-- no translation found for inline_turn_off_notifications (2653064779176881329) -->
- <skip />
+ <string name="inline_turn_off_notifications" msgid="2653064779176881329">"បិទ"</string>
<string name="notification_silence_title" msgid="8608090968400832335">"ស្ងាត់"</string>
<string name="notification_alert_title" msgid="3656229781017543655">"លំនាំដើម"</string>
<string name="notification_automatic_title" msgid="3745465364578762652">"ស្វ័យប្រវត្តិ"</string>
@@ -977,9 +982,13 @@
<string name="right_icon" msgid="1103955040645237425">"រូបតំណាង​ខាង​ស្ដាំ"</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"ចុច​ឱ្យ​ជាប់ រួចអូសដើម្បី​បញ្ចូល​ប្រអប់"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"ចុច​ឱ្យ​ជាប់ រួចអូស​ដើម្បី​រៀបចំ​ប្រអប់​ឡើងវិញ"</string>
+ <!-- no translation found for tap_to_position_tile (6282815817773342757) -->
+ <skip />
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"អូសទីនេះដើម្បីយកចេញ"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"អ្នក​ត្រូវការ​ប្រអប់​យ៉ាងតិច <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g>"</string>
<string name="qs_edit" msgid="5583565172803472437">"កែ"</string>
+ <!-- no translation found for qs_edit_tiles (2105215324060865035) -->
+ <skip />
<string name="tuner_time" msgid="2450785840990529997">"ម៉ោង"</string>
<string-array name="clock_options">
<item msgid="3986445361435142273">"បង្ហាញម៉ោង នាទី និងវិនាទី"</item>
@@ -995,6 +1004,10 @@
<string name="other" msgid="429768510980739978">"ផ្សេងៗ"</string>
<string name="accessibility_qs_edit_toggle_tile_size_action" msgid="1485194410119733586">"បិទ/បើកទំហំរបស់ប្រអប់"</string>
<string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"ដកប្រអប់ចេញ"</string>
+ <!-- no translation found for accessibility_qs_edit_toggle_placement_mode (3870429389210569610) -->
+ <skip />
+ <!-- no translation found for accessibility_qs_edit_toggle_selection (2201248304072372239) -->
+ <skip />
<string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"បញ្ចូលប្រអប់ទៅទីតាំងចុងក្រោយ"</string>
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"ផ្លាស់ទី​ប្រអប់"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"បញ្ចូលប្រអប់ទៅទីតាំងដែលចង់បាន"</string>
@@ -1570,5 +1583,7 @@
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"មិនស្គាល់"</string>
<string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"កំណត់ប្រអប់ទាំងអស់​ឡើងវិញឬ?"</string>
<string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"ប្រអប់​ការកំណត់រហ័សទាំងអស់នឹងកំណត់ឡើងវិញទៅការ​កំណត់ដើមរបស់ឧបករណ៍"</string>
+ <!-- no translation found for demote_explain_text (1600426458580544250) -->
+ <skip />
<string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
</resources>
diff --git a/packages/SystemUI/res/values-km/tiles_states_strings.xml b/packages/SystemUI/res/values-km/tiles_states_strings.xml
index ec120089a220..49c549f6016a 100644
--- a/packages/SystemUI/res/values-km/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-km/tiles_states_strings.xml
@@ -56,9 +56,11 @@
<item msgid="5376619709702103243">"បិទ"</item>
<item msgid="4875147066469902392">"បើក"</item>
</string-array>
- <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) -->
- <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) -->
- <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) -->
+ <string-array name="tile_states_modes_dnd">
+ <item msgid="6509540227356524582">"មិនមានទេ"</item>
+ <item msgid="8589336868985358191">"បិទ"</item>
+ <item msgid="726072717827778234">"បើក"</item>
+ </string-array>
<string-array name="tile_states_flashlight">
<item msgid="3465257127433353857">"មិនមានទេ"</item>
<item msgid="5044688398303285224">"បិទ"</item>
diff --git a/packages/SystemUI/res/values-kn/strings.xml b/packages/SystemUI/res/values-kn/strings.xml
index c84b33baed10..3bddcbd9f906 100644
--- a/packages/SystemUI/res/values-kn/strings.xml
+++ b/packages/SystemUI/res/values-kn/strings.xml
@@ -419,6 +419,11 @@
<string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"ಹೊಸ ಸಾಧನವನ್ನು ಜೋಡಿಸಲು ಕ್ಲಿಕ್ ಮಾಡಿ"</string>
<string name="hearing_devices_presets_error" msgid="350363093458408536">"ಪ್ರಿಸೆಟ್ ಅನ್ನು ಅಪ್‌ಡೇಟ್ ಮಾಡಲು ಸಾಧ್ಯವಾಗಲಿಲ್ಲ"</string>
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"ಪ್ರಿಸೆಟ್‌"</string>
+ <string name="hearing_devices_input_routing_label" msgid="730396728151232306">"ಕರೆಗಳಿಗಾಗಿ ಡೀಫಾಲ್ಟ್ ಮೈಕ್ರೊಫೋನ್"</string>
+ <string-array name="hearing_device_input_routing_options">
+ <item msgid="4582190415045337003">"ಶ್ರವಣ ಸಾಧನದ ಮೈಕ್ರೊಫೋನ್"</item>
+ <item msgid="8501466270452446450">"ಈ ಫೋನ್‌ನ ಮೈಕ್ರೊಫೋನ್"</item>
+ </string-array>
<string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"ಆಯ್ಕೆಮಾಡಲಾಗಿದೆ"</string>
<string name="hearing_devices_ambient_label" msgid="629440938614895797">"ಆ್ಯಂಬಿಯೆಂಟ್ ವಾಲ್ಯೂಮ್"</string>
<string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"ಎಡ"</string>
@@ -582,6 +587,7 @@
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"ನೀವು ಆ್ಯಪ್ ಅನ್ನು ಹಂಚಿಕೊಳ್ಳುತ್ತಿರುವಾಗ, ಆ ಆ್ಯಪ್‌ನಲ್ಲಿ ತೋರಿಸಿರುವ ಅಥವಾ ಪ್ಲೇ ಮಾಡಿದ ಏನಾದರೂ <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ಗೆ ಗೋಚರಿಸುತ್ತದೆ. ಆದ್ದರಿಂದ ಪಾಸ್‌ವರ್ಡ್‌ಗಳು, ಪಾವತಿ ವಿವರಗಳು, ಸಂದೇಶಗಳು, ಫೋಟೋಗಳು ಮತ್ತು ಆಡಿಯೋ ಮತ್ತು ವೀಡಿಯೊದಂತಹ ವಿಷಯಗಳ ಬಗ್ಗೆ ಜಾಗರೂಕರಾಗಿರಿ."</string>
<string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"ಸ್ಕ್ರೀನ್‌ ಹಂಚಿಕೊಳ್ಳಿ"</string>
<string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g> ಈ ಆಯ್ಕೆಯನ್ನು ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಿದೆ"</string>
+ <string name="media_projection_entry_app_permission_dialog_single_app_not_supported" msgid="4860247304058870233">"ಆ್ಯಪ್ ಬೆಂಬಲಿಸುವುದಿಲ್ಲ"</string>
<string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"ಹಂಚಿಕೊಳ್ಳಲು ಆ್ಯಪ್ ಅನ್ನು ಆಯ್ಕೆಮಾಡಿ"</string>
<string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"ನಿಮ್ಮ ಸ್ಕ್ರೀನ್ ಅನ್ನು ಬಿತ್ತರಿಸಬೇಕೇ?"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"ಒಂದು ಆ್ಯಪ್ ಅನ್ನು ಬಿತ್ತರಿಸಿ"</string>
@@ -794,8 +800,7 @@
<string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"ಆನ್ ಆಗಿದೆ - ಮುಖ-ಆಧಾರಿತ"</string>
<string name="inline_done_button" msgid="6043094985588909584">"ಪೂರ್ಣಗೊಂಡಿದೆ"</string>
<string name="inline_ok_button" msgid="603075490581280343">"ಅನ್ವಯಿಸಿ"</string>
- <!-- no translation found for inline_turn_off_notifications (2653064779176881329) -->
- <skip />
+ <string name="inline_turn_off_notifications" msgid="2653064779176881329">"ಆಫ್ ಮಾಡಿ"</string>
<string name="notification_silence_title" msgid="8608090968400832335">"ನಿಶ್ಶಬ್ದ"</string>
<string name="notification_alert_title" msgid="3656229781017543655">"ಡೀಫಾಲ್ಟ್"</string>
<string name="notification_automatic_title" msgid="3745465364578762652">"ಸ್ವಯಂಚಾಲಿತ"</string>
@@ -977,9 +982,13 @@
<string name="right_icon" msgid="1103955040645237425">"ಬಲ ಐಕಾನ್"</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"ಟೈಲ್‌ಗಳನ್ನು ಸೇರಿಸಲು ಹೋಲ್ಡ್‌ ಮಾಡಿ ಮತ್ತು ಡ್ರ್ಯಾಗ್‌ ಮಾಡಿ"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"ಟೈಲ್‌ಗಳನ್ನು ಮರುಹೊಂದಿಸಲು ಹೋಲ್ಡ್‌ ಮಾಡಿ ಮತ್ತು ಡ್ರ್ಯಾಗ್‌ ಮಾಡಿ"</string>
+ <!-- no translation found for tap_to_position_tile (6282815817773342757) -->
+ <skip />
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"ತೆಗೆದುಹಾಕಲು ಇಲ್ಲಿ ಡ್ರ್ಯಾಗ್‌ ಮಾಡಿ"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"ನಿಮಗೆ ಕನಿಷ್ಠ <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> ಟೈಲ್‌ಗಳ ಅಗತ್ಯವಿದೆ"</string>
<string name="qs_edit" msgid="5583565172803472437">"ಎಡಿಟ್"</string>
+ <!-- no translation found for qs_edit_tiles (2105215324060865035) -->
+ <skip />
<string name="tuner_time" msgid="2450785840990529997">"ಸಮಯ"</string>
<string-array name="clock_options">
<item msgid="3986445361435142273">"ಗಂಟೆಗಳು, ನಿಮಿಷಗಳು, ಸೆಕೆಂಡುಗಳನ್ನು ತೋರಿಸು"</item>
@@ -995,6 +1004,10 @@
<string name="other" msgid="429768510980739978">"ಇತರ"</string>
<string name="accessibility_qs_edit_toggle_tile_size_action" msgid="1485194410119733586">"ಟೈಲ್ ನ ಗಾತ್ರವನ್ನು ಟಾಗಲ್ ಮಾಡಿ"</string>
<string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"ಟೈಲ್ ಅನ್ನು ತೆಗೆದುಹಾಕಿ"</string>
+ <!-- no translation found for accessibility_qs_edit_toggle_placement_mode (3870429389210569610) -->
+ <skip />
+ <!-- no translation found for accessibility_qs_edit_toggle_selection (2201248304072372239) -->
+ <skip />
<string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"ಕೊನೆಯ ಸ್ಥಾನಕ್ಕೆ ಟೈಲ್ ಅನ್ನು ಸೇರಿಸಿ"</string>
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"ಟೈಲ್ ಸರಿಸಿ"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"ಬಯಸಿದ ಸ್ಥಾನಕ್ಕೆ ಟೈಲ್ ಅನ್ನು ಸೇರಿಸಿ"</string>
@@ -1570,5 +1583,7 @@
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"ಅಪರಿಚಿತ"</string>
<string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"ಎಲ್ಲಾ ಟೈಲ್‌ಗಳನ್ನು ರೀಸೆಟ್ ಮಾಡಬೇಕೆ?"</string>
<string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"ಎಲ್ಲಾ ತ್ವರಿತ ಸೆಟ್ಟಿಂಗ್‌ಗಳ ಟೈಲ್‌ಗಳನ್ನು ಸಾಧನದ ಮೂಲ ಸೆಟ್ಟಿಂಗ್‌ಗಳಿಗೆ ರೀಸೆಟ್ ಮಾಡಲಾಗುತ್ತದೆ"</string>
+ <!-- no translation found for demote_explain_text (1600426458580544250) -->
+ <skip />
<string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
</resources>
diff --git a/packages/SystemUI/res/values-kn/tiles_states_strings.xml b/packages/SystemUI/res/values-kn/tiles_states_strings.xml
index ad57922c9f2c..712936a39299 100644
--- a/packages/SystemUI/res/values-kn/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-kn/tiles_states_strings.xml
@@ -56,9 +56,11 @@
<item msgid="5376619709702103243">"ಆಫ್"</item>
<item msgid="4875147066469902392">"ಆನ್"</item>
</string-array>
- <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) -->
- <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) -->
- <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) -->
+ <string-array name="tile_states_modes_dnd">
+ <item msgid="6509540227356524582">"ಲಭ್ಯವಿಲ್ಲ"</item>
+ <item msgid="8589336868985358191">"ಆಫ್ ಆಗಿದೆ"</item>
+ <item msgid="726072717827778234">"ಆನ್ ಆಗಿದೆ"</item>
+ </string-array>
<string-array name="tile_states_flashlight">
<item msgid="3465257127433353857">"ಲಭ್ಯವಿಲ್ಲ"</item>
<item msgid="5044688398303285224">"ಆಫ್"</item>
diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml
index 048050cf53df..970096ea9e5f 100644
--- a/packages/SystemUI/res/values-ko/strings.xml
+++ b/packages/SystemUI/res/values-ko/strings.xml
@@ -419,6 +419,11 @@
<string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"새 기기와 페어링하려면 클릭하세요"</string>
<string name="hearing_devices_presets_error" msgid="350363093458408536">"사전 설정을 업데이트할 수 없음"</string>
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"미리 설정"</string>
+ <string name="hearing_devices_input_routing_label" msgid="730396728151232306">"통화용 기본 마이크"</string>
+ <string-array name="hearing_device_input_routing_options">
+ <item msgid="4582190415045337003">"보청기 마이크"</item>
+ <item msgid="8501466270452446450">"이 휴대전화의 마이크"</item>
+ </string-array>
<string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"선택됨"</string>
<string name="hearing_devices_ambient_label" msgid="629440938614895797">"주변 소리"</string>
<string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"왼쪽"</string>
@@ -582,6 +587,8 @@
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"앱을 공유하면 앱에 표시되거나 앱에서 재생되는 모든 항목이 <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>에 표시됩니다. 따라서 비밀번호, 결제 세부정보, 메시지, 사진, 오디오 및 동영상 등이 노출되지 않도록 주의하세요."</string>
<string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"화면 공유"</string>
<string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g>에서 이 옵션을 사용 중지했습니다."</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_not_supported (4860247304058870233) -->
+ <skip />
<string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"공유할 앱 선택"</string>
<string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"화면을 전송하시겠습니까?"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"앱 1개 전송"</string>
@@ -794,8 +801,7 @@
<string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"켜짐 - 얼굴 기준"</string>
<string name="inline_done_button" msgid="6043094985588909584">"완료"</string>
<string name="inline_ok_button" msgid="603075490581280343">"적용"</string>
- <!-- no translation found for inline_turn_off_notifications (2653064779176881329) -->
- <skip />
+ <string name="inline_turn_off_notifications" msgid="2653064779176881329">"사용 중지"</string>
<string name="notification_silence_title" msgid="8608090968400832335">"무음"</string>
<string name="notification_alert_title" msgid="3656229781017543655">"기본값"</string>
<string name="notification_automatic_title" msgid="3745465364578762652">"자동"</string>
@@ -977,9 +983,13 @@
<string name="right_icon" msgid="1103955040645237425">"오른쪽 아이콘"</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"길게 터치하고 드래그하여 타일 추가"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"길게 터치하고 드래그하여 타일 재정렬"</string>
+ <!-- no translation found for tap_to_position_tile (6282815817773342757) -->
+ <skip />
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"여기로 드래그하여 삭제"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"<xliff:g id="MIN_NUM_TILES">%1$d</xliff:g>개 이상의 타일이 필요합니다."</string>
<string name="qs_edit" msgid="5583565172803472437">"수정"</string>
+ <!-- no translation found for qs_edit_tiles (2105215324060865035) -->
+ <skip />
<string name="tuner_time" msgid="2450785840990529997">"시간"</string>
<string-array name="clock_options">
<item msgid="3986445361435142273">"시간, 분, 초 표시"</item>
@@ -995,6 +1005,10 @@
<string name="other" msgid="429768510980739978">"기타"</string>
<string name="accessibility_qs_edit_toggle_tile_size_action" msgid="1485194410119733586">"타일 크기 전환"</string>
<string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"타일 삭제"</string>
+ <!-- no translation found for accessibility_qs_edit_toggle_placement_mode (3870429389210569610) -->
+ <skip />
+ <!-- no translation found for accessibility_qs_edit_toggle_selection (2201248304072372239) -->
+ <skip />
<string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"마지막 위치에 타일 추가"</string>
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"타일 이동"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"원하는 위치에 타일 추가"</string>
@@ -1570,5 +1584,7 @@
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"알 수 없음"</string>
<string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"모든 타일을 재설정하시겠습니까?"</string>
<string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"모든 빠른 설정 타일이 기기의 원래 설정으로 재설정됩니다."</string>
+ <!-- no translation found for demote_explain_text (1600426458580544250) -->
+ <skip />
<string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ko/tiles_states_strings.xml b/packages/SystemUI/res/values-ko/tiles_states_strings.xml
index 49113eea79b7..f31f8106d374 100644
--- a/packages/SystemUI/res/values-ko/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-ko/tiles_states_strings.xml
@@ -56,9 +56,11 @@
<item msgid="5376619709702103243">"꺼짐"</item>
<item msgid="4875147066469902392">"켜짐"</item>
</string-array>
- <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) -->
- <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) -->
- <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) -->
+ <string-array name="tile_states_modes_dnd">
+ <item msgid="6509540227356524582">"사용할 수 없음"</item>
+ <item msgid="8589336868985358191">"사용 안함"</item>
+ <item msgid="726072717827778234">"사용"</item>
+ </string-array>
<string-array name="tile_states_flashlight">
<item msgid="3465257127433353857">"이용 불가"</item>
<item msgid="5044688398303285224">"꺼짐"</item>
diff --git a/packages/SystemUI/res/values-ky/strings.xml b/packages/SystemUI/res/values-ky/strings.xml
index e6b108126012..46c67e72b93a 100644
--- a/packages/SystemUI/res/values-ky/strings.xml
+++ b/packages/SystemUI/res/values-ky/strings.xml
@@ -419,6 +419,11 @@
<string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Жаңы түзмөк кошуу үчүн басыңыз"</string>
<string name="hearing_devices_presets_error" msgid="350363093458408536">"Алдын ала коюлган параметрлер жаңыртылган жок"</string>
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"Алдын ала коюлган параметрлер"</string>
+ <string name="hearing_devices_input_routing_label" msgid="730396728151232306">"Чалуулар үчүн демейки микрофон"</string>
+ <string-array name="hearing_device_input_routing_options">
+ <item msgid="4582190415045337003">"Угуу аппаратынын микрофону"</item>
+ <item msgid="8501466270452446450">"Ушул телефондун микрофону"</item>
+ </string-array>
<string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"Тандалды"</string>
<string name="hearing_devices_ambient_label" msgid="629440938614895797">"Айланадагы үндөр"</string>
<string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Сол"</string>
@@ -582,6 +587,7 @@
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Колдонмону бөлүшкөндө, андагы нерселер <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> колдонмосуна көрүнөт. Андыктан сырсөздөр, төлөмдүн чоо-жайы, билдирүүлөр, сүрөттөр, аудио жана видеолор менен этият болуңуз."</string>
<string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Экранды бөлүшүү"</string>
<string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g> бул параметрди өчүрүп койду"</string>
+ <string name="media_projection_entry_app_permission_dialog_single_app_not_supported" msgid="4860247304058870233">"Колдонмодо иштебейт"</string>
<string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"Бөлүшүү үчүн колдонмо тандоо"</string>
<string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Экранды башка түзмөккө чыгарасызбы?"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Бир колдонмону чыгаруу"</string>
@@ -794,8 +800,7 @@
<string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Күйүк – Жүздүн негизинде"</string>
<string name="inline_done_button" msgid="6043094985588909584">"Бүттү"</string>
<string name="inline_ok_button" msgid="603075490581280343">"Колдонуу"</string>
- <!-- no translation found for inline_turn_off_notifications (2653064779176881329) -->
- <skip />
+ <string name="inline_turn_off_notifications" msgid="2653064779176881329">"Өчүрүү"</string>
<string name="notification_silence_title" msgid="8608090968400832335">"Үнсүз"</string>
<string name="notification_alert_title" msgid="3656229781017543655">"Демейки"</string>
<string name="notification_automatic_title" msgid="3745465364578762652">"Автоматтык"</string>
@@ -977,9 +982,13 @@
<string name="right_icon" msgid="1103955040645237425">"¨Оңго¨ сүрөтчөсү"</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"Керектүү элементтерди сүйрөп келиңиз"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"Иретин өзгөртүү үчүн кармап туруп, сүйрөңүз"</string>
+ <!-- no translation found for tap_to_position_tile (6282815817773342757) -->
+ <skip />
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"Өчүрүү үчүн бул жерге сүйрөңүз"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"Сизге жок дегенде <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> мозаика керек"</string>
<string name="qs_edit" msgid="5583565172803472437">"Түзөтүү"</string>
+ <!-- no translation found for qs_edit_tiles (2105215324060865035) -->
+ <skip />
<string name="tuner_time" msgid="2450785840990529997">"Убакыт"</string>
<string-array name="clock_options">
<item msgid="3986445361435142273">"Сааттар, мүнөттөр жана секунддар"</item>
@@ -995,6 +1004,10 @@
<string name="other" msgid="429768510980739978">"Башка"</string>
<string name="accessibility_qs_edit_toggle_tile_size_action" msgid="1485194410119733586">"плитканын өлчөмүн которуштуруу"</string>
<string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"ыкчам баскычты өчүрүү"</string>
+ <!-- no translation found for accessibility_qs_edit_toggle_placement_mode (3870429389210569610) -->
+ <skip />
+ <!-- no translation found for accessibility_qs_edit_toggle_selection (2201248304072372239) -->
+ <skip />
<string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"аягына карта кошуу"</string>
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Ыкчам баскычты жылдыруу"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"Керектүү жерге карта кошуу"</string>
@@ -1570,5 +1583,7 @@
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Белгисиз"</string>
<string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"Бардык параметрлерди кайра коесузбу?"</string>
<string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Бардык ыкчам параметрлер түзмөктүн баштапкы маанилерине кайтарылат"</string>
+ <!-- no translation found for demote_explain_text (1600426458580544250) -->
+ <skip />
<string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ky/tiles_states_strings.xml b/packages/SystemUI/res/values-ky/tiles_states_strings.xml
index 68f8987a02dc..fe451facb749 100644
--- a/packages/SystemUI/res/values-ky/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-ky/tiles_states_strings.xml
@@ -56,9 +56,11 @@
<item msgid="5376619709702103243">"Өчүк"</item>
<item msgid="4875147066469902392">"Күйүк"</item>
</string-array>
- <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) -->
- <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) -->
- <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) -->
+ <string-array name="tile_states_modes_dnd">
+ <item msgid="6509540227356524582">"Жеткиликсиз"</item>
+ <item msgid="8589336868985358191">"Өчүк"</item>
+ <item msgid="726072717827778234">"Күйүк"</item>
+ </string-array>
<string-array name="tile_states_flashlight">
<item msgid="3465257127433353857">"Жеткиликсиз"</item>
<item msgid="5044688398303285224">"Өчүк"</item>
diff --git a/packages/SystemUI/res/values-lo/strings.xml b/packages/SystemUI/res/values-lo/strings.xml
index e7229e8f3704..961cab32d18f 100644
--- a/packages/SystemUI/res/values-lo/strings.xml
+++ b/packages/SystemUI/res/values-lo/strings.xml
@@ -419,6 +419,11 @@
<string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"ຄລິກເພື່ອຈັບຄູ່ອຸປະກອນໃໝ່"</string>
<string name="hearing_devices_presets_error" msgid="350363093458408536">"ບໍ່ສາມາດອັບເດດການຕັ້ງຄ່າລ່ວງໜ້າໄດ້"</string>
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"ຄ່າທີ່ກຳນົດລ່ວງໜ້າ"</string>
+ <string name="hearing_devices_input_routing_label" msgid="730396728151232306">"ໄມໂຄຣໂຟນເລີ່ມຕົ້ນສຳລັບການໂທ"</string>
+ <string-array name="hearing_device_input_routing_options">
+ <item msgid="4582190415045337003">"ໄມໂຄຣໂຟນເຄື່ອງຊ່ວຍຟັງ"</item>
+ <item msgid="8501466270452446450">"ໄມໂຄຣໂຟນຂອງໂທລະສັບເຄື່ອງນີ້"</item>
+ </string-array>
<string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"ເລືອກແລ້ວ"</string>
<string name="hearing_devices_ambient_label" msgid="629440938614895797">"ສຽງແວດລ້ອມ"</string>
<string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"ຊ້າຍ"</string>
@@ -582,6 +587,7 @@
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"ເມື່ອທ່ານແບ່ງປັນແອັບຂອງທ່ານ, ຄົນອື່ນຈະເບິ່ງເຫັນທຸກຢ່າງທີ່ສະແດງ ຫຼື ຫຼິ້ນໃນແອັບໃນ <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. ດັ່ງນັ້ນ, ໃຫ້ລະມັດລະວັງສິ່ງຕ່າງໆ ເຊັ່ນ: ລະຫັດຜ່ານ, ລາຍລະອຽດການຈ່າຍເງິນ, ຂໍ້ຄວາມ, ຮູບພາບ, ພ້ອມທັງສຽງ ແລະ ວິດີໂອ."</string>
<string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"ແບ່ງປັນໜ້າຈໍ"</string>
<string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g> ປິດການນຳໃຊ້ຕົວເລືອກນີ້ແລ້ວ"</string>
+ <string name="media_projection_entry_app_permission_dialog_single_app_not_supported" msgid="4860247304058870233">"ບໍ່ຮອງຮັບໂດຍແອັບ"</string>
<string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"ເລືອກແອັບທີ່ຈະແບ່ງປັນ"</string>
<string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"ສົ່ງສັນຍານໜ້າຈໍຂອງທ່ານບໍ?"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"ສົ່ງສັນຍານແອັບ 1 ລາຍການ"</string>
@@ -794,8 +800,7 @@
<string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"ເປີດ - ອ້າງອີງໃບໜ້າ"</string>
<string name="inline_done_button" msgid="6043094985588909584">"ແລ້ວໆ"</string>
<string name="inline_ok_button" msgid="603075490581280343">"ນຳໃຊ້"</string>
- <!-- no translation found for inline_turn_off_notifications (2653064779176881329) -->
- <skip />
+ <string name="inline_turn_off_notifications" msgid="2653064779176881329">"ປິດ"</string>
<string name="notification_silence_title" msgid="8608090968400832335">"ປິດສຽງ"</string>
<string name="notification_alert_title" msgid="3656229781017543655">"ຄ່າເລີ່ມຕົ້ນ"</string>
<string name="notification_automatic_title" msgid="3745465364578762652">"ອັດຕະໂນມັດ"</string>
@@ -977,9 +982,13 @@
<string name="right_icon" msgid="1103955040645237425">"ໄອຄອນຂວາ"</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"ກົດຄ້າງໄວ້ແລ້ວລາກເພື່ອເພີ່ມຊ່ອງ"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"ກົດຄ້າງໄວ້ແລ້ວລາກເພື່ອຈັດຮຽງໃໝ່"</string>
+ <!-- no translation found for tap_to_position_tile (6282815817773342757) -->
+ <skip />
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"ລາກມາບ່ອນນີ້ເພື່ອລຶບອອກ"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"ທ່ານຍຕ້ອງໃຊ້ຢ່າງໜ້ອຍ <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> ຊ່ອງ"</string>
<string name="qs_edit" msgid="5583565172803472437">"ແກ້ໄຂ"</string>
+ <!-- no translation found for qs_edit_tiles (2105215324060865035) -->
+ <skip />
<string name="tuner_time" msgid="2450785840990529997">"ເວລາ"</string>
<string-array name="clock_options">
<item msgid="3986445361435142273">"ສະແດງຊົ່ວໂມງ, ນາທີ ແລະ ວິນາທີ"</item>
@@ -995,6 +1004,10 @@
<string name="other" msgid="429768510980739978">"ອື່ນໆ"</string>
<string name="accessibility_qs_edit_toggle_tile_size_action" msgid="1485194410119733586">"ສະຫຼັບຂະໜາດຂອງແຜ່ນ"</string>
<string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"ລຶບແຜ່ນອອກ"</string>
+ <!-- no translation found for accessibility_qs_edit_toggle_placement_mode (3870429389210569610) -->
+ <skip />
+ <!-- no translation found for accessibility_qs_edit_toggle_selection (2201248304072372239) -->
+ <skip />
<string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"ເພີ່ມແຜ່ນໃສ່ຕຳແໜ່ງສຸດທ້າຍ"</string>
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"ຍ້າຍແຜ່ນ"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"ເພີ່ມແຜ່ນໃສ່ຕຳແໜ່ງທີ່ຕ້ອງການ"</string>
@@ -1570,5 +1583,7 @@
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"ບໍ່ຮູ້ຈັກ"</string>
<string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"ຣີເຊັດແຜ່ນທັງໝົດບໍ?"</string>
<string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"ແຜ່ນການຕັ້ງຄ່າດ່ວນທັງໝົດຈະຣີເຊັດເປັນການຕັ້ງຄ່າແບບເກົ່າຂອງອຸປະກອນ"</string>
+ <!-- no translation found for demote_explain_text (1600426458580544250) -->
+ <skip />
<string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
</resources>
diff --git a/packages/SystemUI/res/values-lo/tiles_states_strings.xml b/packages/SystemUI/res/values-lo/tiles_states_strings.xml
index 90390e23162b..72c57e645879 100644
--- a/packages/SystemUI/res/values-lo/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-lo/tiles_states_strings.xml
@@ -56,9 +56,11 @@
<item msgid="5376619709702103243">"ປິດ"</item>
<item msgid="4875147066469902392">"ເປີດ"</item>
</string-array>
- <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) -->
- <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) -->
- <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) -->
+ <string-array name="tile_states_modes_dnd">
+ <item msgid="6509540227356524582">"ບໍ່ພ້ອມໃຫ້ນຳໃຊ້"</item>
+ <item msgid="8589336868985358191">"ປິດ"</item>
+ <item msgid="726072717827778234">"ເປີດ"</item>
+ </string-array>
<string-array name="tile_states_flashlight">
<item msgid="3465257127433353857">"ບໍ່ສາມາດໃຊ້ໄດ້"</item>
<item msgid="5044688398303285224">"ປິດ"</item>
diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml
index 6563cb97f35d..46ef5cf78e61 100644
--- a/packages/SystemUI/res/values-lt/strings.xml
+++ b/packages/SystemUI/res/values-lt/strings.xml
@@ -419,6 +419,11 @@
<string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Spustelėkite, kad susietumėte naują įrenginį"</string>
<string name="hearing_devices_presets_error" msgid="350363093458408536">"Išankstinių nustatymų atnaujinti nepavyko"</string>
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"Išankstiniai nustatymai"</string>
+ <string name="hearing_devices_input_routing_label" msgid="730396728151232306">"Numatytasis mikrofonas skambučiams"</string>
+ <string-array name="hearing_device_input_routing_options">
+ <item msgid="4582190415045337003">"Klausos aparato mikrofonas"</item>
+ <item msgid="8501466270452446450">"Šio telefono mikrofonas"</item>
+ </string-array>
<string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"Pasirinkta"</string>
<string name="hearing_devices_ambient_label" msgid="629440938614895797">"Aplinka"</string>
<string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Kairė"</string>
@@ -582,6 +587,8 @@
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Kai bendrinate programą, „<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>“ matomas visas toje programoje rodomas ar leidžiamas turinys. Todėl būkite atsargūs naudodami slaptažodžius, išsamią mokėjimo metodo informaciją, pranešimus, nuotraukas ir garso bei vaizdo įrašus."</string>
<string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Bendrinti ekraną"</string>
<string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"Programoje „<xliff:g id="APP_NAME">%1$s</xliff:g>“ ši parinktis išjungta"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_not_supported (4860247304058870233) -->
+ <skip />
<string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"Norimos bendrinti programos pasirinkimas"</string>
<string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Perduoti ekraną?"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Perduoti vieną programą"</string>
@@ -794,8 +801,7 @@
<string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Įjungta – pagal veidą"</string>
<string name="inline_done_button" msgid="6043094985588909584">"Atlikta"</string>
<string name="inline_ok_button" msgid="603075490581280343">"Taikyti"</string>
- <!-- no translation found for inline_turn_off_notifications (2653064779176881329) -->
- <skip />
+ <string name="inline_turn_off_notifications" msgid="2653064779176881329">"Išjungti"</string>
<string name="notification_silence_title" msgid="8608090968400832335">"Tylūs"</string>
<string name="notification_alert_title" msgid="3656229781017543655">"Numatytasis"</string>
<string name="notification_automatic_title" msgid="3745465364578762652">"Automatinis"</string>
@@ -977,9 +983,13 @@
<string name="right_icon" msgid="1103955040645237425">"Piktograma dešinėje"</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"Jei norite pridėti išklotinių, laikykite nuspaudę ir vilkite"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"Norėdami pertvarkyti išklot., laikykite nuspaudę ir vilkite"</string>
+ <!-- no translation found for tap_to_position_tile (6282815817773342757) -->
+ <skip />
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"Vilkite čia, jei norite pašalinti"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"Turi būti bent <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> išklotinės elem."</string>
<string name="qs_edit" msgid="5583565172803472437">"Redaguoti"</string>
+ <!-- no translation found for qs_edit_tiles (2105215324060865035) -->
+ <skip />
<string name="tuner_time" msgid="2450785840990529997">"Laikas"</string>
<string-array name="clock_options">
<item msgid="3986445361435142273">"Rodyti valandas, minutes ir sekundes"</item>
@@ -995,6 +1005,10 @@
<string name="other" msgid="429768510980739978">"Kita"</string>
<string name="accessibility_qs_edit_toggle_tile_size_action" msgid="1485194410119733586">"perjungti išklotinės elemento dydį"</string>
<string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"pašalintumėte išklotinės elementą"</string>
+ <!-- no translation found for accessibility_qs_edit_toggle_placement_mode (3870429389210569610) -->
+ <skip />
+ <!-- no translation found for accessibility_qs_edit_toggle_selection (2201248304072372239) -->
+ <skip />
<string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"pridėti išklotinės elementą paskutinėje pozicijoje"</string>
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Perkelti išklotinės elementą"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"Pridėti išklotinės elementą norimoje pozicijoje"</string>
@@ -1570,5 +1584,7 @@
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Nežinoma"</string>
<string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"Iš naujo nustatyti visus išklotines elementus?"</string>
<string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Visi sparčiųjų nustatymų išklotinės elementai bus iš naujo nustatyti į pradinius įrenginio nustatymus"</string>
+ <!-- no translation found for demote_explain_text (1600426458580544250) -->
+ <skip />
<string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
</resources>
diff --git a/packages/SystemUI/res/values-lt/tiles_states_strings.xml b/packages/SystemUI/res/values-lt/tiles_states_strings.xml
index 3e1f3c702e61..4ed6bd95303a 100644
--- a/packages/SystemUI/res/values-lt/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-lt/tiles_states_strings.xml
@@ -56,9 +56,11 @@
<item msgid="5376619709702103243">"Išjungta"</item>
<item msgid="4875147066469902392">"Įjungta"</item>
</string-array>
- <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) -->
- <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) -->
- <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) -->
+ <string-array name="tile_states_modes_dnd">
+ <item msgid="6509540227356524582">"Nepasiekiama"</item>
+ <item msgid="8589336868985358191">"Išjungta"</item>
+ <item msgid="726072717827778234">"Įjungta"</item>
+ </string-array>
<string-array name="tile_states_flashlight">
<item msgid="3465257127433353857">"Nepasiekiama"</item>
<item msgid="5044688398303285224">"Išjungta"</item>
diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml
index 0c030b7029f9..5c052d92f220 100644
--- a/packages/SystemUI/res/values-lv/strings.xml
+++ b/packages/SystemUI/res/values-lv/strings.xml
@@ -419,6 +419,11 @@
<string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Noklikšķiniet, lai savienotu pārī jaunu ierīci"</string>
<string name="hearing_devices_presets_error" msgid="350363093458408536">"Nevarēja atjaunināt pirmsiestatījumu"</string>
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"Pirmsiestatījums"</string>
+ <string name="hearing_devices_input_routing_label" msgid="730396728151232306">"Noklusējuma mikrofons zvaniem"</string>
+ <string-array name="hearing_device_input_routing_options">
+ <item msgid="4582190415045337003">"Dzirdes aparāta mikrofons"</item>
+ <item msgid="8501466270452446450">"Šī tālruņa mikrofons"</item>
+ </string-array>
<string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"Atlasīts"</string>
<string name="hearing_devices_ambient_label" msgid="629440938614895797">"Apkārtnes skaņas"</string>
<string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Pa kreisi"</string>
@@ -582,6 +587,8 @@
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Kopīgojot lietotni, lietotnei <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ir pieejams viss kopīgotajā lietotnē parādītais vai atskaņotais saturs. Tāpēc piesardzīgi apejieties ar parolēm, maksājumu informāciju, ziņojumiem, fotoattēliem un audio un video saturu."</string>
<string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Kopīgot ekrānu"</string>
<string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"Lietotnē <xliff:g id="APP_NAME">%1$s</xliff:g> tika atspējota šī opcija"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_not_supported (4860247304058870233) -->
+ <skip />
<string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"Lietotnes izvēlēšanās kopīgošanai"</string>
<string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Vai apraidīt ekrānu?"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Apraidīt vienu lietotni"</string>
@@ -794,8 +801,7 @@
<string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Ieslēgta — ar sejas noteikšanu"</string>
<string name="inline_done_button" msgid="6043094985588909584">"Gatavs"</string>
<string name="inline_ok_button" msgid="603075490581280343">"Lietot"</string>
- <!-- no translation found for inline_turn_off_notifications (2653064779176881329) -->
- <skip />
+ <string name="inline_turn_off_notifications" msgid="2653064779176881329">"Izslēgt"</string>
<string name="notification_silence_title" msgid="8608090968400832335">"Klusums"</string>
<string name="notification_alert_title" msgid="3656229781017543655">"Noklusējums"</string>
<string name="notification_automatic_title" msgid="3745465364578762652">"Automātiski"</string>
@@ -977,9 +983,13 @@
<string name="right_icon" msgid="1103955040645237425">"Ikona labajā pusē"</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"Lai pievienotu elementus, turiet un velciet tos"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"Lai pārkārtotu elementus, turiet un velciet tos"</string>
+ <!-- no translation found for tap_to_position_tile (6282815817773342757) -->
+ <skip />
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"Lai noņemtu vienumus, velciet tos šeit."</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"Nepieciešami vismaz <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> elementi"</string>
<string name="qs_edit" msgid="5583565172803472437">"Rediģēt"</string>
+ <!-- no translation found for qs_edit_tiles (2105215324060865035) -->
+ <skip />
<string name="tuner_time" msgid="2450785840990529997">"Laiks"</string>
<string-array name="clock_options">
<item msgid="3986445361435142273">"Rādīt stundas, minūtes un sekundes"</item>
@@ -995,6 +1005,10 @@
<string name="other" msgid="429768510980739978">"Citi"</string>
<string name="accessibility_qs_edit_toggle_tile_size_action" msgid="1485194410119733586">"pārslēgt elementa lielumu"</string>
<string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"noņemt elementu"</string>
+ <!-- no translation found for accessibility_qs_edit_toggle_placement_mode (3870429389210569610) -->
+ <skip />
+ <!-- no translation found for accessibility_qs_edit_toggle_selection (2201248304072372239) -->
+ <skip />
<string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"pievienotu elementu pēdējā pozīcijā"</string>
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Pārvietot elementu"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"Pievienot elementu vēlamajā pozīcijā"</string>
@@ -1570,5 +1584,7 @@
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Nezināma"</string>
<string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"Vai atiestatīt visus elementus?"</string>
<string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Visiem ātro iestatījumu elementiem tiks atiestatīti sākotnējie iestatījumi"</string>
+ <!-- no translation found for demote_explain_text (1600426458580544250) -->
+ <skip />
<string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
</resources>
diff --git a/packages/SystemUI/res/values-lv/tiles_states_strings.xml b/packages/SystemUI/res/values-lv/tiles_states_strings.xml
index e55babc7317b..0c59186e2165 100644
--- a/packages/SystemUI/res/values-lv/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-lv/tiles_states_strings.xml
@@ -56,9 +56,11 @@
<item msgid="5376619709702103243">"Izslēgts"</item>
<item msgid="4875147066469902392">"Ieslēgts"</item>
</string-array>
- <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) -->
- <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) -->
- <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) -->
+ <string-array name="tile_states_modes_dnd">
+ <item msgid="6509540227356524582">"Nav pieejams"</item>
+ <item msgid="8589336868985358191">"Izslēgts"</item>
+ <item msgid="726072717827778234">"Ieslēgts"</item>
+ </string-array>
<string-array name="tile_states_flashlight">
<item msgid="3465257127433353857">"Nav pieejams"</item>
<item msgid="5044688398303285224">"Izslēgts"</item>
diff --git a/packages/SystemUI/res/values-mk/strings.xml b/packages/SystemUI/res/values-mk/strings.xml
index b798f457ee8b..b8ed62f46a15 100644
--- a/packages/SystemUI/res/values-mk/strings.xml
+++ b/packages/SystemUI/res/values-mk/strings.xml
@@ -419,6 +419,11 @@
<string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Кликнете за да спарите нов уред"</string>
<string name="hearing_devices_presets_error" msgid="350363093458408536">"Не можеше да се ажурира зададената вредност"</string>
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"Зададени вредности"</string>
+ <string name="hearing_devices_input_routing_label" msgid="730396728151232306">"Стандарден микрофон за повици"</string>
+ <string-array name="hearing_device_input_routing_options">
+ <item msgid="4582190415045337003">"Микрофон на слушно помагало"</item>
+ <item msgid="8501466270452446450">"Микрофонот на овој телефон"</item>
+ </string-array>
<string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"Избрано"</string>
<string name="hearing_devices_ambient_label" msgid="629440938614895797">"Опкружување"</string>
<string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Лево"</string>
@@ -582,6 +587,7 @@
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Додека споделувате апликација, сѐ што се прикажува или пушта на таа апликација е видливо за <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Затоа, бидете внимателни со работи како лозинки, детали за плаќање, пораки, фотографии и аудио и видео."</string>
<string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Сподели екран"</string>
<string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g> ја оневозможи опцијава"</string>
+ <string name="media_projection_entry_app_permission_dialog_single_app_not_supported" msgid="4860247304058870233">"Не е поддржано од апликацијата"</string>
<string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"Изберете апликација за споделување"</string>
<string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Да се емитува вашиот екран?"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Емитувајте една апликација"</string>
@@ -794,8 +800,7 @@
<string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Вклучено - според лице"</string>
<string name="inline_done_button" msgid="6043094985588909584">"Готово"</string>
<string name="inline_ok_button" msgid="603075490581280343">"Примени"</string>
- <!-- no translation found for inline_turn_off_notifications (2653064779176881329) -->
- <skip />
+ <string name="inline_turn_off_notifications" msgid="2653064779176881329">"Исклучи"</string>
<string name="notification_silence_title" msgid="8608090968400832335">"Безгласно"</string>
<string name="notification_alert_title" msgid="3656229781017543655">"Стандардно"</string>
<string name="notification_automatic_title" msgid="3745465364578762652">"Автоматски"</string>
@@ -977,9 +982,13 @@
<string name="right_icon" msgid="1103955040645237425">"Десна икона"</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"Задржете и влечете за да додадете плочки"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"Задржете и влечете за да ги преуредите плочките"</string>
+ <!-- no translation found for tap_to_position_tile (6282815817773342757) -->
+ <skip />
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"Повлечете тука за да се отстрани"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"Потребни ви се најмалку <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> плочки"</string>
<string name="qs_edit" msgid="5583565172803472437">"Измени"</string>
+ <!-- no translation found for qs_edit_tiles (2105215324060865035) -->
+ <skip />
<string name="tuner_time" msgid="2450785840990529997">"Време"</string>
<string-array name="clock_options">
<item msgid="3986445361435142273">"Прикажи часови, минути и секунди"</item>
@@ -995,6 +1004,10 @@
<string name="other" msgid="429768510980739978">"Друго"</string>
<string name="accessibility_qs_edit_toggle_tile_size_action" msgid="1485194410119733586">"вклучување/исклучување на големината на плочката"</string>
<string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"отстранување на плочката"</string>
+ <!-- no translation found for accessibility_qs_edit_toggle_placement_mode (3870429389210569610) -->
+ <skip />
+ <!-- no translation found for accessibility_qs_edit_toggle_selection (2201248304072372239) -->
+ <skip />
<string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"додајте плочка на последната позиција"</string>
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Преместување на плочката"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"Додајте плочка на саканата позиција"</string>
@@ -1570,5 +1583,7 @@
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Непознато"</string>
<string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"Да се ресетираат сите плочки?"</string>
<string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Сите плочки на „Брзи поставки“ ќе се ресетираат на првичните поставки на уредот"</string>
+ <!-- no translation found for demote_explain_text (1600426458580544250) -->
+ <skip />
<string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
</resources>
diff --git a/packages/SystemUI/res/values-mk/tiles_states_strings.xml b/packages/SystemUI/res/values-mk/tiles_states_strings.xml
index 8986ca5a7c86..0f32b8e88573 100644
--- a/packages/SystemUI/res/values-mk/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-mk/tiles_states_strings.xml
@@ -56,9 +56,11 @@
<item msgid="5376619709702103243">"Исклучено"</item>
<item msgid="4875147066469902392">"Вклучено"</item>
</string-array>
- <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) -->
- <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) -->
- <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) -->
+ <string-array name="tile_states_modes_dnd">
+ <item msgid="6509540227356524582">"Недостапно"</item>
+ <item msgid="8589336868985358191">"Исклучено"</item>
+ <item msgid="726072717827778234">"Вклучено"</item>
+ </string-array>
<string-array name="tile_states_flashlight">
<item msgid="3465257127433353857">"Недостапно"</item>
<item msgid="5044688398303285224">"Исклучено"</item>
diff --git a/packages/SystemUI/res/values-ml/strings.xml b/packages/SystemUI/res/values-ml/strings.xml
index eb7da33b02d2..4b21fa28f697 100644
--- a/packages/SystemUI/res/values-ml/strings.xml
+++ b/packages/SystemUI/res/values-ml/strings.xml
@@ -419,6 +419,11 @@
<string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"പുതിയ ഉപകരണം ജോടിയാക്കാൻ ക്ലിക്ക് ചെയ്യുക"</string>
<string name="hearing_devices_presets_error" msgid="350363093458408536">"പ്രീസെറ്റ് അപ്ഡേറ്റ് ചെയ്യാനായില്ല"</string>
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"പ്രീസെറ്റ്"</string>
+ <string name="hearing_devices_input_routing_label" msgid="730396728151232306">"കോളുകൾക്കുള്ള ഡിഫോൾട്ട് മൈക്രോഫോൺ"</string>
+ <string-array name="hearing_device_input_routing_options">
+ <item msgid="4582190415045337003">"ശ്രവണ സഹായി മൈക്രോഫോൺ"</item>
+ <item msgid="8501466270452446450">"ഈ ഫോണിന്റെ മൈക്രോഫോൺ"</item>
+ </string-array>
<string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"തിരഞ്ഞെടുത്തു"</string>
<string name="hearing_devices_ambient_label" msgid="629440938614895797">"സറൗണ്ടിംഗ്‌സ്"</string>
<string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"ഇടത്"</string>
@@ -582,6 +587,7 @@
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"നിങ്ങളുടെ ആപ്പ് പങ്കിടുമ്പോൾ, ആ ആപ്പിൽ കാണിക്കുന്നതോ പ്ലേ ചെയ്യുന്നതോ ആയ എല്ലാ കാര്യങ്ങളും <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> എന്നതിന് ദൃശ്യമാകും. അതിനാൽ പാസ്‍വേഡുകൾ, പേയ്‌മെന്റ് വിശദാംശങ്ങൾ, സന്ദേശങ്ങൾ, ഫോട്ടോകൾ, ഓഡിയോ, വീഡിയോ എന്നിവ പോലുള്ള കാര്യങ്ങളിൽ ശ്രദ്ധ പുലർത്തുക."</string>
<string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"സ്‌ക്രീൻ പങ്കിടുക"</string>
<string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g> ഈ ഓപ്‌ഷൻ പ്രവർത്തനരഹിതമാക്കി"</string>
+ <string name="media_projection_entry_app_permission_dialog_single_app_not_supported" msgid="4860247304058870233">"ആപ്പ് പിന്തുണയ്ക്കുന്നില്ല"</string>
<string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"പങ്കിടാൻ ആപ്പ് തിരഞ്ഞെടുക്കുക"</string>
<string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"നിങ്ങളുടെ സ്ക്രീൻ കാസ്റ്റ് ചെയ്യണോ?"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"ഒരു ആപ്പ് കാസ്റ്റ് ചെയ്യുക"</string>
@@ -794,8 +800,7 @@
<string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"ഓണാണ് - ഫേസ് ബേസ്‌ഡ്"</string>
<string name="inline_done_button" msgid="6043094985588909584">"പൂർത്തിയായി"</string>
<string name="inline_ok_button" msgid="603075490581280343">"ബാധകമാക്കുക"</string>
- <!-- no translation found for inline_turn_off_notifications (2653064779176881329) -->
- <skip />
+ <string name="inline_turn_off_notifications" msgid="2653064779176881329">"ഓഫാക്കുക"</string>
<string name="notification_silence_title" msgid="8608090968400832335">"നിശബ്‌ദം"</string>
<string name="notification_alert_title" msgid="3656229781017543655">"ഡിഫോൾട്ട്"</string>
<string name="notification_automatic_title" msgid="3745465364578762652">"സ്വയമേവ"</string>
@@ -977,9 +982,13 @@
<string name="right_icon" msgid="1103955040645237425">"വലതുവശത്തെ ചിഹ്നം"</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"ടൈലുകൾ ചേർക്കാൻ അമർത്തിപ്പിടിച്ച് വലിച്ചിടുക"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"ടൈലുകൾ പുനഃക്രമീകരിക്കാൻ അമർത്തിപ്പിടിച്ച് വലിച്ചിടുക"</string>
+ <!-- no translation found for tap_to_position_tile (6282815817773342757) -->
+ <skip />
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"നീക്കംചെയ്യുന്നതിന് ഇവിടെ വലിച്ചിടുക"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"നിങ്ങൾക്ക് ചുരുങ്ങിയത് <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> ടൈലുകളെങ്കിലും വേണം"</string>
<string name="qs_edit" msgid="5583565172803472437">"എഡിറ്റ് ചെയ്യുക"</string>
+ <!-- no translation found for qs_edit_tiles (2105215324060865035) -->
+ <skip />
<string name="tuner_time" msgid="2450785840990529997">"സമയം"</string>
<string-array name="clock_options">
<item msgid="3986445361435142273">"മണിക്കൂറും മിനിറ്റും സെക്കൻഡും കാണിക്കുക"</item>
@@ -995,6 +1004,10 @@
<string name="other" msgid="429768510980739978">"മറ്റുള്ളവ"</string>
<string name="accessibility_qs_edit_toggle_tile_size_action" msgid="1485194410119733586">"ടൈലിന്റെ വലുപ്പം മാറ്റുക"</string>
<string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"ടൈൽ നീക്കം ചെയ്യുക"</string>
+ <!-- no translation found for accessibility_qs_edit_toggle_placement_mode (3870429389210569610) -->
+ <skip />
+ <!-- no translation found for accessibility_qs_edit_toggle_selection (2201248304072372239) -->
+ <skip />
<string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"അവസാന ഭാഗത്ത് ടൈൽ ചേർക്കുക"</string>
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"ടൈൽ നീക്കുക"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"ഇഷ്ടമുള്ള ഭാഗത്ത് ടൈൽ ചേർക്കുക"</string>
@@ -1570,5 +1583,7 @@
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"അജ്ഞാതം"</string>
<string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"എല്ലാ ടൈലുകളും റീസെറ്റ് ചെയ്യണോ?"</string>
<string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"എല്ലാ ദ്രുത ക്രമീകരണ ടൈലുകളും ഉപകരണത്തിന്റെ ഒറിജിനൽ ക്രമീകരണത്തിലേക്ക് റീസെറ്റ് ചെയ്യും"</string>
+ <!-- no translation found for demote_explain_text (1600426458580544250) -->
+ <skip />
<string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ml/tiles_states_strings.xml b/packages/SystemUI/res/values-ml/tiles_states_strings.xml
index a7098c9ef814..c6ba7f16fa22 100644
--- a/packages/SystemUI/res/values-ml/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-ml/tiles_states_strings.xml
@@ -56,9 +56,11 @@
<item msgid="5376619709702103243">"ഓഫാണ്"</item>
<item msgid="4875147066469902392">"ഓണാണ്"</item>
</string-array>
- <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) -->
- <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) -->
- <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) -->
+ <string-array name="tile_states_modes_dnd">
+ <item msgid="6509540227356524582">"ലഭ്യമല്ല"</item>
+ <item msgid="8589336868985358191">"ഓഫാണ്"</item>
+ <item msgid="726072717827778234">"ഓണാണ്"</item>
+ </string-array>
<string-array name="tile_states_flashlight">
<item msgid="3465257127433353857">"ലഭ്യമല്ല"</item>
<item msgid="5044688398303285224">"ഓഫാണ്"</item>
diff --git a/packages/SystemUI/res/values-mn/strings.xml b/packages/SystemUI/res/values-mn/strings.xml
index 3adddc2d27ad..abdf33125e6c 100644
--- a/packages/SystemUI/res/values-mn/strings.xml
+++ b/packages/SystemUI/res/values-mn/strings.xml
@@ -419,6 +419,11 @@
<string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Шинэ төхөөрөмж хослуулахын тулд товшино уу"</string>
<string name="hearing_devices_presets_error" msgid="350363093458408536">"Урьдчилсан тохируулгыг шинэчилж чадсангүй"</string>
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"Урьдчилсан тохируулга"</string>
+ <string name="hearing_devices_input_routing_label" msgid="730396728151232306">"Дуудлагад ашиглах өгөгдмөл микрофон"</string>
+ <string-array name="hearing_device_input_routing_options">
+ <item msgid="4582190415045337003">"Сонсголын төхөөрөмжийн микрофон"</item>
+ <item msgid="8501466270452446450">"Энэ утасны микрофон"</item>
+ </string-array>
<string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"Сонгосон"</string>
<string name="hearing_devices_ambient_label" msgid="629440938614895797">"Орчин тойрон"</string>
<string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Зүүн"</string>
@@ -582,6 +587,8 @@
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Таныг апп хуваалцаж байхад тухайн аппад харуулж эсвэл тоглуулж буй аливаа зүйл <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>-д харагдана. Тиймээс нууц үг, төлбөрийн дэлгэрэнгүй, мессеж, зураг, аудио, видео зэрэг зүйлд болгоомжтой хандаарай."</string>
<string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Дэлгэцийг хуваалцах"</string>
<string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g> энэ сонголтыг идэвхгүй болгосон"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_not_supported (4860247304058870233) -->
+ <skip />
<string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"Хуваалцах апп сонгох"</string>
<string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Дэлгэцээ дамжуулах уу?"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Нэг апп дамжуулах"</string>
@@ -794,8 +801,7 @@
<string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Асаалттай - Царайнд суурилсан"</string>
<string name="inline_done_button" msgid="6043094985588909584">"Болсон"</string>
<string name="inline_ok_button" msgid="603075490581280343">"Ашиглах"</string>
- <!-- no translation found for inline_turn_off_notifications (2653064779176881329) -->
- <skip />
+ <string name="inline_turn_off_notifications" msgid="2653064779176881329">"Унтраах"</string>
<string name="notification_silence_title" msgid="8608090968400832335">"Чимээгүй"</string>
<string name="notification_alert_title" msgid="3656229781017543655">"Өгөгдмөл"</string>
<string name="notification_automatic_title" msgid="3745465364578762652">"Автомат"</string>
@@ -977,9 +983,13 @@
<string name="right_icon" msgid="1103955040645237425">"Баруун дүрс тэмдэг"</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"Хавтан нэмэхийн тулд дараад чирэх"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"Хавтангуудыг дахин цэгцлэхийн тулд дараад чирнэ үү"</string>
+ <!-- no translation found for tap_to_position_tile (6282815817773342757) -->
+ <skip />
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"Устгахын тулд энд зөөнө үү"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"Танд хамгийн багадаа <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> хавтан шаардлагатай"</string>
<string name="qs_edit" msgid="5583565172803472437">"Засах"</string>
+ <!-- no translation found for qs_edit_tiles (2105215324060865035) -->
+ <skip />
<string name="tuner_time" msgid="2450785840990529997">"Цаг"</string>
<string-array name="clock_options">
<item msgid="3986445361435142273">"Цаг, минут, секундийг харуулах"</item>
@@ -995,6 +1005,10 @@
<string name="other" msgid="429768510980739978">"Бусад"</string>
<string name="accessibility_qs_edit_toggle_tile_size_action" msgid="1485194410119733586">"хавтангийн хэмжээг асаах/унтраах"</string>
<string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"хавтанг хасна уу"</string>
+ <!-- no translation found for accessibility_qs_edit_toggle_placement_mode (3870429389210569610) -->
+ <skip />
+ <!-- no translation found for accessibility_qs_edit_toggle_selection (2201248304072372239) -->
+ <skip />
<string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"хавтанг сүүлийн байрлалд нэмэх"</string>
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Хавтанг зөөх"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"Хавтанг хүссэн байрлалд нэмэх"</string>
@@ -1570,5 +1584,7 @@
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Тодорхойгүй"</string>
<string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"Бүх хавтанг шинэчлэх үү?"</string>
<string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Шуурхай тохиргооны бүх хавтан төхөөрөмжийн эх тохиргоо руу шинэчлэгдэнэ"</string>
+ <!-- no translation found for demote_explain_text (1600426458580544250) -->
+ <skip />
<string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
</resources>
diff --git a/packages/SystemUI/res/values-mn/tiles_states_strings.xml b/packages/SystemUI/res/values-mn/tiles_states_strings.xml
index 9b5a6e3c3037..394c42e5f3a7 100644
--- a/packages/SystemUI/res/values-mn/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-mn/tiles_states_strings.xml
@@ -56,9 +56,11 @@
<item msgid="5376619709702103243">"Унтраалттай"</item>
<item msgid="4875147066469902392">"Асаалттай"</item>
</string-array>
- <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) -->
- <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) -->
- <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) -->
+ <string-array name="tile_states_modes_dnd">
+ <item msgid="6509540227356524582">"Боломжгүй"</item>
+ <item msgid="8589336868985358191">"Унтраалттай"</item>
+ <item msgid="726072717827778234">"Асаалттай"</item>
+ </string-array>
<string-array name="tile_states_flashlight">
<item msgid="3465257127433353857">"Боломжгүй"</item>
<item msgid="5044688398303285224">"Унтраалттай"</item>
diff --git a/packages/SystemUI/res/values-mr/strings.xml b/packages/SystemUI/res/values-mr/strings.xml
index 08ef7b2e9cfc..bc80b33b312c 100644
--- a/packages/SystemUI/res/values-mr/strings.xml
+++ b/packages/SystemUI/res/values-mr/strings.xml
@@ -419,6 +419,11 @@
<string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"नवीन डिव्हाइस पेअर करण्यासाठी क्लिक करा"</string>
<string name="hearing_devices_presets_error" msgid="350363093458408536">"प्रीसेट अपडेट करता आले नाही"</string>
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"प्रीसेट"</string>
+ <string name="hearing_devices_input_routing_label" msgid="730396728151232306">"कॉलसाठी डीफॉल्ट मायक्रोफोन"</string>
+ <string-array name="hearing_device_input_routing_options">
+ <item msgid="4582190415045337003">"श्रवणयंत्र मायक्रोफोन"</item>
+ <item msgid="8501466270452446450">"या फोनचा मायक्रोफोन"</item>
+ </string-array>
<string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"निवडला आहे"</string>
<string name="hearing_devices_ambient_label" msgid="629440938614895797">"जवळपासचे"</string>
<string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"डावे"</string>
@@ -582,6 +587,7 @@
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"तुम्ही अ‍ॅप शेअर करता, तेव्हा त्या अ‍ॅपमध्ये दाखवल्या किंवा प्ले होणाऱ्या कोणत्याही गोष्टी <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> साठी दृश्यमान असतात. त्यामुळे पासवर्ड, पेमेंट तपशील, मेसेज, फोटो आणि ऑडिओ व व्हिडिओ यांसारख्या गोष्टींबाबत सावधगिरी बाळगा."</string>
<string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"स्क्रीन शेअर करा"</string>
<string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g> हा पर्याय बंद केला आहे"</string>
+ <string name="media_projection_entry_app_permission_dialog_single_app_not_supported" msgid="4860247304058870233">"ॲपद्वारे सपोर्ट नाही"</string>
<string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"शेअर करण्यासाठी अ‍ॅप निवडा"</string>
<string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"तुमची स्क्रीन कास्ट करायची आहे का?"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"एक अ‍ॅप कास्ट करा"</string>
@@ -794,8 +800,7 @@
<string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"सुरू - चेहऱ्यावर आधारित"</string>
<string name="inline_done_button" msgid="6043094985588909584">"पूर्ण झाले"</string>
<string name="inline_ok_button" msgid="603075490581280343">"लागू करा"</string>
- <!-- no translation found for inline_turn_off_notifications (2653064779176881329) -->
- <skip />
+ <string name="inline_turn_off_notifications" msgid="2653064779176881329">"बंद करा"</string>
<string name="notification_silence_title" msgid="8608090968400832335">"सायलंट"</string>
<string name="notification_alert_title" msgid="3656229781017543655">"डीफॉल्ट"</string>
<string name="notification_automatic_title" msgid="3745465364578762652">"ऑटोमॅटिक"</string>
@@ -977,9 +982,13 @@
<string name="right_icon" msgid="1103955040645237425">"उजवे आयकन"</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"टाइल जोडण्यासाठी धरून ठेवून ड्रॅग करा"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"टाइलची पुनर्रचना करण्यासाठी धरून ठेवून ड्रॅग करा"</string>
+ <!-- no translation found for tap_to_position_tile (6282815817773342757) -->
+ <skip />
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"काढण्यासाठी येथे ड्रॅग करा"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"तुम्हाला किमान <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> टाइलची गरज आहे"</string>
<string name="qs_edit" msgid="5583565172803472437">"संपादित करा"</string>
+ <!-- no translation found for qs_edit_tiles (2105215324060865035) -->
+ <skip />
<string name="tuner_time" msgid="2450785840990529997">"वेळ"</string>
<string-array name="clock_options">
<item msgid="3986445361435142273">"तास, मिनिटे आणि सेकंद दर्शवा"</item>
@@ -995,6 +1004,10 @@
<string name="other" msgid="429768510980739978">"अन्य"</string>
<string name="accessibility_qs_edit_toggle_tile_size_action" msgid="1485194410119733586">"टाइलचा आकार टॉगल करा"</string>
<string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"टाइल काढून टाका"</string>
+ <!-- no translation found for accessibility_qs_edit_toggle_placement_mode (3870429389210569610) -->
+ <skip />
+ <!-- no translation found for accessibility_qs_edit_toggle_selection (2201248304072372239) -->
+ <skip />
<string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"टाइल शेवटच्या स्थानावर जोडा"</string>
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"टाइल हलवा"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"हव्या असलेल्या स्थानावर टाइल जोडा"</string>
@@ -1570,5 +1583,7 @@
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"अज्ञात"</string>
<string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"सर्व टाइल रीसेट करायच्या?"</string>
<string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"सर्व क्विक सेटिंग्ज टाइल डिव्हाइसच्या मूळ सेटिंग्जवर रीसेट केल्या जातील"</string>
+ <!-- no translation found for demote_explain_text (1600426458580544250) -->
+ <skip />
<string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
</resources>
diff --git a/packages/SystemUI/res/values-mr/tiles_states_strings.xml b/packages/SystemUI/res/values-mr/tiles_states_strings.xml
index a5930d9ff295..4809c740406a 100644
--- a/packages/SystemUI/res/values-mr/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-mr/tiles_states_strings.xml
@@ -56,9 +56,11 @@
<item msgid="5376619709702103243">"बंद आहे"</item>
<item msgid="4875147066469902392">"सुरू आहे"</item>
</string-array>
- <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) -->
- <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) -->
- <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) -->
+ <string-array name="tile_states_modes_dnd">
+ <item msgid="6509540227356524582">"उपलब्ध नाही"</item>
+ <item msgid="8589336868985358191">"बंद आहे"</item>
+ <item msgid="726072717827778234">"सुरू आहे"</item>
+ </string-array>
<string-array name="tile_states_flashlight">
<item msgid="3465257127433353857">"उपलब्ध नाही"</item>
<item msgid="5044688398303285224">"बंद आहे"</item>
diff --git a/packages/SystemUI/res/values-ms/strings.xml b/packages/SystemUI/res/values-ms/strings.xml
index 5aec75608f76..14c7197aa9e4 100644
--- a/packages/SystemUI/res/values-ms/strings.xml
+++ b/packages/SystemUI/res/values-ms/strings.xml
@@ -419,6 +419,11 @@
<string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Klik untuk menggandingkan peranti baharu"</string>
<string name="hearing_devices_presets_error" msgid="350363093458408536">"Tidak dapat mengemaskinikan pratetapan"</string>
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"Pratetapan"</string>
+ <string name="hearing_devices_input_routing_label" msgid="730396728151232306">"Mikrofon lalai untuk panggilan"</string>
+ <string-array name="hearing_device_input_routing_options">
+ <item msgid="4582190415045337003">"Mikrofon alat bantu pendengaran"</item>
+ <item msgid="8501466270452446450">"Mikrofon telefon ini"</item>
+ </string-array>
<string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"Dipilih"</string>
<string name="hearing_devices_ambient_label" msgid="629440938614895797">"Persekitaran"</string>
<string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Kiri"</string>
@@ -582,6 +587,7 @@
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Apabila anda berkongsi apl, apa-apa sahaja kandungan yang dipaparkan atau dimainkan dalam apl tersebut boleh dilihat oleh <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Oleh hal yang demikian, berhati-hati dengan perkara seperti kata laluan, butiran pembayaran, mesej, foto dan audio serta video."</string>
<string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Kongsi skrin"</string>
<string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g> telah melumpuhkan pilihan ini"</string>
+ <string name="media_projection_entry_app_permission_dialog_single_app_not_supported" msgid="4860247304058870233">"Tidak disokong oleh apl"</string>
<string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"Pilih apl untuk dikongsi"</string>
<string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Hantar skrin anda?"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Hantar satu apl"</string>
@@ -794,8 +800,7 @@
<string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Hidup - Berasaskan wajah"</string>
<string name="inline_done_button" msgid="6043094985588909584">"Selesai"</string>
<string name="inline_ok_button" msgid="603075490581280343">"Guna"</string>
- <!-- no translation found for inline_turn_off_notifications (2653064779176881329) -->
- <skip />
+ <string name="inline_turn_off_notifications" msgid="2653064779176881329">"Matikan"</string>
<string name="notification_silence_title" msgid="8608090968400832335">"Senyap"</string>
<string name="notification_alert_title" msgid="3656229781017543655">"Lalai"</string>
<string name="notification_automatic_title" msgid="3745465364578762652">"Automatik"</string>
@@ -977,9 +982,13 @@
<string name="right_icon" msgid="1103955040645237425">"Ikon kanan"</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"Tahan dan seret untuk menambahkan jubin"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"Tahan dan seret untuk mengatur semula jubin"</string>
+ <!-- no translation found for tap_to_position_tile (6282815817773342757) -->
+ <skip />
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"Seret ke sini untuk mengalih keluar"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"Anda memerlukan sekurang-kurangnya <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> jubin"</string>
<string name="qs_edit" msgid="5583565172803472437">"Edit"</string>
+ <!-- no translation found for qs_edit_tiles (2105215324060865035) -->
+ <skip />
<string name="tuner_time" msgid="2450785840990529997">"Masa"</string>
<string-array name="clock_options">
<item msgid="3986445361435142273">"Tunjukkan jam, minit dan saat"</item>
@@ -995,6 +1004,10 @@
<string name="other" msgid="429768510980739978">"Lain-lain"</string>
<string name="accessibility_qs_edit_toggle_tile_size_action" msgid="1485194410119733586">"togol saiz jubin"</string>
<string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"alih keluar jubin"</string>
+ <!-- no translation found for accessibility_qs_edit_toggle_placement_mode (3870429389210569610) -->
+ <skip />
+ <!-- no translation found for accessibility_qs_edit_toggle_selection (2201248304072372239) -->
+ <skip />
<string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"tambahkan jubin pada kedudukan terakhir"</string>
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Alihkan jubin"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"Tambahkan jubin pada kedudukan yang dikehendaki"</string>
@@ -1570,5 +1583,7 @@
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Tidak diketahui"</string>
<string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"Tetapkan semula semua jubin?"</string>
<string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Semua jubin Tetapan Pantas akan ditetapkan semula kepada tetapan asal peranti"</string>
+ <!-- no translation found for demote_explain_text (1600426458580544250) -->
+ <skip />
<string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ms/tiles_states_strings.xml b/packages/SystemUI/res/values-ms/tiles_states_strings.xml
index eca55a34e028..88544435c076 100644
--- a/packages/SystemUI/res/values-ms/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-ms/tiles_states_strings.xml
@@ -56,9 +56,11 @@
<item msgid="5376619709702103243">"Mati"</item>
<item msgid="4875147066469902392">"Hidup"</item>
</string-array>
- <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) -->
- <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) -->
- <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) -->
+ <string-array name="tile_states_modes_dnd">
+ <item msgid="6509540227356524582">"Tidak tersedia"</item>
+ <item msgid="8589336868985358191">"Mati"</item>
+ <item msgid="726072717827778234">"Hidup"</item>
+ </string-array>
<string-array name="tile_states_flashlight">
<item msgid="3465257127433353857">"Tidak tersedia"</item>
<item msgid="5044688398303285224">"Mati"</item>
diff --git a/packages/SystemUI/res/values-my/strings.xml b/packages/SystemUI/res/values-my/strings.xml
index 98f937b377e1..9849dd9d6219 100644
--- a/packages/SystemUI/res/values-my/strings.xml
+++ b/packages/SystemUI/res/values-my/strings.xml
@@ -419,6 +419,11 @@
<string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"စက်အသစ် တွဲချိတ်ရန် နှိပ်ပါ"</string>
<string name="hearing_devices_presets_error" msgid="350363093458408536">"အသင့်သုံးကို အပ်ဒိတ်လုပ်၍မရပါ"</string>
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"ကြိုတင်သတ်မှတ်ချက်"</string>
+ <string name="hearing_devices_input_routing_label" msgid="730396728151232306">"ခေါ်ဆိုမှုများအတွက် မူရင်းမိုက်ခရိုဖုန်း"</string>
+ <string-array name="hearing_device_input_routing_options">
+ <item msgid="4582190415045337003">"နားကြားကိရိယာ မိုက်ခရိုဖုန်း"</item>
+ <item msgid="8501466270452446450">"ဤဖုန်း၏ မိုက်ခရိုဖုန်း"</item>
+ </string-array>
<string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"ရွေးထားသည်"</string>
<string name="hearing_devices_ambient_label" msgid="629440938614895797">"ဝန်းကျင်အသံ"</string>
<string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"ဘယ်"</string>
@@ -582,6 +587,8 @@
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"အက်ပ်ကို မျှဝေနေချိန်တွင် ယင်းအက်ပ်တွင် ပြထားသော (သို့) ဖွင့်ထားသော အရာအားလုံးကို <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> က မြင်နိုင်သည်။ စကားဝှက်၊ ငွေပေးချေမှု အချက်အလက်၊ မက်ဆေ့ဂျ်၊ ဓာတ်ပုံ၊ အသံနှင့် ဗီဒီယိုကဲ့သို့ အရာများကို ဂရုစိုက်ပါ။"</string>
<string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"စခရင် မျှဝေရန်"</string>
<string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g> သည် ဤရွေးစရာကို ပိတ်ထားသည်"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_not_supported (4860247304058870233) -->
+ <skip />
<string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"မျှဝေရန် အက်ပ်ရွေးခြင်း"</string>
<string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"သင့်ဖန်သားပြင်ကို ကာစ်လုပ်မလား။"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"အက်ပ်တစ်ခုကို ကာစ်လုပ်ရန်"</string>
@@ -794,8 +801,7 @@
<string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"ဖွင့် - မျက်နှာအခြေခံ"</string>
<string name="inline_done_button" msgid="6043094985588909584">"ပြီးပြီ"</string>
<string name="inline_ok_button" msgid="603075490581280343">"အသုံးပြုရန်"</string>
- <!-- no translation found for inline_turn_off_notifications (2653064779176881329) -->
- <skip />
+ <string name="inline_turn_off_notifications" msgid="2653064779176881329">"ပိတ်ရန်"</string>
<string name="notification_silence_title" msgid="8608090968400832335">"အသံပိတ်ရန်"</string>
<string name="notification_alert_title" msgid="3656229781017543655">"မူလ"</string>
<string name="notification_automatic_title" msgid="3745465364578762652">"အလိုအလျောက်"</string>
@@ -977,9 +983,13 @@
<string name="right_icon" msgid="1103955040645237425">"လက်ယာသင်္ကေတ"</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"အကွက်များထည့်ရန် ဖိဆွဲပါ"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"အကွက်ငယ်များ ပြန်စီစဉ်ရန် ဖိပြီးဆွဲပါ"</string>
+ <!-- no translation found for tap_to_position_tile (6282815817773342757) -->
+ <skip />
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"ဖယ်ရှားရန် ဤနေရာသို့ဖိဆွဲပါ"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"အနည်းဆုံး <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> ကွက် ရှိရမည်"</string>
<string name="qs_edit" msgid="5583565172803472437">"တည်းဖြတ်ရန်"</string>
+ <!-- no translation found for qs_edit_tiles (2105215324060865035) -->
+ <skip />
<string name="tuner_time" msgid="2450785840990529997">"အချိန်"</string>
<string-array name="clock_options">
<item msgid="3986445361435142273">"နာရီ၊ မိနစ်နှင့် စက္ကန့်ကိုပြပါ"</item>
@@ -995,6 +1005,10 @@
<string name="other" msgid="429768510980739978">"အခြား"</string>
<string name="accessibility_qs_edit_toggle_tile_size_action" msgid="1485194410119733586">"အကွက်ငယ်၏ အရွယ်အစားကို ပြောင်းရန်"</string>
<string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"အကွက်ငယ်ကို ဖယ်ရှားရန်"</string>
+ <!-- no translation found for accessibility_qs_edit_toggle_placement_mode (3870429389210569610) -->
+ <skip />
+ <!-- no translation found for accessibility_qs_edit_toggle_selection (2201248304072372239) -->
+ <skip />
<string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"နောက်ဆုံးနေရာတွင် အကွက်ငယ် ထည့်ရန်"</string>
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"အကွက်ငယ်ကို ရွှေ့ရန်"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"ဆန္ဒရှိရာ နေရာတွင် အကွက်ငယ် ထည့်ရန်"</string>
@@ -1570,5 +1584,7 @@
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"အမျိုးအမည်မသိ"</string>
<string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"အကွက်ငယ်အားလုံးကို ပြင်ဆင်သတ်မှတ်မလား။"</string>
<string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"အမြန်ဆက်တင်များ အကွက်ငယ်အားလုံးကို စက်ပစ္စည်း၏ မူရင်းဆက်တင်များသို့ ပြင်ဆင်သတ်မှတ်ပါမည်"</string>
+ <!-- no translation found for demote_explain_text (1600426458580544250) -->
+ <skip />
<string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>၊ <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
</resources>
diff --git a/packages/SystemUI/res/values-my/tiles_states_strings.xml b/packages/SystemUI/res/values-my/tiles_states_strings.xml
index 2bd239043dea..75221239f686 100644
--- a/packages/SystemUI/res/values-my/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-my/tiles_states_strings.xml
@@ -56,9 +56,11 @@
<item msgid="5376619709702103243">"ပိတ်"</item>
<item msgid="4875147066469902392">"ဖွင့်"</item>
</string-array>
- <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) -->
- <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) -->
- <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) -->
+ <string-array name="tile_states_modes_dnd">
+ <item msgid="6509540227356524582">"မရနိုင်ပါ"</item>
+ <item msgid="8589336868985358191">"ပိတ်"</item>
+ <item msgid="726072717827778234">"ဖွင့်"</item>
+ </string-array>
<string-array name="tile_states_flashlight">
<item msgid="3465257127433353857">"မရနိုင်ပါ"</item>
<item msgid="5044688398303285224">"ပိတ်"</item>
diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml
index 627ab44fba26..62c018bf68d9 100644
--- a/packages/SystemUI/res/values-nb/strings.xml
+++ b/packages/SystemUI/res/values-nb/strings.xml
@@ -419,6 +419,11 @@
<string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Klikk for å koble til en ny enhet"</string>
<string name="hearing_devices_presets_error" msgid="350363093458408536">"Kunne ikke oppdatere forhåndsinnstillingen"</string>
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"Forhåndsinnstilling"</string>
+ <string name="hearing_devices_input_routing_label" msgid="730396728151232306">"Standardmikrofonen for anrop"</string>
+ <string-array name="hearing_device_input_routing_options">
+ <item msgid="4582190415045337003">"Mikrofonen i høreapparatet"</item>
+ <item msgid="8501466270452446450">"Mikrofonen i denne telefonen"</item>
+ </string-array>
<string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"Valgt"</string>
<string name="hearing_devices_ambient_label" msgid="629440938614895797">"Omgivelser"</string>
<string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Venstre"</string>
@@ -582,6 +587,8 @@
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Når du deler en app, er alt som vises eller spilles av i appen, synlig for <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Derfor bør du være forsiktig med for eksempel passord, betalingsopplysninger, meldinger, bilder, lyd og video."</string>
<string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Del skjermen"</string>
<string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g> har deaktivert dette alternativet"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_not_supported (4860247304058870233) -->
+ <skip />
<string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"Velg app å dele"</string>
<string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Vil du caste skjermen?"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Cast én app"</string>
@@ -794,8 +801,7 @@
<string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"På – ansiktsbasert"</string>
<string name="inline_done_button" msgid="6043094985588909584">"Ferdig"</string>
<string name="inline_ok_button" msgid="603075490581280343">"Bruk"</string>
- <!-- no translation found for inline_turn_off_notifications (2653064779176881329) -->
- <skip />
+ <string name="inline_turn_off_notifications" msgid="2653064779176881329">"Slå av"</string>
<string name="notification_silence_title" msgid="8608090968400832335">"Lydløs"</string>
<string name="notification_alert_title" msgid="3656229781017543655">"Standard"</string>
<string name="notification_automatic_title" msgid="3745465364578762652">"Automatisk"</string>
@@ -977,9 +983,13 @@
<string name="right_icon" msgid="1103955040645237425">"Høyre-ikon"</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"Hold og dra for å legge til brikker"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"Hold og dra for å flytte på rutene"</string>
+ <!-- no translation found for tap_to_position_tile (6282815817773342757) -->
+ <skip />
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"Dra hit for å fjerne"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"Du trenger minst <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> infobrikker"</string>
<string name="qs_edit" msgid="5583565172803472437">"Endre"</string>
+ <!-- no translation found for qs_edit_tiles (2105215324060865035) -->
+ <skip />
<string name="tuner_time" msgid="2450785840990529997">"Tid"</string>
<string-array name="clock_options">
<item msgid="3986445361435142273">"Vis timer, minutter og sekunder"</item>
@@ -995,6 +1005,10 @@
<string name="other" msgid="429768510980739978">"Annet"</string>
<string name="accessibility_qs_edit_toggle_tile_size_action" msgid="1485194410119733586">"bytt størrelse på brikken"</string>
<string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"fjerne infobrikken"</string>
+ <!-- no translation found for accessibility_qs_edit_toggle_placement_mode (3870429389210569610) -->
+ <skip />
+ <!-- no translation found for accessibility_qs_edit_toggle_selection (2201248304072372239) -->
+ <skip />
<string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"legge til en brikke på den siste posisjonen"</string>
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Flytt infobrikken"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"Legg til brikken på ønsket posisjon"</string>
@@ -1570,5 +1584,7 @@
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Ukjent"</string>
<string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"Vil du tilbakestille alle brikkene?"</string>
<string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Alle brikker for hurtiginnstillinger tilbakestilles til enhetens opprinnelige innstillinger"</string>
+ <!-- no translation found for demote_explain_text (1600426458580544250) -->
+ <skip />
<string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
</resources>
diff --git a/packages/SystemUI/res/values-nb/tiles_states_strings.xml b/packages/SystemUI/res/values-nb/tiles_states_strings.xml
index fca868e98240..975c1c1c0b78 100644
--- a/packages/SystemUI/res/values-nb/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-nb/tiles_states_strings.xml
@@ -56,9 +56,11 @@
<item msgid="5376619709702103243">"Av"</item>
<item msgid="4875147066469902392">"På"</item>
</string-array>
- <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) -->
- <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) -->
- <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) -->
+ <string-array name="tile_states_modes_dnd">
+ <item msgid="6509540227356524582">"Utilgjengelig"</item>
+ <item msgid="8589336868985358191">"Av"</item>
+ <item msgid="726072717827778234">"På"</item>
+ </string-array>
<string-array name="tile_states_flashlight">
<item msgid="3465257127433353857">"Utilgjengelig"</item>
<item msgid="5044688398303285224">"Av"</item>
diff --git a/packages/SystemUI/res/values-ne/strings.xml b/packages/SystemUI/res/values-ne/strings.xml
index 6090ca509036..c10163883409 100644
--- a/packages/SystemUI/res/values-ne/strings.xml
+++ b/packages/SystemUI/res/values-ne/strings.xml
@@ -419,6 +419,11 @@
<string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"नयाँ डिभाइसमा कनेक्ट गर्न क्लिक गर्नुहोस्"</string>
<string name="hearing_devices_presets_error" msgid="350363093458408536">"प्रिसेट अपडेट गर्न सकिएन"</string>
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"पूर्वनिर्धारित"</string>
+ <string name="hearing_devices_input_routing_label" msgid="730396728151232306">"कलका लागि प्रयोग गरिने डिफल्ट माइक्रोफोन"</string>
+ <string-array name="hearing_device_input_routing_options">
+ <item msgid="4582190415045337003">"श्रवण यन्त्रको माइक्रोफोन"</item>
+ <item msgid="8501466270452446450">"यो फोनको माइक्रोफोन"</item>
+ </string-array>
<string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"चयन गरिएको छ"</string>
<string name="hearing_devices_ambient_label" msgid="629440938614895797">"वरपरका आवाज"</string>
<string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"बायाँ"</string>
@@ -582,6 +587,8 @@
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"तपाईंले यो एप सेयर गरिरहेका बेला यो एपमा देखाइने वा प्ले गरिने सबै सामग्री <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> मा देखिन्छ। त्यसैले पासवर्ड, भुक्तानीसम्बन्धी विवरण, म्यासेज, फोटो र अडियो तथा भिडियो जस्ता कुरा हेर्दा वा प्ले गर्दा सावधानी अपनाउनुहोला।"</string>
<string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"स्क्रिन सेयर गर्नुहोस्"</string>
<string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g> ले यो विकल्प अफ गर्नुभएको छ"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_not_supported (4860247304058870233) -->
+ <skip />
<string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"सेयर गर्न छनौट गर्नुहोस्"</string>
<string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"स्क्रिन कास्ट गर्ने हो?"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"एउटा एप कास्ट गर्नुहोस्"</string>
@@ -794,8 +801,7 @@
<string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"अन छ - अनुहारमा आधारित"</string>
<string name="inline_done_button" msgid="6043094985588909584">"सम्पन्न भयो"</string>
<string name="inline_ok_button" msgid="603075490581280343">"लागू गर्नुहोस्"</string>
- <!-- no translation found for inline_turn_off_notifications (2653064779176881329) -->
- <skip />
+ <string name="inline_turn_off_notifications" msgid="2653064779176881329">"अफ गर्नुहोस्"</string>
<string name="notification_silence_title" msgid="8608090968400832335">"साइलेन्ट"</string>
<string name="notification_alert_title" msgid="3656229781017543655">"डिफल्ट"</string>
<string name="notification_automatic_title" msgid="3745465364578762652">"स्वचालित"</string>
@@ -977,9 +983,13 @@
<string name="right_icon" msgid="1103955040645237425">"दायाँतिरको आइकन"</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"टाइलहरू थप्न होल्ड गरी ड्र्याग गर्नुहोस्"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"टाइलहरू पुनः क्रमबद्ध गर्न होल्ड गरी ड्र्याग गर्नुहोस्"</string>
+ <!-- no translation found for tap_to_position_tile (6282815817773342757) -->
+ <skip />
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"हटाउनका लागि यहाँ तान्नुहोस्"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"तपाईंलाई कम्तीमा <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> वटा टाइल चाहिन्छ"</string>
<string name="qs_edit" msgid="5583565172803472437">"सम्पादन गर्नुहोस्"</string>
+ <!-- no translation found for qs_edit_tiles (2105215324060865035) -->
+ <skip />
<string name="tuner_time" msgid="2450785840990529997">"समय"</string>
<string-array name="clock_options">
<item msgid="3986445361435142273">"घन्टा, मिनेट, र सेकेन्ड देखाउनुहोस्"</item>
@@ -995,6 +1005,10 @@
<string name="other" msgid="429768510980739978">"अन्य"</string>
<string name="accessibility_qs_edit_toggle_tile_size_action" msgid="1485194410119733586">"टाइलको आकार टगल गर्नुहोस्"</string>
<string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"टाइल हटाउनुहोस्"</string>
+ <!-- no translation found for accessibility_qs_edit_toggle_placement_mode (3870429389210569610) -->
+ <skip />
+ <!-- no translation found for accessibility_qs_edit_toggle_selection (2201248304072372239) -->
+ <skip />
<string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"अन्तिम स्थानमा टाइल हाल्नुहोस्"</string>
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"टाइल सार्नुहोस्"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"आफूले चाहेको स्थानमा टाइल हाल्नुहोस्"</string>
@@ -1570,5 +1584,7 @@
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"अज्ञात"</string>
<string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"सबै टाइलहरू रिसेट गर्ने हो?"</string>
<string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"द्रुत सेटिङका सबै टाइलहरू रिसेट गरी डिभाइसका मूल सेटिङ लागू गरिने छन्"</string>
+ <!-- no translation found for demote_explain_text (1600426458580544250) -->
+ <skip />
<string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ne/tiles_states_strings.xml b/packages/SystemUI/res/values-ne/tiles_states_strings.xml
index 71f415a3d0ef..f48daa32df9e 100644
--- a/packages/SystemUI/res/values-ne/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-ne/tiles_states_strings.xml
@@ -56,9 +56,11 @@
<item msgid="5376619709702103243">"अफ छ"</item>
<item msgid="4875147066469902392">"अन छ"</item>
</string-array>
- <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) -->
- <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) -->
- <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) -->
+ <string-array name="tile_states_modes_dnd">
+ <item msgid="6509540227356524582">"उपलब्ध छैन"</item>
+ <item msgid="8589336868985358191">"अफ छ"</item>
+ <item msgid="726072717827778234">"अन छ"</item>
+ </string-array>
<string-array name="tile_states_flashlight">
<item msgid="3465257127433353857">"उपलब्ध छैन"</item>
<item msgid="5044688398303285224">"अफ छ"</item>
diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml
index ebe9ad475229..82d29de64116 100644
--- a/packages/SystemUI/res/values-nl/strings.xml
+++ b/packages/SystemUI/res/values-nl/strings.xml
@@ -419,6 +419,11 @@
<string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Klik om nieuw apparaat te koppelen"</string>
<string name="hearing_devices_presets_error" msgid="350363093458408536">"Kan voorinstelling niet updaten"</string>
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"Voorinstelling"</string>
+ <string name="hearing_devices_input_routing_label" msgid="730396728151232306">"Standaardmicrofoon voor gesprekken"</string>
+ <string-array name="hearing_device_input_routing_options">
+ <item msgid="4582190415045337003">"Microfoon voor hoortoestel"</item>
+ <item msgid="8501466270452446450">"Microfoon van deze telefoon"</item>
+ </string-array>
<string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"Geselecteerd"</string>
<string name="hearing_devices_ambient_label" msgid="629440938614895797">"Omgevingsgeluid"</string>
<string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Links"</string>
@@ -582,6 +587,8 @@
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Als je een app deelt, is alles dat wordt getoond of afgespeeld in die app zichtbaar voor <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Wees daarom voorzichtig met bijvoorbeeld wachtwoorden, betalingsgegevens, berichten, foto\'s, en audio en video."</string>
<string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Scherm delen"</string>
<string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"Voor <xliff:g id="APP_NAME">%1$s</xliff:g> staat deze optie uit"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_not_supported (4860247304058870233) -->
+ <skip />
<string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"App kiezen om te delen"</string>
<string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Je scherm casten?"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Eén app casten"</string>
@@ -794,8 +801,7 @@
<string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Aan: op basis van gezicht"</string>
<string name="inline_done_button" msgid="6043094985588909584">"Klaar"</string>
<string name="inline_ok_button" msgid="603075490581280343">"Toepassen"</string>
- <!-- no translation found for inline_turn_off_notifications (2653064779176881329) -->
- <skip />
+ <string name="inline_turn_off_notifications" msgid="2653064779176881329">"Uitzetten"</string>
<string name="notification_silence_title" msgid="8608090968400832335">"Stil"</string>
<string name="notification_alert_title" msgid="3656229781017543655">"Standaard"</string>
<string name="notification_automatic_title" msgid="3745465364578762652">"Automatisch"</string>
@@ -977,9 +983,13 @@
<string name="right_icon" msgid="1103955040645237425">"Icoon rechts"</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"Houd een tegel ingedrukt en sleep om die toe te voegen"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"Houd een tegel ingedrukt en sleep om die te verplaatsen"</string>
+ <!-- no translation found for tap_to_position_tile (6282815817773342757) -->
+ <skip />
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"Sleep hier naartoe om te verwijderen"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"Je hebt minimaal <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> tegels nodig"</string>
<string name="qs_edit" msgid="5583565172803472437">"Bewerken"</string>
+ <!-- no translation found for qs_edit_tiles (2105215324060865035) -->
+ <skip />
<string name="tuner_time" msgid="2450785840990529997">"Tijd"</string>
<string-array name="clock_options">
<item msgid="3986445361435142273">"Uren, minuten en seconden tonen"</item>
@@ -995,6 +1005,10 @@
<string name="other" msgid="429768510980739978">"Overig"</string>
<string name="accessibility_qs_edit_toggle_tile_size_action" msgid="1485194410119733586">"het formaat van de tegel schakelen"</string>
<string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"tegel verwijderen"</string>
+ <!-- no translation found for accessibility_qs_edit_toggle_placement_mode (3870429389210569610) -->
+ <skip />
+ <!-- no translation found for accessibility_qs_edit_toggle_selection (2201248304072372239) -->
+ <skip />
<string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"tegel toevoegen op de laatste positie"</string>
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Tegel verplaatsen"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"Tegel toevoegen op gewenste positie"</string>
@@ -1570,5 +1584,7 @@
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Onbekend"</string>
<string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"Alle tegels resetten?"</string>
<string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Alle tegels voor Snelle instellingen worden teruggezet naar de oorspronkelijke instellingen van het apparaat"</string>
+ <!-- no translation found for demote_explain_text (1600426458580544250) -->
+ <skip />
<string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
</resources>
diff --git a/packages/SystemUI/res/values-nl/tiles_states_strings.xml b/packages/SystemUI/res/values-nl/tiles_states_strings.xml
index 01de4cbc574f..b739fa2211b3 100644
--- a/packages/SystemUI/res/values-nl/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-nl/tiles_states_strings.xml
@@ -56,9 +56,11 @@
<item msgid="5376619709702103243">"Uit"</item>
<item msgid="4875147066469902392">"Aan"</item>
</string-array>
- <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) -->
- <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) -->
- <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) -->
+ <string-array name="tile_states_modes_dnd">
+ <item msgid="6509540227356524582">"Niet beschikbaar"</item>
+ <item msgid="8589336868985358191">"Uit"</item>
+ <item msgid="726072717827778234">"Aan"</item>
+ </string-array>
<string-array name="tile_states_flashlight">
<item msgid="3465257127433353857">"Niet beschikbaar"</item>
<item msgid="5044688398303285224">"Uit"</item>
diff --git a/packages/SystemUI/res/values-or/strings.xml b/packages/SystemUI/res/values-or/strings.xml
index 8569c7aaad2e..a93508f2a3f3 100644
--- a/packages/SystemUI/res/values-or/strings.xml
+++ b/packages/SystemUI/res/values-or/strings.xml
@@ -419,6 +419,11 @@
<string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"ନୂଆ ଡିଭାଇସ ପେୟାର କରିବାକୁ କ୍ଲିକ କରନ୍ତୁ"</string>
<string name="hearing_devices_presets_error" msgid="350363093458408536">"ପ୍ରିସେଟକୁ ଅପଡେଟ କରାଯାଇପାରିଲା ନାହିଁ"</string>
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"ପ୍ରିସେଟ"</string>
+ <string name="hearing_devices_input_routing_label" msgid="730396728151232306">"କଲ ପାଇଁ ଡିଫଲ୍ଟ ମାଇକ୍ରୋଫୋନ"</string>
+ <string-array name="hearing_device_input_routing_options">
+ <item msgid="4582190415045337003">"ଶ୍ରବଣ ଯନ୍ତ୍ରର ମାଇକ୍ରୋଫୋନ"</item>
+ <item msgid="8501466270452446450">"ଏହି ଫୋନର ମାଇକ୍ରୋଫୋନ"</item>
+ </string-array>
<string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"ଚୟନ କରାଯାଇଛି"</string>
<string name="hearing_devices_ambient_label" msgid="629440938614895797">"ପରିପାର୍ଶ୍ୱ"</string>
<string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"ବାମ"</string>
@@ -582,6 +587,7 @@
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"ଆପଣ ଏକ ଆପ ସେୟାର କରିବା ସମୟରେ, ସେହି ଆପରେ ଦେଖାଯାଉଥିବା କିମ୍ବା ପ୍ଲେ ହେଉଥିବା ସବୁକିଛି <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>କୁ ଦେଖାଯାଏ। ତେଣୁ ପାସୱାର୍ଡ, ପେମେଣ୍ଟ ବିବରଣୀ, ମେସେଜ, ଫଟୋ ଏବଂ ଅଡିଓ ଓ ଭିଡିଓ ପରି ବିଷୟଗୁଡ଼ିକ ପ୍ରତି ସତର୍କ ରୁହନ୍ତୁ।"</string>
<string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"ସ୍କ୍ରିନ ସେୟାର କରନ୍ତୁ"</string>
<string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g> ଏହି ବିକଳ୍ପକୁ ଅକ୍ଷମ କରିଛି"</string>
+ <string name="media_projection_entry_app_permission_dialog_single_app_not_supported" msgid="4860247304058870233">"ଆପରେ ସପୋର୍ଟ କରୁନାହିଁ"</string>
<string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"ସେୟାର କରିବାକୁ ଆପ ବାଛନ୍ତୁ"</string>
<string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"ଆପଣଙ୍କ ସ୍କ୍ରିନକୁ କାଷ୍ଟ କରିବେ?"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"ଗୋଟିଏ ଆପକୁ କାଷ୍ଟ କରନ୍ତୁ"</string>
@@ -794,8 +800,7 @@
<string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"ଚାଲୁ ଅଛି - ଫେସ-ଆଧାରିତ"</string>
<string name="inline_done_button" msgid="6043094985588909584">"ହୋଇଗଲା"</string>
<string name="inline_ok_button" msgid="603075490581280343">"ଲାଗୁ କରନ୍ତୁ"</string>
- <!-- no translation found for inline_turn_off_notifications (2653064779176881329) -->
- <skip />
+ <string name="inline_turn_off_notifications" msgid="2653064779176881329">"ବନ୍ଦ କରନ୍ତୁ"</string>
<string name="notification_silence_title" msgid="8608090968400832335">"ନୀରବ"</string>
<string name="notification_alert_title" msgid="3656229781017543655">"ଡିଫଲ୍ଟ"</string>
<string name="notification_automatic_title" msgid="3745465364578762652">"ସ୍ୱଚାଳିତ"</string>
@@ -977,9 +982,13 @@
<string name="right_icon" msgid="1103955040645237425">"ଡାହାଣ ଆଇକନ୍"</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"ଟାଇଲ୍ ଯୋଗ କରିବା ପାଇଁ ଦାବିଧରି ଡ୍ରାଗ କରନ୍ତୁ"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"ଟାଇଲ ପୁଣି ସଜାଇବାକୁ ଦାବିଧରି ଟାଣନ୍ତୁ"</string>
+ <!-- no translation found for tap_to_position_tile (6282815817773342757) -->
+ <skip />
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"ବାହାର କରିବାକୁ ଏଠାକୁ ଡ୍ରାଗ୍‍ କରନ୍ତୁ"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"ଆପଣଙ୍କର ଅତିକମ୍‌ରେ <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g>ଟି ଟାଇଲ୍ ଆବଶ୍ୟକ"</string>
<string name="qs_edit" msgid="5583565172803472437">"ଏଡିଟ କରନ୍ତୁ"</string>
+ <!-- no translation found for qs_edit_tiles (2105215324060865035) -->
+ <skip />
<string name="tuner_time" msgid="2450785840990529997">"ସମୟ"</string>
<string-array name="clock_options">
<item msgid="3986445361435142273">"ଘଣ୍ଟା, ମିନିଟ୍‍ ଏବଂ ସେକେଣ୍ଡ ଦେଖାନ୍ତୁ"</item>
@@ -995,6 +1004,10 @@
<string name="other" msgid="429768510980739978">"ଅନ୍ୟ"</string>
<string name="accessibility_qs_edit_toggle_tile_size_action" msgid="1485194410119733586">"ଟାଇଲର ସାଇଜକୁ ଟୋଗଲ କରନ୍ତୁ"</string>
<string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"ଟାଇଲ୍ କାଢ଼ି ଦିଅନ୍ତୁ"</string>
+ <!-- no translation found for accessibility_qs_edit_toggle_placement_mode (3870429389210569610) -->
+ <skip />
+ <!-- no translation found for accessibility_qs_edit_toggle_selection (2201248304072372239) -->
+ <skip />
<string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"ଶେଷ ପୋଜିସନରେ ଟାଇଲ ଯୋଗ କରିବା"</string>
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"ଟାଇଲ୍ ମୁଭ୍ କରନ୍ତୁ"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"ଇଚ୍ଛିତ ପୋଜିସନରେ ଟାଇଲ ଯୋଗ କରନ୍ତୁ"</string>
@@ -1249,7 +1262,7 @@
<string name="media_output_dialog_accessibility_title" msgid="4681741064190167888">"ଅଡିଓ ଆଉଟପୁଟ ପାଇଁ ଉପଲବ୍ଧ ଡିଭାଇସଗୁଡ଼ିକ।"</string>
<string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"ଭଲ୍ୟୁମ"</string>
<string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string>
- <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"ସ୍ପିକର ଏବଂ ଡିସପ୍ଲେଗୁଡ଼ିକ"</string>
+ <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"ସ୍ପିକର ଏବଂ ଡିସପ୍ଲେ"</string>
<string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"ପ୍ରସ୍ତାବିତ ଡିଭାଇସଗୁଡ଼ିକ"</string>
<string name="media_input_group_title" msgid="2057057473860783021">"ଇନପୁଟ"</string>
<string name="media_output_group_title" msgid="6789001895863332576">"ଆଉଟପୁଟ"</string>
@@ -1570,5 +1583,7 @@
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"ଅଜଣା"</string>
<string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"ସମସ୍ତ ଟାଇଲକୁ ରିସେଟ କରିବେ?"</string>
<string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"ସମସ୍ତ କୁଇକ ସେଟିଂସ ଟାଇଲ ଡିଭାଇସର ମୂଳ ସେଟିଂସରେ ରିସେଟ ହୋଇଯିବ"</string>
+ <!-- no translation found for demote_explain_text (1600426458580544250) -->
+ <skip />
<string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
</resources>
diff --git a/packages/SystemUI/res/values-or/tiles_states_strings.xml b/packages/SystemUI/res/values-or/tiles_states_strings.xml
index 37d3c95629f5..ac4d3b35f96c 100644
--- a/packages/SystemUI/res/values-or/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-or/tiles_states_strings.xml
@@ -56,9 +56,11 @@
<item msgid="5376619709702103243">"ବନ୍ଦ ଅଛି"</item>
<item msgid="4875147066469902392">"ଚାଲୁ ଅଛି"</item>
</string-array>
- <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) -->
- <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) -->
- <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) -->
+ <string-array name="tile_states_modes_dnd">
+ <item msgid="6509540227356524582">"ଅନୁପଲବ୍ଧ"</item>
+ <item msgid="8589336868985358191">"ବନ୍ଦ ଅଛି"</item>
+ <item msgid="726072717827778234">"ଚାଲୁ ଅଛି"</item>
+ </string-array>
<string-array name="tile_states_flashlight">
<item msgid="3465257127433353857">"ଉପଲବ୍ଧ ନାହିଁ"</item>
<item msgid="5044688398303285224">"ବନ୍ଦ ଅଛି"</item>
diff --git a/packages/SystemUI/res/values-pa/strings.xml b/packages/SystemUI/res/values-pa/strings.xml
index c7735a21b25b..0e2373093dfc 100644
--- a/packages/SystemUI/res/values-pa/strings.xml
+++ b/packages/SystemUI/res/values-pa/strings.xml
@@ -419,6 +419,11 @@
<string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"\'ਨਵਾਂ ਡੀਵਾਈਸ ਜੋੜਾਬੱਧ ਕਰੋ\' \'ਤੇ ਕਲਿੱਕ ਕਰੋ"</string>
<string name="hearing_devices_presets_error" msgid="350363093458408536">"ਪ੍ਰੀਸੈੱਟ ਨੂੰ ਅੱਪਡੇਟ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਿਆ"</string>
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"ਪ੍ਰੀਸੈੱਟ"</string>
+ <string name="hearing_devices_input_routing_label" msgid="730396728151232306">"ਕਾਲਾਂ ਲਈ ਪੂਰਵ-ਨਿਰਧਾਰਿਤ ਮਾਈਕ੍ਰੋਫ਼ੋਨ"</string>
+ <string-array name="hearing_device_input_routing_options">
+ <item msgid="4582190415045337003">"ਸੁਣਨ ਦੇ ਸਾਧਨ ਦਾ ਮਾਈਕ੍ਰੋਫ਼ੋਨ"</item>
+ <item msgid="8501466270452446450">"ਇਸ ਫ਼ੋਨ ਦਾ ਮਾਈਕ੍ਰੋਫ਼ੋਨ"</item>
+ </string-array>
<string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"ਚੁਣਿਆ ਗਿਆ"</string>
<string name="hearing_devices_ambient_label" msgid="629440938614895797">"ਆਲੇ-ਦੁਆਲੇ"</string>
<string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"ਖੱਬੇ"</string>
@@ -582,6 +587,7 @@
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"ਕਿਸੇ ਐਪ ਨੂੰ ਸਾਂਝਾ ਕਰਨ ਦੌਰਾਨ, ਉਸ ਐਪ \'ਤੇ ਦਿਖ ਰਹੀ ਜਾਂ ਚਲਾਈ ਗਈ ਹਰੇਕ ਚੀਜ਼ <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ਨੂੰ ਦਿਖਣਯੋਗ ਹੁੰਦੀ ਹੈ। ਇਸ ਲਈ ਪਾਸਵਰਡਾਂ, ਭੁਗਤਾਨ ਵੇਰਵਿਆਂ, ਸੁਨੇਹਿਆਂ, ਫ਼ੋਟੋਆਂ ਅਤੇ ਆਡੀਓ ਅਤੇ ਵੀਡੀਓ ਵਰਗੀਆਂ ਚੀਜ਼ਾਂ ਵਾਸਤੇ ਸਾਵਧਾਨ ਰਹੋ।"</string>
<string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"ਸਕ੍ਰੀਨ ਸਾਂਝੀ ਕਰੋ"</string>
<string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g> ਨੇ ਇਸ ਵਿਕਲਪ ਨੂੰ ਬੰਦ ਕਰ ਦਿੱਤਾ ਹੈ"</string>
+ <string name="media_projection_entry_app_permission_dialog_single_app_not_supported" msgid="4860247304058870233">"ਐਪ ਵੱਲੋਂ ਸਮਰਥਿਤ ਨਹੀਂ"</string>
<string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"ਸਾਂਝਾ ਕਰਨ ਲਈ ਐਪ ਚੁਣੋ"</string>
<string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"ਕੀ ਸਕ੍ਰੀਨ ਨੂੰ ਕਾਸਟ ਕਰਨਾ ਹੈ?"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"ਇੱਕ ਐਪ ਨੂੰ ਕਾਸਟ ਕਰੋ"</string>
@@ -794,8 +800,7 @@
<string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"ਚਾਲੂ ਹੈ - ਚਿਹਰਾ-ਆਧਾਰਿਤ"</string>
<string name="inline_done_button" msgid="6043094985588909584">"ਹੋ ਗਿਆ"</string>
<string name="inline_ok_button" msgid="603075490581280343">"ਲਾਗੂ ਕਰੋ"</string>
- <!-- no translation found for inline_turn_off_notifications (2653064779176881329) -->
- <skip />
+ <string name="inline_turn_off_notifications" msgid="2653064779176881329">"ਬੰਦ ਕਰੋ"</string>
<string name="notification_silence_title" msgid="8608090968400832335">"ਸ਼ਾਂਤ"</string>
<string name="notification_alert_title" msgid="3656229781017543655">"ਪੂਰਵ-ਨਿਰਧਾਰਿਤ"</string>
<string name="notification_automatic_title" msgid="3745465364578762652">"ਸਵੈਚਲਿਤ"</string>
@@ -977,9 +982,13 @@
<string name="right_icon" msgid="1103955040645237425">"ਸੱਜਾ ਪ੍ਰਤੀਕ"</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"ਟਾਇਲਾਂ ਸ਼ਾਮਲ ਕਰਨ ਲਈ ਫੜ੍ਹ ਕੇ ਘਸੀਟੋ"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"ਟਾਇਲਾਂ ਨੂੰ ਮੁੜ-ਵਿਵਸਥਿਤ ਕਰਨ ਲਈ ਫੜ੍ਹ ਕੇ ਘਸੀਟੋ"</string>
+ <!-- no translation found for tap_to_position_tile (6282815817773342757) -->
+ <skip />
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"ਹਟਾਉਣ ਲਈ ਇੱਥੇ ਘਸੀਟੋ"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"ਤੁਹਾਨੂੰ ਘੱਟੋ-ਘੱਟ <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> ਟਾਇਲਾਂ ਦੀ ਲੋੜ ਪਵੇਗੀ"</string>
<string name="qs_edit" msgid="5583565172803472437">"ਸੰਪਾਦਨ ਕਰੋ"</string>
+ <!-- no translation found for qs_edit_tiles (2105215324060865035) -->
+ <skip />
<string name="tuner_time" msgid="2450785840990529997">"ਸਮਾਂ"</string>
<string-array name="clock_options">
<item msgid="3986445361435142273">"ਘੰਟੇ, ਮਿੰਟ, ਅਤੇ ਸਕਿੰਟ ਦਿਖਾਓ"</item>
@@ -995,6 +1004,10 @@
<string name="other" msgid="429768510980739978">"ਹੋਰ"</string>
<string name="accessibility_qs_edit_toggle_tile_size_action" msgid="1485194410119733586">"ਟਾਇਲ ਦੇ ਆਕਾਰ ਨੂੰ ਟੌਗਲ ਕਰੋ"</string>
<string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"ਟਾਇਲ ਹਟਾਓ"</string>
+ <!-- no translation found for accessibility_qs_edit_toggle_placement_mode (3870429389210569610) -->
+ <skip />
+ <!-- no translation found for accessibility_qs_edit_toggle_selection (2201248304072372239) -->
+ <skip />
<string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"ਪਿਛਲੀ ਸਥਿਤੀ \'ਤੇ ਟਾਇਲ ਸ਼ਾਮਲ ਕਰੋ"</string>
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"ਟਾਇਲ ਨੂੰ ਲਿਜਾਓ"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"ਮਨਪਸੰਦ ਸਥਿਤੀ \'ਤੇ ਟਾਇਲ ਸ਼ਾਮਲ ਕਰੋ"</string>
@@ -1570,5 +1583,7 @@
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"ਅਗਿਆਤ"</string>
<string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"ਕੀ ਸਾਰੀਆਂ ਟਾਇਲਾਂ ਨੂੰ ਰੀਸੈੱਟ ਕਰਨਾ ਹੈ?"</string>
<string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"ਸਾਰੀਆਂ ਤਤਕਾਲ ਸੈਟਿੰਗਾਂ ਟਾਇਲਾਂ ਡੀਵਾਈਸ ਦੀਆਂ ਮੂਲ ਸੈਟਿੰਗਾਂ \'ਤੇ ਰੀਸੈੱਟ ਹੋ ਜਾਣਗੀਆਂ"</string>
+ <!-- no translation found for demote_explain_text (1600426458580544250) -->
+ <skip />
<string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
</resources>
diff --git a/packages/SystemUI/res/values-pa/tiles_states_strings.xml b/packages/SystemUI/res/values-pa/tiles_states_strings.xml
index 43b8734bb274..d0f5103c32a1 100644
--- a/packages/SystemUI/res/values-pa/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-pa/tiles_states_strings.xml
@@ -56,9 +56,11 @@
<item msgid="5376619709702103243">"ਬੰਦ ਹੈ"</item>
<item msgid="4875147066469902392">"ਚਾਲੂ ਹੈ"</item>
</string-array>
- <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) -->
- <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) -->
- <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) -->
+ <string-array name="tile_states_modes_dnd">
+ <item msgid="6509540227356524582">"ਉਪਲਬਧ ਨਹੀਂ"</item>
+ <item msgid="8589336868985358191">"ਬੰਦ"</item>
+ <item msgid="726072717827778234">"ਚਾਲੂ"</item>
+ </string-array>
<string-array name="tile_states_flashlight">
<item msgid="3465257127433353857">"ਅਣਉਪਲਬਧ ਹੈ"</item>
<item msgid="5044688398303285224">"ਬੰਦ ਹੈ"</item>
diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml
index 7c490736651c..ed9641716cd7 100644
--- a/packages/SystemUI/res/values-pl/strings.xml
+++ b/packages/SystemUI/res/values-pl/strings.xml
@@ -419,6 +419,11 @@
<string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Kliknij, aby sparować nowe urządzenie"</string>
<string name="hearing_devices_presets_error" msgid="350363093458408536">"Nie udało się zaktualizować gotowego ustawienia"</string>
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"Gotowe ustawienie"</string>
+ <string name="hearing_devices_input_routing_label" msgid="730396728151232306">"Domyślny mikrofon do połączeń"</string>
+ <string-array name="hearing_device_input_routing_options">
+ <item msgid="4582190415045337003">"Mikrofon aparatu słuchowego"</item>
+ <item msgid="8501466270452446450">"Mikrofon tego telefonu"</item>
+ </string-array>
<string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"Wybrano"</string>
<string name="hearing_devices_ambient_label" msgid="629440938614895797">"Otoczenie"</string>
<string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Po lewej"</string>
@@ -582,6 +587,8 @@
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Kiedy udostępniasz obraz z aplikacji <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>, widoczne jest wszystko to, co jest w niej wyświetlane lub odtwarzane. Dlatego zachowaj ostrożność w zakresie haseł, danych do płatności, wiadomości, zdjęć, audio i filmów."</string>
<string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Udostępnij ekran"</string>
<string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g> ma wyłączoną tę opcję"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_not_supported (4860247304058870233) -->
+ <skip />
<string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"Wybierz aplikację do udostępniania"</string>
<string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Włączyć przesyłanie treści z ekranu?"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Przesyłanie obrazu z 1 aplikacji"</string>
@@ -794,8 +801,7 @@
<string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Włączono – na podstawie twarzy"</string>
<string name="inline_done_button" msgid="6043094985588909584">"Gotowe"</string>
<string name="inline_ok_button" msgid="603075490581280343">"Zastosuj"</string>
- <!-- no translation found for inline_turn_off_notifications (2653064779176881329) -->
- <skip />
+ <string name="inline_turn_off_notifications" msgid="2653064779176881329">"Wyłącz"</string>
<string name="notification_silence_title" msgid="8608090968400832335">"Ciche"</string>
<string name="notification_alert_title" msgid="3656229781017543655">"Domyślne"</string>
<string name="notification_automatic_title" msgid="3745465364578762652">"Automatycznie"</string>
@@ -977,9 +983,13 @@
<string name="right_icon" msgid="1103955040645237425">"Prawa ikona"</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"Aby dodać kafelki, przytrzymaj je i przeciągnij"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"Aby przestawić kafelki, przytrzymaj je i przeciągnij"</string>
+ <!-- no translation found for tap_to_position_tile (6282815817773342757) -->
+ <skip />
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"Przeciągnij tutaj, by usunąć"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"Minimalna liczba kafelków to <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g>"</string>
<string name="qs_edit" msgid="5583565172803472437">"Edytuj"</string>
+ <!-- no translation found for qs_edit_tiles (2105215324060865035) -->
+ <skip />
<string name="tuner_time" msgid="2450785840990529997">"Godzina"</string>
<string-array name="clock_options">
<item msgid="3986445361435142273">"Pokazuj godziny, minuty i sekundy"</item>
@@ -995,6 +1005,10 @@
<string name="other" msgid="429768510980739978">"Inne"</string>
<string name="accessibility_qs_edit_toggle_tile_size_action" msgid="1485194410119733586">"przełącz rozmiar kafelka"</string>
<string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"usunąć kartę"</string>
+ <!-- no translation found for accessibility_qs_edit_toggle_placement_mode (3870429389210569610) -->
+ <skip />
+ <!-- no translation found for accessibility_qs_edit_toggle_selection (2201248304072372239) -->
+ <skip />
<string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"dodaj kafelek do ostatniej pozycji"</string>
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Przenieś kartę"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"Dodaj kafelek do wybranej pozycji"</string>
@@ -1570,5 +1584,7 @@
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Nieznane"</string>
<string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"Zresetować wszystkie kafelki?"</string>
<string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Wszystkie kafelki Szybkich ustawień zostaną zresetowane do oryginalnych ustawień urządzenia"</string>
+ <!-- no translation found for demote_explain_text (1600426458580544250) -->
+ <skip />
<string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
</resources>
diff --git a/packages/SystemUI/res/values-pl/tiles_states_strings.xml b/packages/SystemUI/res/values-pl/tiles_states_strings.xml
index abc1867f0660..ebd89d2dc7b9 100644
--- a/packages/SystemUI/res/values-pl/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-pl/tiles_states_strings.xml
@@ -56,9 +56,11 @@
<item msgid="5376619709702103243">"Wyłączone"</item>
<item msgid="4875147066469902392">"Włączone"</item>
</string-array>
- <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) -->
- <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) -->
- <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) -->
+ <string-array name="tile_states_modes_dnd">
+ <item msgid="6509540227356524582">"Niedostępny"</item>
+ <item msgid="8589336868985358191">"Wyłączony"</item>
+ <item msgid="726072717827778234">"Włączony"</item>
+ </string-array>
<string-array name="tile_states_flashlight">
<item msgid="3465257127433353857">"Niedostępny"</item>
<item msgid="5044688398303285224">"Wyłączona"</item>
diff --git a/packages/SystemUI/res/values-pt-rBR/strings.xml b/packages/SystemUI/res/values-pt-rBR/strings.xml
index 40b85cc0da14..ab06ef85e7e4 100644
--- a/packages/SystemUI/res/values-pt-rBR/strings.xml
+++ b/packages/SystemUI/res/values-pt-rBR/strings.xml
@@ -419,6 +419,11 @@
<string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Clique para parear o novo dispositivo"</string>
<string name="hearing_devices_presets_error" msgid="350363093458408536">"Não foi possível atualizar a predefinição"</string>
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"Predefinição"</string>
+ <string name="hearing_devices_input_routing_label" msgid="730396728151232306">"Microfone padrão para ligações"</string>
+ <string-array name="hearing_device_input_routing_options">
+ <item msgid="4582190415045337003">"Microfone para aparelhos auditivos"</item>
+ <item msgid="8501466270452446450">"O microfone deste smartphone"</item>
+ </string-array>
<string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"Selecionado"</string>
<string name="hearing_devices_ambient_label" msgid="629440938614895797">"Som ambiente"</string>
<string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Lado esquerdo"</string>
@@ -582,6 +587,8 @@
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Quando você compartilha um aplicativo, todas as informações mostradas ou abertas nele ficam visíveis para o app <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Tenha cuidado com senhas, detalhes de pagamento, mensagens, fotos, áudios e vídeos."</string>
<string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Compartilhar tela"</string>
<string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"O app <xliff:g id="APP_NAME">%1$s</xliff:g> desativou essa opção"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_not_supported (4860247304058870233) -->
+ <skip />
<string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"Escolha um app para compartilhar"</string>
<string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Transmitir a tela?"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Transmitir um app"</string>
@@ -794,8 +801,7 @@
<string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Ativada (reconhecimento facial)"</string>
<string name="inline_done_button" msgid="6043094985588909584">"Concluído"</string>
<string name="inline_ok_button" msgid="603075490581280343">"Aplicar"</string>
- <!-- no translation found for inline_turn_off_notifications (2653064779176881329) -->
- <skip />
+ <string name="inline_turn_off_notifications" msgid="2653064779176881329">"Desativar"</string>
<string name="notification_silence_title" msgid="8608090968400832335">"Silenciosas"</string>
<string name="notification_alert_title" msgid="3656229781017543655">"Padrão"</string>
<string name="notification_automatic_title" msgid="3745465364578762652">"Automática"</string>
@@ -977,9 +983,13 @@
<string name="right_icon" msgid="1103955040645237425">"Ícone à direita"</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"Mantenha pressionado e arraste para adicionar blocos"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"Para reorganizar, toque no bloco sem soltar e arraste"</string>
+ <!-- no translation found for tap_to_position_tile (6282815817773342757) -->
+ <skip />
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"Arraste aqui para remover"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"É preciso haver pelo menos <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> blocos"</string>
<string name="qs_edit" msgid="5583565172803472437">"Editar"</string>
+ <!-- no translation found for qs_edit_tiles (2105215324060865035) -->
+ <skip />
<string name="tuner_time" msgid="2450785840990529997">"Horas"</string>
<string-array name="clock_options">
<item msgid="3986445361435142273">"Mostrar horas, minutos e segundos"</item>
@@ -995,6 +1005,10 @@
<string name="other" msgid="429768510980739978">"Outros"</string>
<string name="accessibility_qs_edit_toggle_tile_size_action" msgid="1485194410119733586">"alternar o tamanho do bloco"</string>
<string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"remover o bloco"</string>
+ <!-- no translation found for accessibility_qs_edit_toggle_placement_mode (3870429389210569610) -->
+ <skip />
+ <!-- no translation found for accessibility_qs_edit_toggle_selection (2201248304072372239) -->
+ <skip />
<string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"adicionar o bloco à última posição"</string>
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Mover bloco"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"Adicionar o bloco à posição desejada"</string>
@@ -1570,5 +1584,7 @@
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Desconhecidos"</string>
<string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"Redefinir todos os blocos?"</string>
<string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Todos os blocos \"Configurações rápidas\" serão redefinidos para as configurações originais do dispositivo"</string>
+ <!-- no translation found for demote_explain_text (1600426458580544250) -->
+ <skip />
<string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
</resources>
diff --git a/packages/SystemUI/res/values-pt-rBR/tiles_states_strings.xml b/packages/SystemUI/res/values-pt-rBR/tiles_states_strings.xml
index 30e45feb3c13..28ea47f31f5c 100644
--- a/packages/SystemUI/res/values-pt-rBR/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-pt-rBR/tiles_states_strings.xml
@@ -56,9 +56,11 @@
<item msgid="5376619709702103243">"Desativado"</item>
<item msgid="4875147066469902392">"Ativado"</item>
</string-array>
- <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) -->
- <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) -->
- <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) -->
+ <string-array name="tile_states_modes_dnd">
+ <item msgid="6509540227356524582">"Indisponível"</item>
+ <item msgid="8589336868985358191">"Desativado"</item>
+ <item msgid="726072717827778234">"Ativado"</item>
+ </string-array>
<string-array name="tile_states_flashlight">
<item msgid="3465257127433353857">"Indisponível"</item>
<item msgid="5044688398303285224">"Apagada"</item>
diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml
index 934c7176d4a3..94dbc4428ae0 100644
--- a/packages/SystemUI/res/values-pt-rPT/strings.xml
+++ b/packages/SystemUI/res/values-pt-rPT/strings.xml
@@ -419,6 +419,11 @@
<string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Clique para sincronizar um novo dispositivo"</string>
<string name="hearing_devices_presets_error" msgid="350363093458408536">"Não foi possível atualizar a predefinição"</string>
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"Predefinição"</string>
+ <string name="hearing_devices_input_routing_label" msgid="730396728151232306">"Microfone predefinido para chamadas"</string>
+ <string-array name="hearing_device_input_routing_options">
+ <item msgid="4582190415045337003">"Microfone do aparelho auditivo"</item>
+ <item msgid="8501466270452446450">"Microfone deste telemóvel"</item>
+ </string-array>
<string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"Selecionado"</string>
<string name="hearing_devices_ambient_label" msgid="629440938614895797">"Ambiente"</string>
<string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Esquerda"</string>
@@ -582,6 +587,7 @@
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Quando está a partilhar uma app, tudo o que é mostrado ou reproduzido nessa app é visível para a app <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Por isso, tenha cuidado com, por exemplo, palavras-passe, detalhes de pagamento, mensagens, fotos, áudio e vídeo."</string>
<string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Partilhar ecrã"</string>
<string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"A app <xliff:g id="APP_NAME">%1$s</xliff:g> desativou esta opção"</string>
+ <string name="media_projection_entry_app_permission_dialog_single_app_not_supported" msgid="4860247304058870233">"Não compatível com a app"</string>
<string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"Escolha uma app para partilhar"</string>
<string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Transmitir o ecrã?"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Transmitir uma app"</string>
@@ -794,8 +800,7 @@
<string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Ativada – Com base no rosto"</string>
<string name="inline_done_button" msgid="6043094985588909584">"Concluído"</string>
<string name="inline_ok_button" msgid="603075490581280343">"Aplicar"</string>
- <!-- no translation found for inline_turn_off_notifications (2653064779176881329) -->
- <skip />
+ <string name="inline_turn_off_notifications" msgid="2653064779176881329">"Desativar"</string>
<string name="notification_silence_title" msgid="8608090968400832335">"Silencioso"</string>
<string name="notification_alert_title" msgid="3656229781017543655">"Predefinição"</string>
<string name="notification_automatic_title" msgid="3745465364578762652">"Automática"</string>
@@ -977,9 +982,13 @@
<string name="right_icon" msgid="1103955040645237425">"Ícone direito"</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"Toque sem soltar e arraste para reorganizar os mosaicos"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"Toque sem soltar e arraste para reorganizar os mosaicos"</string>
+ <!-- no translation found for tap_to_position_tile (6282815817773342757) -->
+ <skip />
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"Arrastar para aqui para remover"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"Necessita de, pelo menos, <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> cartões"</string>
<string name="qs_edit" msgid="5583565172803472437">"Editar"</string>
+ <!-- no translation found for qs_edit_tiles (2105215324060865035) -->
+ <skip />
<string name="tuner_time" msgid="2450785840990529997">"Hora"</string>
<string-array name="clock_options">
<item msgid="3986445361435142273">"Mostrar horas, minutos e segundos"</item>
@@ -995,6 +1004,10 @@
<string name="other" msgid="429768510980739978">"Outro"</string>
<string name="accessibility_qs_edit_toggle_tile_size_action" msgid="1485194410119733586">"ativar/desativar o tamanho do mosaico"</string>
<string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"remover o cartão"</string>
+ <!-- no translation found for accessibility_qs_edit_toggle_placement_mode (3870429389210569610) -->
+ <skip />
+ <!-- no translation found for accessibility_qs_edit_toggle_selection (2201248304072372239) -->
+ <skip />
<string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"adicionar o mosaico à última posição"</string>
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Mover cartão"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"Adicionar mosaico à posição pretendida"</string>
@@ -1570,5 +1583,7 @@
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Desconhecido"</string>
<string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"Repor todos os mosaicos?"</string>
<string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Todos os mosaicos de Definições rápidas vão ser repostos para as definições originais do dispositivo"</string>
+ <!-- no translation found for demote_explain_text (1600426458580544250) -->
+ <skip />
<string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
</resources>
diff --git a/packages/SystemUI/res/values-pt-rPT/tiles_states_strings.xml b/packages/SystemUI/res/values-pt-rPT/tiles_states_strings.xml
index 42a465ea415f..bedcab0eabc4 100644
--- a/packages/SystemUI/res/values-pt-rPT/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-pt-rPT/tiles_states_strings.xml
@@ -56,9 +56,11 @@
<item msgid="5376619709702103243">"Desligado"</item>
<item msgid="4875147066469902392">"Ligado"</item>
</string-array>
- <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) -->
- <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) -->
- <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) -->
+ <string-array name="tile_states_modes_dnd">
+ <item msgid="6509540227356524582">"Indisponível"</item>
+ <item msgid="8589336868985358191">"Desativado"</item>
+ <item msgid="726072717827778234">"Ativado"</item>
+ </string-array>
<string-array name="tile_states_flashlight">
<item msgid="3465257127433353857">"Indisponível"</item>
<item msgid="5044688398303285224">"Desligada"</item>
diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml
index 40b85cc0da14..ab06ef85e7e4 100644
--- a/packages/SystemUI/res/values-pt/strings.xml
+++ b/packages/SystemUI/res/values-pt/strings.xml
@@ -419,6 +419,11 @@
<string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Clique para parear o novo dispositivo"</string>
<string name="hearing_devices_presets_error" msgid="350363093458408536">"Não foi possível atualizar a predefinição"</string>
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"Predefinição"</string>
+ <string name="hearing_devices_input_routing_label" msgid="730396728151232306">"Microfone padrão para ligações"</string>
+ <string-array name="hearing_device_input_routing_options">
+ <item msgid="4582190415045337003">"Microfone para aparelhos auditivos"</item>
+ <item msgid="8501466270452446450">"O microfone deste smartphone"</item>
+ </string-array>
<string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"Selecionado"</string>
<string name="hearing_devices_ambient_label" msgid="629440938614895797">"Som ambiente"</string>
<string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Lado esquerdo"</string>
@@ -582,6 +587,8 @@
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Quando você compartilha um aplicativo, todas as informações mostradas ou abertas nele ficam visíveis para o app <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Tenha cuidado com senhas, detalhes de pagamento, mensagens, fotos, áudios e vídeos."</string>
<string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Compartilhar tela"</string>
<string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"O app <xliff:g id="APP_NAME">%1$s</xliff:g> desativou essa opção"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_not_supported (4860247304058870233) -->
+ <skip />
<string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"Escolha um app para compartilhar"</string>
<string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Transmitir a tela?"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Transmitir um app"</string>
@@ -794,8 +801,7 @@
<string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Ativada (reconhecimento facial)"</string>
<string name="inline_done_button" msgid="6043094985588909584">"Concluído"</string>
<string name="inline_ok_button" msgid="603075490581280343">"Aplicar"</string>
- <!-- no translation found for inline_turn_off_notifications (2653064779176881329) -->
- <skip />
+ <string name="inline_turn_off_notifications" msgid="2653064779176881329">"Desativar"</string>
<string name="notification_silence_title" msgid="8608090968400832335">"Silenciosas"</string>
<string name="notification_alert_title" msgid="3656229781017543655">"Padrão"</string>
<string name="notification_automatic_title" msgid="3745465364578762652">"Automática"</string>
@@ -977,9 +983,13 @@
<string name="right_icon" msgid="1103955040645237425">"Ícone à direita"</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"Mantenha pressionado e arraste para adicionar blocos"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"Para reorganizar, toque no bloco sem soltar e arraste"</string>
+ <!-- no translation found for tap_to_position_tile (6282815817773342757) -->
+ <skip />
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"Arraste aqui para remover"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"É preciso haver pelo menos <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> blocos"</string>
<string name="qs_edit" msgid="5583565172803472437">"Editar"</string>
+ <!-- no translation found for qs_edit_tiles (2105215324060865035) -->
+ <skip />
<string name="tuner_time" msgid="2450785840990529997">"Horas"</string>
<string-array name="clock_options">
<item msgid="3986445361435142273">"Mostrar horas, minutos e segundos"</item>
@@ -995,6 +1005,10 @@
<string name="other" msgid="429768510980739978">"Outros"</string>
<string name="accessibility_qs_edit_toggle_tile_size_action" msgid="1485194410119733586">"alternar o tamanho do bloco"</string>
<string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"remover o bloco"</string>
+ <!-- no translation found for accessibility_qs_edit_toggle_placement_mode (3870429389210569610) -->
+ <skip />
+ <!-- no translation found for accessibility_qs_edit_toggle_selection (2201248304072372239) -->
+ <skip />
<string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"adicionar o bloco à última posição"</string>
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Mover bloco"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"Adicionar o bloco à posição desejada"</string>
@@ -1570,5 +1584,7 @@
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Desconhecidos"</string>
<string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"Redefinir todos os blocos?"</string>
<string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Todos os blocos \"Configurações rápidas\" serão redefinidos para as configurações originais do dispositivo"</string>
+ <!-- no translation found for demote_explain_text (1600426458580544250) -->
+ <skip />
<string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
</resources>
diff --git a/packages/SystemUI/res/values-pt/tiles_states_strings.xml b/packages/SystemUI/res/values-pt/tiles_states_strings.xml
index 30e45feb3c13..28ea47f31f5c 100644
--- a/packages/SystemUI/res/values-pt/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-pt/tiles_states_strings.xml
@@ -56,9 +56,11 @@
<item msgid="5376619709702103243">"Desativado"</item>
<item msgid="4875147066469902392">"Ativado"</item>
</string-array>
- <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) -->
- <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) -->
- <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) -->
+ <string-array name="tile_states_modes_dnd">
+ <item msgid="6509540227356524582">"Indisponível"</item>
+ <item msgid="8589336868985358191">"Desativado"</item>
+ <item msgid="726072717827778234">"Ativado"</item>
+ </string-array>
<string-array name="tile_states_flashlight">
<item msgid="3465257127433353857">"Indisponível"</item>
<item msgid="5044688398303285224">"Apagada"</item>
diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml
index 3f8324e3b7f3..0d16f3549144 100644
--- a/packages/SystemUI/res/values-ro/strings.xml
+++ b/packages/SystemUI/res/values-ro/strings.xml
@@ -419,6 +419,11 @@
<string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Dă clic pentru a asocia un nou dispozitiv"</string>
<string name="hearing_devices_presets_error" msgid="350363093458408536">"Nu s-a putut actualiza presetarea"</string>
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"Presetare"</string>
+ <string name="hearing_devices_input_routing_label" msgid="730396728151232306">"Microfon prestabilit pentru apeluri"</string>
+ <string-array name="hearing_device_input_routing_options">
+ <item msgid="4582190415045337003">"Microfonul aparatului auditiv"</item>
+ <item msgid="8501466270452446450">"Microfonul acestui telefon"</item>
+ </string-array>
<string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"Selectat"</string>
<string name="hearing_devices_ambient_label" msgid="629440938614895797">"Împrejurimi"</string>
<string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Stânga"</string>
@@ -582,6 +587,8 @@
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Când permiți accesul la o aplicație, orice conținut se afișează sau se redă în aplicație este vizibil pentru <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Prin urmare, ai grijă cu parolele, detaliile de plată, mesajele, fotografiile și conținutul audio și video."</string>
<string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Permite accesul la ecran"</string>
<string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g> a dezactivat această opțiune"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_not_supported (4860247304058870233) -->
+ <skip />
<string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"Alege aplicația de trimis"</string>
<string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Proiectezi ecranul?"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Proiectează o aplicație"</string>
@@ -794,8 +801,7 @@
<string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Activată – În funcție de chip"</string>
<string name="inline_done_button" msgid="6043094985588909584">"Gata"</string>
<string name="inline_ok_button" msgid="603075490581280343">"Aplică"</string>
- <!-- no translation found for inline_turn_off_notifications (2653064779176881329) -->
- <skip />
+ <string name="inline_turn_off_notifications" msgid="2653064779176881329">"Dezactivează"</string>
<string name="notification_silence_title" msgid="8608090968400832335">"Silențios"</string>
<string name="notification_alert_title" msgid="3656229781017543655">"Prestabilite"</string>
<string name="notification_automatic_title" msgid="3745465364578762652">"Automat"</string>
@@ -977,9 +983,13 @@
<string name="right_icon" msgid="1103955040645237425">"Pictograma din dreapta"</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"Ține apăsat și trage pentru a adăuga carduri"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"Ține apăsat și trage pentru a rearanja cardurile"</string>
+ <!-- no translation found for tap_to_position_tile (6282815817773342757) -->
+ <skip />
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"Trage aici pentru a elimina"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"Ai nevoie de cel puțin <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> carduri"</string>
<string name="qs_edit" msgid="5583565172803472437">"Editează"</string>
+ <!-- no translation found for qs_edit_tiles (2105215324060865035) -->
+ <skip />
<string name="tuner_time" msgid="2450785840990529997">"Oră"</string>
<string-array name="clock_options">
<item msgid="3986445361435142273">"Afișează orele, minutele și secundele"</item>
@@ -995,6 +1005,10 @@
<string name="other" msgid="429768510980739978">"Altele"</string>
<string name="accessibility_qs_edit_toggle_tile_size_action" msgid="1485194410119733586">"comută dimensiunea cardului"</string>
<string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"elimină cardul"</string>
+ <!-- no translation found for accessibility_qs_edit_toggle_placement_mode (3870429389210569610) -->
+ <skip />
+ <!-- no translation found for accessibility_qs_edit_toggle_selection (2201248304072372239) -->
+ <skip />
<string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"adaugă cardul în ultima poziție"</string>
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Mută cardul"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"Adaugă cardul în poziția dorită"</string>
@@ -1570,5 +1584,7 @@
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Necunoscută"</string>
<string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"Resetezi toate cardurile?"</string>
<string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Toate cardurile Setări rapide se vor reseta la setările inițiale ale dispozitivului"</string>
+ <!-- no translation found for demote_explain_text (1600426458580544250) -->
+ <skip />
<string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ro/tiles_states_strings.xml b/packages/SystemUI/res/values-ro/tiles_states_strings.xml
index 56460771381d..3f893fab4db2 100644
--- a/packages/SystemUI/res/values-ro/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-ro/tiles_states_strings.xml
@@ -56,9 +56,11 @@
<item msgid="5376619709702103243">"Dezactivată"</item>
<item msgid="4875147066469902392">"Activată"</item>
</string-array>
- <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) -->
- <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) -->
- <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) -->
+ <string-array name="tile_states_modes_dnd">
+ <item msgid="6509540227356524582">"Indisponibil"</item>
+ <item msgid="8589336868985358191">"Dezactivat"</item>
+ <item msgid="726072717827778234">"Activat"</item>
+ </string-array>
<string-array name="tile_states_flashlight">
<item msgid="3465257127433353857">"Indisponibilă"</item>
<item msgid="5044688398303285224">"Dezactivată"</item>
diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml
index 450d048da5bf..442b16e16b0b 100644
--- a/packages/SystemUI/res/values-ru/strings.xml
+++ b/packages/SystemUI/res/values-ru/strings.xml
@@ -419,6 +419,11 @@
<string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Нажмите, чтобы подключить новое устройство"</string>
<string name="hearing_devices_presets_error" msgid="350363093458408536">"Не удалось обновить набор настроек."</string>
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"Набор настроек"</string>
+ <string name="hearing_devices_input_routing_label" msgid="730396728151232306">"Микрофон по умолчанию для вызовов"</string>
+ <string-array name="hearing_device_input_routing_options">
+ <item msgid="4582190415045337003">"Микрофон слухового аппарата"</item>
+ <item msgid="8501466270452446450">"Встроенный микрофон"</item>
+ </string-array>
<string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"Выбрано"</string>
<string name="hearing_devices_ambient_label" msgid="629440938614895797">"Окружающие звуки"</string>
<string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Левый"</string>
@@ -582,6 +587,7 @@
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"При показе приложения все, что в нем происходит, будет видно в приложении \"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>\". Поэтому будьте осторожны с паролями, сведениями о способах оплаты, сообщениями, фотографиями, аудио- и видеозаписями."</string>
<string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Показать экран"</string>
<string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"Приложение \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" отключило эту возможность"</string>
+ <string name="media_projection_entry_app_permission_dialog_single_app_not_supported" msgid="4860247304058870233">"Приложение не поддерживает эту функцию"</string>
<string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"Выбор приложения для демонстрации"</string>
<string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Начать трансляцию экрана?"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Транслировать одно приложение"</string>
@@ -794,8 +800,7 @@
<string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Включить (на основе распознавания лиц)"</string>
<string name="inline_done_button" msgid="6043094985588909584">"Готово"</string>
<string name="inline_ok_button" msgid="603075490581280343">"Применить"</string>
- <!-- no translation found for inline_turn_off_notifications (2653064779176881329) -->
- <skip />
+ <string name="inline_turn_off_notifications" msgid="2653064779176881329">"Отключить"</string>
<string name="notification_silence_title" msgid="8608090968400832335">"Без звука"</string>
<string name="notification_alert_title" msgid="3656229781017543655">"По умолчанию"</string>
<string name="notification_automatic_title" msgid="3745465364578762652">"Автоматически"</string>
@@ -977,9 +982,13 @@
<string name="right_icon" msgid="1103955040645237425">"Значок \"Вправо\""</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"Чтобы добавить элементы, перетащите их."</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"Чтобы изменить порядок элементов, перетащите их."</string>
+ <!-- no translation found for tap_to_position_tile (6282815817773342757) -->
+ <skip />
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"Чтобы удалить, перетащите сюда"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"Должно остаться не менее <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> элементов"</string>
<string name="qs_edit" msgid="5583565172803472437">"Изменить"</string>
+ <!-- no translation found for qs_edit_tiles (2105215324060865035) -->
+ <skip />
<string name="tuner_time" msgid="2450785840990529997">"Время"</string>
<string-array name="clock_options">
<item msgid="3986445361435142273">"Часы, минуты и секунды"</item>
@@ -995,6 +1004,10 @@
<string name="other" msgid="429768510980739978">"Другое"</string>
<string name="accessibility_qs_edit_toggle_tile_size_action" msgid="1485194410119733586">"изменить размер параметра"</string>
<string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"удалить панель"</string>
+ <!-- no translation found for accessibility_qs_edit_toggle_placement_mode (3870429389210569610) -->
+ <skip />
+ <!-- no translation found for accessibility_qs_edit_toggle_selection (2201248304072372239) -->
+ <skip />
<string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"добавить параметр в конец"</string>
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Переместить панель"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"Выбрать, куда добавить параметр"</string>
@@ -1570,5 +1583,7 @@
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Неизвестно"</string>
<string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"Сбросить все параметры?"</string>
<string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Для всех параметров быстрых настроек будут восстановлены значения по умолчанию."</string>
+ <!-- no translation found for demote_explain_text (1600426458580544250) -->
+ <skip />
<string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ru/tiles_states_strings.xml b/packages/SystemUI/res/values-ru/tiles_states_strings.xml
index 9ffa7f1f4152..d20f5e218806 100644
--- a/packages/SystemUI/res/values-ru/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-ru/tiles_states_strings.xml
@@ -56,9 +56,11 @@
<item msgid="5376619709702103243">"Откл."</item>
<item msgid="4875147066469902392">"Вкл."</item>
</string-array>
- <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) -->
- <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) -->
- <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) -->
+ <string-array name="tile_states_modes_dnd">
+ <item msgid="6509540227356524582">"Недоступно"</item>
+ <item msgid="8589336868985358191">"Отключено"</item>
+ <item msgid="726072717827778234">"Включено"</item>
+ </string-array>
<string-array name="tile_states_flashlight">
<item msgid="3465257127433353857">"Функция недоступна"</item>
<item msgid="5044688398303285224">"Откл."</item>
diff --git a/packages/SystemUI/res/values-si/strings.xml b/packages/SystemUI/res/values-si/strings.xml
index c6326f27c478..074ef9386ecc 100644
--- a/packages/SystemUI/res/values-si/strings.xml
+++ b/packages/SystemUI/res/values-si/strings.xml
@@ -419,6 +419,11 @@
<string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"නව උපාංගය යුගල කිරීමට ක්ලික් කරන්න"</string>
<string name="hearing_devices_presets_error" msgid="350363093458408536">"පෙර සැකසීම යාවත්කාලීන කළ නොහැකි විය"</string>
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"පෙරසැකසුම"</string>
+ <string name="hearing_devices_input_routing_label" msgid="730396728151232306">"ඇමතුම් සඳහා පෙරනිමි මයික්‍රෆෝනය"</string>
+ <string-array name="hearing_device_input_routing_options">
+ <item msgid="4582190415045337003">"ශ්‍රවණාධාර මයික්‍රෆෝනය"</item>
+ <item msgid="8501466270452446450">"මෙම දුරකථනයේ මයික්‍රෆෝනය"</item>
+ </string-array>
<string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"තෝරන ලදි"</string>
<string name="hearing_devices_ambient_label" msgid="629440938614895797">"වටපිටාව"</string>
<string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"වම"</string>
@@ -582,6 +587,7 @@
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"ඔබ යෙදුමක් බෙදා ගන්නා විට, එම යෙදුමේ පෙන්වන හෝ වාදනය කරන ඕනෑම දෙයක් <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> වෙත දෘශ්‍යමාන වේ. ඒ නිසා මුරපද, ගෙවීම් විස්තර, පණිවිඩ, ඡායාරූප, සහ ශ්‍රව්‍ය සහ දෘශ්‍ය වැනි දේවල් පිළිබඳ ප්‍රවේශම් වන්න."</string>
<string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"තිරය බෙදා ගන්න"</string>
<string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g> මෙම විකල්පය අබල කර ඇත"</string>
+ <string name="media_projection_entry_app_permission_dialog_single_app_not_supported" msgid="4860247304058870233">"යෙදුම මඟින් සහාය නොදක්වයි"</string>
<string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"බෙදා ගැනීමට යෙදුම තෝරන්න"</string>
<string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"ඔබේ තිරය විකාශය කරන්න ද?"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"එක් යෙදුමක් විකාශය කරන්න"</string>
@@ -794,8 +800,7 @@
<string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"ක්‍රියාත්මකයි - මුහුණ-පදනම්ව"</string>
<string name="inline_done_button" msgid="6043094985588909584">"නිමයි"</string>
<string name="inline_ok_button" msgid="603075490581280343">"යොදන්න"</string>
- <!-- no translation found for inline_turn_off_notifications (2653064779176881329) -->
- <skip />
+ <string name="inline_turn_off_notifications" msgid="2653064779176881329">"ක්‍රියාවිරහිත කරන්න"</string>
<string name="notification_silence_title" msgid="8608090968400832335">"නිහඬ"</string>
<string name="notification_alert_title" msgid="3656229781017543655">"පෙරනිමි"</string>
<string name="notification_automatic_title" msgid="3745465364578762652">"ස්වයංක්‍රිය"</string>
@@ -977,9 +982,13 @@
<string name="right_icon" msgid="1103955040645237425">"දකුණු නිරූපකය"</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"ටයිල් එක් කිරීමට අල්ලාගෙන සිට අදින්න"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"ටයිල් නැවත සකස් කිරීමට අල්ලාගෙන සිට අදින්න"</string>
+ <!-- no translation found for tap_to_position_tile (6282815817773342757) -->
+ <skip />
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"ඉවත් කිරීමට මෙතැනට අදින්න"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"ඔබ අවම වශයෙන් ටයිල් <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> ක් අවශ්‍ය වෙයි"</string>
<string name="qs_edit" msgid="5583565172803472437">"සංස්කරණය"</string>
+ <!-- no translation found for qs_edit_tiles (2105215324060865035) -->
+ <skip />
<string name="tuner_time" msgid="2450785840990529997">"වේලාව"</string>
<string-array name="clock_options">
<item msgid="3986445361435142273">"පැය, මිනිත්තු, සහ තත්පර පෙන්වන්න"</item>
@@ -995,6 +1004,10 @@
<string name="other" msgid="429768510980739978">"වෙනත්"</string>
<string name="accessibility_qs_edit_toggle_tile_size_action" msgid="1485194410119733586">"ටයිල් එකේ ප්‍රමාණය මාරු කරන්න"</string>
<string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"ටයිල් ඉවත් කරන්න"</string>
+ <!-- no translation found for accessibility_qs_edit_toggle_placement_mode (3870429389210569610) -->
+ <skip />
+ <!-- no translation found for accessibility_qs_edit_toggle_selection (2201248304072372239) -->
+ <skip />
<string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"ටයිල් එක අවසාන ස්ථානයට එක් කරන්න"</string>
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"ටයිල් ගෙන යන්න"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"අපේක්ෂිත ස්ථානයට ටයිල් එක එක් කරන්න"</string>
@@ -1570,5 +1583,7 @@
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"නොදනී"</string>
<string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"සියලු ටයිල් නැවත සකසන්න ද?"</string>
<string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"සියලු ඉක්මන් සැකසීම් ටයිල් උපාංගයේ මුල් සැකසීම් වෙත නැවත සකසනු ඇත"</string>
+ <!-- no translation found for demote_explain_text (1600426458580544250) -->
+ <skip />
<string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
</resources>
diff --git a/packages/SystemUI/res/values-si/tiles_states_strings.xml b/packages/SystemUI/res/values-si/tiles_states_strings.xml
index 7eeaefd4e546..1b5cf748026a 100644
--- a/packages/SystemUI/res/values-si/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-si/tiles_states_strings.xml
@@ -56,9 +56,11 @@
<item msgid="5376619709702103243">"අක්‍රියයි"</item>
<item msgid="4875147066469902392">"සක්‍රියයි"</item>
</string-array>
- <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) -->
- <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) -->
- <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) -->
+ <string-array name="tile_states_modes_dnd">
+ <item msgid="6509540227356524582">"නොමැත"</item>
+ <item msgid="8589336868985358191">"ක්‍රියාවිරහිතයි"</item>
+ <item msgid="726072717827778234">"ක්‍රියාත්මකයි"</item>
+ </string-array>
<string-array name="tile_states_flashlight">
<item msgid="3465257127433353857">"නොමැත"</item>
<item msgid="5044688398303285224">"අක්‍රියයි"</item>
diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml
index fe1dbbd926d1..ae828110a5c3 100644
--- a/packages/SystemUI/res/values-sk/strings.xml
+++ b/packages/SystemUI/res/values-sk/strings.xml
@@ -419,6 +419,11 @@
<string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Kliknutím spárujete nové zariadenie"</string>
<string name="hearing_devices_presets_error" msgid="350363093458408536">"Predvoľbu sa nepodarilo aktualizovať"</string>
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"Predvoľba"</string>
+ <string name="hearing_devices_input_routing_label" msgid="730396728151232306">"Predvolený mikrofón pre hovory"</string>
+ <string-array name="hearing_device_input_routing_options">
+ <item msgid="4582190415045337003">"Mikrofón načúvadla"</item>
+ <item msgid="8501466270452446450">"Mikrofón tohto telefónu"</item>
+ </string-array>
<string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"Vybrané"</string>
<string name="hearing_devices_ambient_label" msgid="629440938614895797">"Okolie"</string>
<string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Vľavo"</string>
@@ -582,6 +587,8 @@
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Pri zdieľaní aplikácie vidí aplikácia <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> všetko, čo sa v zdieľanej aplikácii zobrazuje alebo prehráva. Preto zvýšte pozornosť v prípade položiek, ako sú heslá, platobné údaje, správy, fotky a zvuk či video."</string>
<string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Zdieľať obrazovku"</string>
<string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"Aplikácia <xliff:g id="APP_NAME">%1$s</xliff:g> túto možnosť zakázala"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_not_supported (4860247304058870233) -->
+ <skip />
<string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"Vyberte aplikáciu, do ktorej chcete zdieľať"</string>
<string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Chcete prenášať obrazovku?"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Prenášať jednu aplikáciu"</string>
@@ -794,8 +801,7 @@
<string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Zapnuté – podľa tváre"</string>
<string name="inline_done_button" msgid="6043094985588909584">"Hotovo"</string>
<string name="inline_ok_button" msgid="603075490581280343">"Použiť"</string>
- <!-- no translation found for inline_turn_off_notifications (2653064779176881329) -->
- <skip />
+ <string name="inline_turn_off_notifications" msgid="2653064779176881329">"Vypnúť"</string>
<string name="notification_silence_title" msgid="8608090968400832335">"Tiché"</string>
<string name="notification_alert_title" msgid="3656229781017543655">"Predvolené"</string>
<string name="notification_automatic_title" msgid="3745465364578762652">"Automaticky"</string>
@@ -977,9 +983,13 @@
<string name="right_icon" msgid="1103955040645237425">"Pravá ikona"</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"Pridržaním a presunutím pridáte karty"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"Karty môžete usporiadať pridržaním a presunutím"</string>
+ <!-- no translation found for tap_to_position_tile (6282815817773342757) -->
+ <skip />
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"Presunutím sem odstránite"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"Minimálny počet vyžadovaných dlaždíc: <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g>"</string>
<string name="qs_edit" msgid="5583565172803472437">"Upraviť"</string>
+ <!-- no translation found for qs_edit_tiles (2105215324060865035) -->
+ <skip />
<string name="tuner_time" msgid="2450785840990529997">"Čas"</string>
<string-array name="clock_options">
<item msgid="3986445361435142273">"Zobrazovať hodiny, minúty a sekundy"</item>
@@ -995,6 +1005,10 @@
<string name="other" msgid="429768510980739978">"Ďalšie"</string>
<string name="accessibility_qs_edit_toggle_tile_size_action" msgid="1485194410119733586">"prepnúť veľkosť karty"</string>
<string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"odstrániť kartu"</string>
+ <!-- no translation found for accessibility_qs_edit_toggle_placement_mode (3870429389210569610) -->
+ <skip />
+ <!-- no translation found for accessibility_qs_edit_toggle_selection (2201248304072372239) -->
+ <skip />
<string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"pridáte kartu na poslednú pozíciu"</string>
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Presunúť kartu"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"Pridať kartu na požadovanú pozíciu"</string>
@@ -1570,5 +1584,7 @@
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Neznáme"</string>
<string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"Chcete resetovať všetky karty?"</string>
<string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Všetky karty rýchlych nastavení sa resetujú na pôvodné nastavenia zariadenia"</string>
+ <!-- no translation found for demote_explain_text (1600426458580544250) -->
+ <skip />
<string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
</resources>
diff --git a/packages/SystemUI/res/values-sk/tiles_states_strings.xml b/packages/SystemUI/res/values-sk/tiles_states_strings.xml
index 5a4ce99bf63e..1b82be8eab8e 100644
--- a/packages/SystemUI/res/values-sk/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-sk/tiles_states_strings.xml
@@ -56,9 +56,11 @@
<item msgid="5376619709702103243">"Vypnuté"</item>
<item msgid="4875147066469902392">"Zapnuté"</item>
</string-array>
- <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) -->
- <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) -->
- <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) -->
+ <string-array name="tile_states_modes_dnd">
+ <item msgid="6509540227356524582">"Nedostupné"</item>
+ <item msgid="8589336868985358191">"Vypnutý"</item>
+ <item msgid="726072717827778234">"Zapnutý"</item>
+ </string-array>
<string-array name="tile_states_flashlight">
<item msgid="3465257127433353857">"Nie je k dispozícii"</item>
<item msgid="5044688398303285224">"Vypnuté"</item>
diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml
index 22d21bc47731..c06b387dde72 100644
--- a/packages/SystemUI/res/values-sl/strings.xml
+++ b/packages/SystemUI/res/values-sl/strings.xml
@@ -419,6 +419,11 @@
<string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Kliknite za seznanitev nove naprave"</string>
<string name="hearing_devices_presets_error" msgid="350363093458408536">"Prednastavljenih vrednosti ni bilo mogoče posodobiti"</string>
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"Prednastavljeno"</string>
+ <string name="hearing_devices_input_routing_label" msgid="730396728151232306">"Privzeti mikrofon za klice"</string>
+ <string-array name="hearing_device_input_routing_options">
+ <item msgid="4582190415045337003">"Mikrofon slušnega aparata"</item>
+ <item msgid="8501466270452446450">"Mikrofon tega telefona"</item>
+ </string-array>
<string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"Izbrano"</string>
<string name="hearing_devices_ambient_label" msgid="629440938614895797">"Okolica"</string>
<string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Levo"</string>
@@ -582,6 +587,7 @@
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Pri deljenju aplikacije je aplikaciji <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> vidno vse, kar je prikazano ali predvajano v tej aplikaciji. Zato bodite previdni z gesli, podatki za plačilo, sporočili, fotografijami ter z zvokom in videom."</string>
<string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Deli zaslon"</string>
<string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"Aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> je onemogočila to možnost"</string>
+ <string name="media_projection_entry_app_permission_dialog_single_app_not_supported" msgid="4860247304058870233">"Aplikacija ne podpira funkcije"</string>
<string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"Izbira aplikacije za deljenje"</string>
<string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Želite predvajati vsebino zaslona?"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Predvajanje ene aplikacije"</string>
@@ -794,8 +800,7 @@
<string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Vklopljeno – na podlagi obraza"</string>
<string name="inline_done_button" msgid="6043094985588909584">"Končano"</string>
<string name="inline_ok_button" msgid="603075490581280343">"Uporabi"</string>
- <!-- no translation found for inline_turn_off_notifications (2653064779176881329) -->
- <skip />
+ <string name="inline_turn_off_notifications" msgid="2653064779176881329">"Izklopi"</string>
<string name="notification_silence_title" msgid="8608090968400832335">"Tiho"</string>
<string name="notification_alert_title" msgid="3656229781017543655">"Privzeto"</string>
<string name="notification_automatic_title" msgid="3745465364578762652">"Samodejno"</string>
@@ -977,9 +982,13 @@
<string name="right_icon" msgid="1103955040645237425">"Desna ikona"</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"Držite in povlecite, da dodate ploščice."</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"Držite in povlecite, da prerazporedite ploščice."</string>
+ <!-- no translation found for tap_to_position_tile (6282815817773342757) -->
+ <skip />
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"Če želite odstraniti, povlecite sem"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"Imeti morate vsaj toliko ploščic: <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g>"</string>
<string name="qs_edit" msgid="5583565172803472437">"Uredi"</string>
+ <!-- no translation found for qs_edit_tiles (2105215324060865035) -->
+ <skip />
<string name="tuner_time" msgid="2450785840990529997">"Ura"</string>
<string-array name="clock_options">
<item msgid="3986445361435142273">"Prikaži ure, minute in sekunde"</item>
@@ -995,6 +1004,10 @@
<string name="other" msgid="429768510980739978">"Drugo"</string>
<string name="accessibility_qs_edit_toggle_tile_size_action" msgid="1485194410119733586">"preklop velikosti ploščice"</string>
<string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"odstranitev ploščice"</string>
+ <!-- no translation found for accessibility_qs_edit_toggle_placement_mode (3870429389210569610) -->
+ <skip />
+ <!-- no translation found for accessibility_qs_edit_toggle_selection (2201248304072372239) -->
+ <skip />
<string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"dodajanje ploščice na zadnji položaj"</string>
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Premik ploščice"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"Dodaj ploščico na želeno mesto"</string>
@@ -1570,5 +1583,7 @@
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Neznano"</string>
<string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"Želite ponastaviti vse ploščice?"</string>
<string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Vse ploščice v hitrih nastavitvah bodo ponastavljene na prvotne nastavitve naprave."</string>
+ <!-- no translation found for demote_explain_text (1600426458580544250) -->
+ <skip />
<string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
</resources>
diff --git a/packages/SystemUI/res/values-sl/tiles_states_strings.xml b/packages/SystemUI/res/values-sl/tiles_states_strings.xml
index 8f243dedef9c..e0db4f1d262a 100644
--- a/packages/SystemUI/res/values-sl/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-sl/tiles_states_strings.xml
@@ -56,9 +56,11 @@
<item msgid="5376619709702103243">"Izklopljeno"</item>
<item msgid="4875147066469902392">"Vklopljeno"</item>
</string-array>
- <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) -->
- <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) -->
- <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) -->
+ <string-array name="tile_states_modes_dnd">
+ <item msgid="6509540227356524582">"Ni na voljo"</item>
+ <item msgid="8589336868985358191">"Izklopljeno"</item>
+ <item msgid="726072717827778234">"Vklopljeno"</item>
+ </string-array>
<string-array name="tile_states_flashlight">
<item msgid="3465257127433353857">"Ni na voljo"</item>
<item msgid="5044688398303285224">"Izklopljeno"</item>
diff --git a/packages/SystemUI/res/values-sq/strings.xml b/packages/SystemUI/res/values-sq/strings.xml
index 203b9faa6730..88f8695ed90f 100644
--- a/packages/SystemUI/res/values-sq/strings.xml
+++ b/packages/SystemUI/res/values-sq/strings.xml
@@ -419,6 +419,11 @@
<string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Kliko për të çiftuar një pajisje të re"</string>
<string name="hearing_devices_presets_error" msgid="350363093458408536">"Paravendosja nuk mund të përditësohej"</string>
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"Paravendosja"</string>
+ <string name="hearing_devices_input_routing_label" msgid="730396728151232306">"Mikrofoni i parazgjedhur për telefonatat"</string>
+ <string-array name="hearing_device_input_routing_options">
+ <item msgid="4582190415045337003">"Mikrofoni i aparatit të dëgjimit"</item>
+ <item msgid="8501466270452446450">"Mikrofoni i këtij telefoni"</item>
+ </string-array>
<string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"Zgjedhur"</string>
<string name="hearing_devices_ambient_label" msgid="629440938614895797">"Ambienti rrethues"</string>
<string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Majtas"</string>
@@ -582,6 +587,8 @@
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Kur ti ndan një aplikacion, çdo gjë që shfaqet ose luhet në atë aplikacion është e dukshme për <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Prandaj, ki kujdes me gjërat si fjalëkalimet, detajet e pagesave, mesazhet, fotografitë, si dhe audion dhe videon."</string>
<string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Ndaj ekranin"</string>
<string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g> e ka çaktivizuar këtë opsion"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_not_supported (4860247304058870233) -->
+ <skip />
<string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"Zgjidh aplikacionin për të ndarë"</string>
<string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Të transmetohet ekrani yt?"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Transmeto një aplikacion"</string>
@@ -794,8 +801,7 @@
<string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Aktiv - Në bazë të fytyrës"</string>
<string name="inline_done_button" msgid="6043094985588909584">"U krye"</string>
<string name="inline_ok_button" msgid="603075490581280343">"Zbato"</string>
- <!-- no translation found for inline_turn_off_notifications (2653064779176881329) -->
- <skip />
+ <string name="inline_turn_off_notifications" msgid="2653064779176881329">"Çaktivizo"</string>
<string name="notification_silence_title" msgid="8608090968400832335">"Në heshtje"</string>
<string name="notification_alert_title" msgid="3656229781017543655">"E parazgjedhur"</string>
<string name="notification_automatic_title" msgid="3745465364578762652">"Automatike"</string>
@@ -977,9 +983,13 @@
<string name="right_icon" msgid="1103955040645237425">"Ikona djathtas"</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"Mbaje të shtypur dhe zvarrit për të shtuar pllakëza"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"Mbaje të shtypur dhe zvarrit për të risistemuar pllakëzat"</string>
+ <!-- no translation found for tap_to_position_tile (6282815817773342757) -->
+ <skip />
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"Zvarrit këtu për ta hequr"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"Të duhen të paktën <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> pllakëza"</string>
<string name="qs_edit" msgid="5583565172803472437">"Redakto"</string>
+ <!-- no translation found for qs_edit_tiles (2105215324060865035) -->
+ <skip />
<string name="tuner_time" msgid="2450785840990529997">"Ora"</string>
<string-array name="clock_options">
<item msgid="3986445361435142273">"Shfaq orët, minutat dhe sekondat"</item>
@@ -995,6 +1005,10 @@
<string name="other" msgid="429768510980739978">"Të tjera"</string>
<string name="accessibility_qs_edit_toggle_tile_size_action" msgid="1485194410119733586">"ndrysho madhësinë e pllakëzës"</string>
<string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"hiq pllakëzën"</string>
+ <!-- no translation found for accessibility_qs_edit_toggle_placement_mode (3870429389210569610) -->
+ <skip />
+ <!-- no translation found for accessibility_qs_edit_toggle_selection (2201248304072372239) -->
+ <skip />
<string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"shtuar pllakëzën në pozicionin e fundit"</string>
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Zhvendos pllakëzën"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"Shto pllakëzën në pozicionin e dëshiruar"</string>
@@ -1570,5 +1584,7 @@
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Nuk njihet"</string>
<string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"Të rivendosen të gjitha pllakëzat?"</string>
<string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Të gjitha pllakëzat e \"Cilësimeve të shpejta\" do të rivendosen te cilësimet origjinale të pajisjes"</string>
+ <!-- no translation found for demote_explain_text (1600426458580544250) -->
+ <skip />
<string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
</resources>
diff --git a/packages/SystemUI/res/values-sq/tiles_states_strings.xml b/packages/SystemUI/res/values-sq/tiles_states_strings.xml
index ac1409990bcd..9bbb0f941ab1 100644
--- a/packages/SystemUI/res/values-sq/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-sq/tiles_states_strings.xml
@@ -56,9 +56,11 @@
<item msgid="5376619709702103243">"Joaktiv"</item>
<item msgid="4875147066469902392">"Aktiv"</item>
</string-array>
- <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) -->
- <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) -->
- <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) -->
+ <string-array name="tile_states_modes_dnd">
+ <item msgid="6509540227356524582">"Nuk ofrohet"</item>
+ <item msgid="8589336868985358191">"Çaktivizuar"</item>
+ <item msgid="726072717827778234">"Aktivizuar"</item>
+ </string-array>
<string-array name="tile_states_flashlight">
<item msgid="3465257127433353857">"Nuk ofrohet"</item>
<item msgid="5044688398303285224">"Joaktiv"</item>
diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml
index 97ca62a420c3..6561de1da71b 100644
--- a/packages/SystemUI/res/values-sr/strings.xml
+++ b/packages/SystemUI/res/values-sr/strings.xml
@@ -419,6 +419,11 @@
<string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Кликните да бисте упарили нов уређај"</string>
<string name="hearing_devices_presets_error" msgid="350363093458408536">"Ажурирање задатих подешавања није успело"</string>
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"Унапред одређена подешавања"</string>
+ <string name="hearing_devices_input_routing_label" msgid="730396728151232306">"Подразумевани микрофон за позиве"</string>
+ <string-array name="hearing_device_input_routing_options">
+ <item msgid="4582190415045337003">"Микрофон слушног апарата"</item>
+ <item msgid="8501466270452446450">"Микрофон овог телефона"</item>
+ </string-array>
<string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"Изабрано"</string>
<string name="hearing_devices_ambient_label" msgid="629440938614895797">"Окружење"</string>
<string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Лево"</string>
@@ -582,6 +587,7 @@
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Када делите апликацију, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> види сав садржај који се приказује или пушта у њој. Зато пазите на лозинке, информације о плаћању, поруке, слике, аудио и видео садржај."</string>
<string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Дели екран"</string>
<string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"Апликација <xliff:g id="APP_NAME">%1$s</xliff:g> је онемогућила ову опцију"</string>
+ <string name="media_projection_entry_app_permission_dialog_single_app_not_supported" msgid="4860247304058870233">"Апликација то не подржава"</string>
<string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"Одаберите апликацију за дељење"</string>
<string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Желите да пребаците екран?"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Пребаци једну апликацију"</string>
@@ -794,8 +800,7 @@
<string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Укључено – на основу лица"</string>
<string name="inline_done_button" msgid="6043094985588909584">"Готово"</string>
<string name="inline_ok_button" msgid="603075490581280343">"Примени"</string>
- <!-- no translation found for inline_turn_off_notifications (2653064779176881329) -->
- <skip />
+ <string name="inline_turn_off_notifications" msgid="2653064779176881329">"Искључи"</string>
<string name="notification_silence_title" msgid="8608090968400832335">"Нечујно"</string>
<string name="notification_alert_title" msgid="3656229781017543655">"Подразумевано"</string>
<string name="notification_automatic_title" msgid="3745465364578762652">"Аутоматска"</string>
@@ -977,9 +982,13 @@
<string name="right_icon" msgid="1103955040645237425">"Десна икона"</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"Задржите и превуците да бисте додали плочице"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"Задржите и превуците да бисте променили распоред плочица"</string>
+ <!-- no translation found for tap_to_position_tile (6282815817773342757) -->
+ <skip />
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"Превуците овде да бисте уклонили"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"Минималан број плочица је <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g>"</string>
<string name="qs_edit" msgid="5583565172803472437">"Измени"</string>
+ <!-- no translation found for qs_edit_tiles (2105215324060865035) -->
+ <skip />
<string name="tuner_time" msgid="2450785840990529997">"Време"</string>
<string-array name="clock_options">
<item msgid="3986445361435142273">"Прикажи сате, минуте и секунде"</item>
@@ -995,6 +1004,10 @@
<string name="other" msgid="429768510980739978">"Друго"</string>
<string name="accessibility_qs_edit_toggle_tile_size_action" msgid="1485194410119733586">"укључивање или искључивање величине плочице"</string>
<string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"уклонили плочицу"</string>
+ <!-- no translation found for accessibility_qs_edit_toggle_placement_mode (3870429389210569610) -->
+ <skip />
+ <!-- no translation found for accessibility_qs_edit_toggle_selection (2201248304072372239) -->
+ <skip />
<string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"додали плочицу на последњу позицију"</string>
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Преместите плочицу"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"Додајте плочицу на жељену позицију"</string>
@@ -1570,5 +1583,7 @@
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Непознато"</string>
<string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"Желите да ресетујете све плочице?"</string>
<string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Све плочице Брзих подешавања ће се ресетовати на првобитна подешавања уређаја"</string>
+ <!-- no translation found for demote_explain_text (1600426458580544250) -->
+ <skip />
<string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
</resources>
diff --git a/packages/SystemUI/res/values-sr/tiles_states_strings.xml b/packages/SystemUI/res/values-sr/tiles_states_strings.xml
index e7e34aea2bee..14a8d94d2839 100644
--- a/packages/SystemUI/res/values-sr/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-sr/tiles_states_strings.xml
@@ -56,9 +56,11 @@
<item msgid="5376619709702103243">"Искључено"</item>
<item msgid="4875147066469902392">"Укључено"</item>
</string-array>
- <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) -->
- <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) -->
- <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) -->
+ <string-array name="tile_states_modes_dnd">
+ <item msgid="6509540227356524582">"Недоступно"</item>
+ <item msgid="8589336868985358191">"Искључено"</item>
+ <item msgid="726072717827778234">"Укључено"</item>
+ </string-array>
<string-array name="tile_states_flashlight">
<item msgid="3465257127433353857">"Недоступно"</item>
<item msgid="5044688398303285224">"Искључено"</item>
diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml
index 4cdc37d72176..6b442082e6e5 100644
--- a/packages/SystemUI/res/values-sv/strings.xml
+++ b/packages/SystemUI/res/values-sv/strings.xml
@@ -419,6 +419,11 @@
<string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Klicka för att parkoppla en ny enhet"</string>
<string name="hearing_devices_presets_error" msgid="350363093458408536">"Det gick inte att uppdatera förinställningen"</string>
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"Förinställning"</string>
+ <string name="hearing_devices_input_routing_label" msgid="730396728151232306">"Standardmikrofon för samtal"</string>
+ <string-array name="hearing_device_input_routing_options">
+ <item msgid="4582190415045337003">"Hörapparatens mikrofon"</item>
+ <item msgid="8501466270452446450">"Telefonens mikrofon"</item>
+ </string-array>
<string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"Markerad"</string>
<string name="hearing_devices_ambient_label" msgid="629440938614895797">"Omgivningsläge"</string>
<string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Vänster"</string>
@@ -582,6 +587,8 @@
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"När du delar en app är allt som visas eller spelas upp i appen synligt för <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Var försiktig med sådant som lösenord, betalningsuppgifter, meddelanden, foton, ljud och video."</string>
<string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Dela skärmen"</string>
<string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g> har inaktiverat alternativet"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_not_supported (4860247304058870233) -->
+ <skip />
<string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"Välj en app att dela"</string>
<string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Vill du casta skärmen?"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Casta en app"</string>
@@ -794,8 +801,7 @@
<string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"På – ansiktsbaserad"</string>
<string name="inline_done_button" msgid="6043094985588909584">"Klart"</string>
<string name="inline_ok_button" msgid="603075490581280343">"Tillämpa"</string>
- <!-- no translation found for inline_turn_off_notifications (2653064779176881329) -->
- <skip />
+ <string name="inline_turn_off_notifications" msgid="2653064779176881329">"Inaktivera"</string>
<string name="notification_silence_title" msgid="8608090968400832335">"Tyst"</string>
<string name="notification_alert_title" msgid="3656229781017543655">"Standard"</string>
<string name="notification_automatic_title" msgid="3745465364578762652">"Automatiskt"</string>
@@ -977,9 +983,13 @@
<string name="right_icon" msgid="1103955040645237425">"Höger ikon"</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"Lägg till rutor genom att trycka och dra"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"Ordna om rutor genom att trycka och dra"</string>
+ <!-- no translation found for tap_to_position_tile (6282815817773342757) -->
+ <skip />
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"Ta bort genom att dra här"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"Minst <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> rutor måste finnas kvar"</string>
<string name="qs_edit" msgid="5583565172803472437">"Redigera"</string>
+ <!-- no translation found for qs_edit_tiles (2105215324060865035) -->
+ <skip />
<string name="tuner_time" msgid="2450785840990529997">"Tid"</string>
<string-array name="clock_options">
<item msgid="3986445361435142273">"Visa timmar, minuter och sekunder"</item>
@@ -995,6 +1005,10 @@
<string name="other" msgid="429768510980739978">"Annat"</string>
<string name="accessibility_qs_edit_toggle_tile_size_action" msgid="1485194410119733586">"växla rutstorlek"</string>
<string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"ta bort ruta"</string>
+ <!-- no translation found for accessibility_qs_edit_toggle_placement_mode (3870429389210569610) -->
+ <skip />
+ <!-- no translation found for accessibility_qs_edit_toggle_selection (2201248304072372239) -->
+ <skip />
<string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"lägg till en ruta på den sista platsen"</string>
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Flytta ruta"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"Lägg till en ruta på önskad plats"</string>
@@ -1570,5 +1584,7 @@
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Okänt"</string>
<string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"Vill du återställa alla rutor?"</string>
<string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Alla Snabbinställningsrutor återställs till enhetens ursprungliga inställningar"</string>
+ <!-- no translation found for demote_explain_text (1600426458580544250) -->
+ <skip />
<string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
</resources>
diff --git a/packages/SystemUI/res/values-sv/tiles_states_strings.xml b/packages/SystemUI/res/values-sv/tiles_states_strings.xml
index 7ec70eac7adc..f042697bd74d 100644
--- a/packages/SystemUI/res/values-sv/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-sv/tiles_states_strings.xml
@@ -56,9 +56,11 @@
<item msgid="5376619709702103243">"Av"</item>
<item msgid="4875147066469902392">"På"</item>
</string-array>
- <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) -->
- <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) -->
- <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) -->
+ <string-array name="tile_states_modes_dnd">
+ <item msgid="6509540227356524582">"Inte tillgängligt"</item>
+ <item msgid="8589336868985358191">"Av"</item>
+ <item msgid="726072717827778234">"På"</item>
+ </string-array>
<string-array name="tile_states_flashlight">
<item msgid="3465257127433353857">"Inte tillgängligt"</item>
<item msgid="5044688398303285224">"Av"</item>
diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml
index 1cb61fdbab01..9e0e67bb2491 100644
--- a/packages/SystemUI/res/values-sw/strings.xml
+++ b/packages/SystemUI/res/values-sw/strings.xml
@@ -419,6 +419,11 @@
<string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Bofya ili uunganishe kifaa kipya"</string>
<string name="hearing_devices_presets_error" msgid="350363093458408536">"Imeshindwa kusasisha mipangilio iliyowekwa mapema"</string>
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"Mipangilio iliyowekwa mapema"</string>
+ <string name="hearing_devices_input_routing_label" msgid="730396728151232306">"Maikrofoni chaguomsingi ya kupiga simu"</string>
+ <string-array name="hearing_device_input_routing_options">
+ <item msgid="4582190415045337003">"Maikrofoni ya visaidizi vya kusikia"</item>
+ <item msgid="8501466270452446450">"Maikrofoni ya simu hii"</item>
+ </string-array>
<string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"Umechagua"</string>
<string name="hearing_devices_ambient_label" msgid="629440938614895797">"Mazingira"</string>
<string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Kushoto"</string>
@@ -582,6 +587,8 @@
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Unaporuhusu ufikiaji wa programu, chochote kinachoonyeshwa au kuchezwa katika programu hiyo kitaonekana kwa <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Kwa hivyo kuwa mwangalifu na vitu kama vile manenosiri, maelezo ya malipo, ujumbe, picha, sauti na video."</string>
<string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Ruhusu ufikiaji wa skrini"</string>
<string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g> imezima chaguo hili"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_not_supported (4860247304058870233) -->
+ <skip />
<string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"Chagua programu utakayoruhusu ifikiwe"</string>
<string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Ungependa kutuma maudhui yaliyo katika skrini yako?"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Tuma maudhui ya programu moja"</string>
@@ -794,8 +801,7 @@
<string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Imewashwa - Inayolenga nyuso"</string>
<string name="inline_done_button" msgid="6043094985588909584">"Nimemaliza"</string>
<string name="inline_ok_button" msgid="603075490581280343">"Tumia"</string>
- <!-- no translation found for inline_turn_off_notifications (2653064779176881329) -->
- <skip />
+ <string name="inline_turn_off_notifications" msgid="2653064779176881329">"Zima"</string>
<string name="notification_silence_title" msgid="8608090968400832335">"Kimya"</string>
<string name="notification_alert_title" msgid="3656229781017543655">"Chaguomsingi"</string>
<string name="notification_automatic_title" msgid="3745465364578762652">"Otomatiki"</string>
@@ -977,9 +983,13 @@
<string name="right_icon" msgid="1103955040645237425">"Aikoni ya kulia"</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"Shikilia na uburute ili uongeze vigae"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"Shikilia na uburute ili upange vigae upya"</string>
+ <!-- no translation found for tap_to_position_tile (6282815817773342757) -->
+ <skip />
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"Buruta hapa ili uondoe"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"Unahitaji angalau vigae <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g>"</string>
<string name="qs_edit" msgid="5583565172803472437">"Badilisha"</string>
+ <!-- no translation found for qs_edit_tiles (2105215324060865035) -->
+ <skip />
<string name="tuner_time" msgid="2450785840990529997">"Wakati"</string>
<string-array name="clock_options">
<item msgid="3986445361435142273">"Onyesha saa, dakika na sekunde"</item>
@@ -995,6 +1005,10 @@
<string name="other" msgid="429768510980739978">"Nyingine"</string>
<string name="accessibility_qs_edit_toggle_tile_size_action" msgid="1485194410119733586">"ubadilishe ukubwa wa kigae"</string>
<string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"ondoa kigae"</string>
+ <!-- no translation found for accessibility_qs_edit_toggle_placement_mode (3870429389210569610) -->
+ <skip />
+ <!-- no translation found for accessibility_qs_edit_toggle_selection (2201248304072372239) -->
+ <skip />
<string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"weka kigae kwenye nafasi ya mwisho"</string>
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Hamisha kigae"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"Weka kigae kwenye nafasi unayopenda"</string>
@@ -1570,5 +1584,7 @@
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Visivyojulikana"</string>
<string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"Ungependa kubadilisha vigae vyote?"</string>
<string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Vigae vyote vya Mipangilio ya Haraka vitabadilishwa kuwa katika mipangilio halisi ya kifaa"</string>
+ <!-- no translation found for demote_explain_text (1600426458580544250) -->
+ <skip />
<string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
</resources>
diff --git a/packages/SystemUI/res/values-sw/tiles_states_strings.xml b/packages/SystemUI/res/values-sw/tiles_states_strings.xml
index 9e262819f560..e08ed3a107da 100644
--- a/packages/SystemUI/res/values-sw/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-sw/tiles_states_strings.xml
@@ -56,9 +56,11 @@
<item msgid="5376619709702103243">"Kimezimwa"</item>
<item msgid="4875147066469902392">"Kimewashwa"</item>
</string-array>
- <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) -->
- <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) -->
- <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) -->
+ <string-array name="tile_states_modes_dnd">
+ <item msgid="6509540227356524582">"Haipatikani"</item>
+ <item msgid="8589336868985358191">"Imezimwa"</item>
+ <item msgid="726072717827778234">"Imewashwa"</item>
+ </string-array>
<string-array name="tile_states_flashlight">
<item msgid="3465257127433353857">"Hakipatikani"</item>
<item msgid="5044688398303285224">"Imezimwa"</item>
diff --git a/packages/SystemUI/res/values-ta/strings.xml b/packages/SystemUI/res/values-ta/strings.xml
index 3109a13290af..66baf1feed4f 100644
--- a/packages/SystemUI/res/values-ta/strings.xml
+++ b/packages/SystemUI/res/values-ta/strings.xml
@@ -419,6 +419,11 @@
<string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"புதிய சாதனத்தை இணைக்க கிளிக் செய்யலாம்"</string>
<string name="hearing_devices_presets_error" msgid="350363093458408536">"முன்னமைவைப் புதுப்பிக்க முடியவில்லை"</string>
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"முன்னமைவு"</string>
+ <string name="hearing_devices_input_routing_label" msgid="730396728151232306">"அழைப்புகளுக்கான இயல்பு மைக்ரோஃபோன்"</string>
+ <string-array name="hearing_device_input_routing_options">
+ <item msgid="4582190415045337003">"செவித்துணைக் கருவி மைக்ரோஃபோன்"</item>
+ <item msgid="8501466270452446450">"இந்த மொபைலின் மைக்ரோஃபோன்"</item>
+ </string-array>
<string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"தேர்ந்தெடுக்கப்பட்டது"</string>
<string name="hearing_devices_ambient_label" msgid="629440938614895797">"சுற்றுப்புறங்கள்"</string>
<string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"இடது"</string>
@@ -582,6 +587,8 @@
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"ஓர் ஆப்ஸைப் பகிரும்போது, அதில் காட்டப்படும்/பிளே செய்யப்படும் அனைத்தும் <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> இல் தெரியும். எனவே கடவுச்சொற்கள், பேமெண்ட் விவரங்கள், மெசேஜ்கள், படங்கள், ஆடியோ, வீடியோ போன்றவை குறித்துக் கவனத்துடன் இருங்கள்."</string>
<string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"திரையைப் பகிர்"</string>
<string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g> ஆப்ஸ் இந்த விருப்பத்தை முடக்கியுள்ளது"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_not_supported (4860247304058870233) -->
+ <skip />
<string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"பகிர ஆப்ஸைத் தேர்வுசெய்க"</string>
<string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"உங்கள் திரையை அலைபரப்ப வேண்டுமா?"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"ஓர் ஆப்ஸை அலைபரப்பு"</string>
@@ -794,8 +801,7 @@
<string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"ஆன் - முகம் அடிப்படையிலானது"</string>
<string name="inline_done_button" msgid="6043094985588909584">"முடிந்தது"</string>
<string name="inline_ok_button" msgid="603075490581280343">"பயன்படுத்து"</string>
- <!-- no translation found for inline_turn_off_notifications (2653064779176881329) -->
- <skip />
+ <string name="inline_turn_off_notifications" msgid="2653064779176881329">"ஆஃப் செய்"</string>
<string name="notification_silence_title" msgid="8608090968400832335">"சைலன்ட்"</string>
<string name="notification_alert_title" msgid="3656229781017543655">"இயல்புநிலை"</string>
<string name="notification_automatic_title" msgid="3745465364578762652">"தானியங்கு"</string>
@@ -977,9 +983,13 @@
<string name="right_icon" msgid="1103955040645237425">"வலப்புற ஐகான்"</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"கட்டங்களைச் சேர்க்க, அவற்றைப் பிடித்து இழுக்கவும்"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"கட்டங்களை மறுவரிசைப்படுத்த அவற்றைப் பிடித்து இழுக்கவும்"</string>
+ <!-- no translation found for tap_to_position_tile (6282815817773342757) -->
+ <skip />
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"அகற்ற, இங்கே இழுக்கவும்"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"குறைந்தது <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> கட்டங்கள் தேவை"</string>
<string name="qs_edit" msgid="5583565172803472437">"மாற்று"</string>
+ <!-- no translation found for qs_edit_tiles (2105215324060865035) -->
+ <skip />
<string name="tuner_time" msgid="2450785840990529997">"நேரம்"</string>
<string-array name="clock_options">
<item msgid="3986445361435142273">"மணிநேரம், நிமிடங்கள், வினாடிகளைக் காட்டு"</item>
@@ -995,6 +1005,10 @@
<string name="other" msgid="429768510980739978">"மற்றவை"</string>
<string name="accessibility_qs_edit_toggle_tile_size_action" msgid="1485194410119733586">"கட்டத்தின் அளவை நிலைமாற்றும்"</string>
<string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"கட்டத்தை அகற்றும்"</string>
+ <!-- no translation found for accessibility_qs_edit_toggle_placement_mode (3870429389210569610) -->
+ <skip />
+ <!-- no translation found for accessibility_qs_edit_toggle_selection (2201248304072372239) -->
+ <skip />
<string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"கடைசி இடத்தில் கட்டத்தைச் சேர்க்கலாம்"</string>
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"கட்டத்தை நகர்த்து"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"விரும்பிய இடத்தில் கட்டத்தைச் சேர்க்கலாம்"</string>
@@ -1570,5 +1584,7 @@
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"தெரியவில்லை"</string>
<string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"அனைத்துக் கட்டங்களையும் மீட்டமைக்கவா?"</string>
<string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"விரைவு அமைப்புகளின் கட்டங்கள் அனைத்தும் சாதனத்தின் அசல் அமைப்புகளுக்கு மீட்டமைக்கப்படும்"</string>
+ <!-- no translation found for demote_explain_text (1600426458580544250) -->
+ <skip />
<string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ta/tiles_states_strings.xml b/packages/SystemUI/res/values-ta/tiles_states_strings.xml
index d3773a761c37..8280da4340e9 100644
--- a/packages/SystemUI/res/values-ta/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-ta/tiles_states_strings.xml
@@ -56,9 +56,11 @@
<item msgid="5376619709702103243">"முடக்கப்பட்டுள்ளது"</item>
<item msgid="4875147066469902392">"இயக்கப்பட்டுள்ளது"</item>
</string-array>
- <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) -->
- <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) -->
- <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) -->
+ <string-array name="tile_states_modes_dnd">
+ <item msgid="6509540227356524582">"கிடைக்கவில்லை"</item>
+ <item msgid="8589336868985358191">"ஆஃப்"</item>
+ <item msgid="726072717827778234">"ஆன்"</item>
+ </string-array>
<string-array name="tile_states_flashlight">
<item msgid="3465257127433353857">"கிடைக்கவில்லை"</item>
<item msgid="5044688398303285224">"முடக்கப்பட்டுள்ளது"</item>
diff --git a/packages/SystemUI/res/values-te/strings.xml b/packages/SystemUI/res/values-te/strings.xml
index 1bf3ab2e913f..d1bc7e53afb5 100644
--- a/packages/SystemUI/res/values-te/strings.xml
+++ b/packages/SystemUI/res/values-te/strings.xml
@@ -419,6 +419,11 @@
<string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"కొత్త పరికరాన్ని పెయిర్ చేయడానికి క్లిక్ చేయండి"</string>
<string name="hearing_devices_presets_error" msgid="350363093458408536">"ప్రీసెట్‌ను అప్‌డేట్ చేయడం సాధ్యపడలేదు"</string>
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"ప్రీసెట్"</string>
+ <string name="hearing_devices_input_routing_label" msgid="730396728151232306">"కాల్స్ అందుకునే ఆటోమేటిక్ మైక్రోఫోన్"</string>
+ <string-array name="hearing_device_input_routing_options">
+ <item msgid="4582190415045337003">"వినికిడి పరికర మైక్రోఫోన్"</item>
+ <item msgid="8501466270452446450">"ఈ ఫోన్ మైక్రోఫోన్"</item>
+ </string-array>
<string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"ఎంచుకోబడింది"</string>
<string name="hearing_devices_ambient_label" msgid="629440938614895797">"పరిసరాలు"</string>
<string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"ఎడమ వైపునకు"</string>
@@ -582,6 +587,7 @@
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"మీరు యాప్‌ను షేర్ చేసేటప్పుడు, సంబంధిత యాప్‌లో కనిపించేవి లేదా ప్లే అయ్యేవన్నీ <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>‌కు కనిపిస్తాయి. కాబట్టి పాస్‌వర్డ్‌లు, పేమెంట్ వివరాలు, మెసేజ్‌లు, ఫోటోలు, ఆడియో, ఇంకా వీడియో వంటి విషయాల్లో జాగ్రత్త వహించండి."</string>
<string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"స్క్రీన్‌ను షేర్ చేయండి"</string>
<string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g> ఈ ఆప్షన్‌ను డిజేబుల్ చేసింది"</string>
+ <string name="media_projection_entry_app_permission_dialog_single_app_not_supported" msgid="4860247304058870233">"యాప్ ద్వారా సపోర్ట్ చేయబడలేదు"</string>
<string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"షేర్ చేయడానికి యాప్‌ను ఎంచుకోండి"</string>
<string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"మీ స్క్రీన్‌ను ప్రసారం చేయాలా?"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"ఒక యాప్‌ను ప్రసారం చేయండి"</string>
@@ -794,8 +800,7 @@
<string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"\'ముఖం ఆధారం\'ను - ఆన్ చేయండి"</string>
<string name="inline_done_button" msgid="6043094985588909584">"పూర్తయింది"</string>
<string name="inline_ok_button" msgid="603075490581280343">"అప్లయి చేయి"</string>
- <!-- no translation found for inline_turn_off_notifications (2653064779176881329) -->
- <skip />
+ <string name="inline_turn_off_notifications" msgid="2653064779176881329">"ఆఫ్ చేయండి"</string>
<string name="notification_silence_title" msgid="8608090968400832335">"నిశ్శబ్దం"</string>
<string name="notification_alert_title" msgid="3656229781017543655">"ఆటోమేటిక్ సెట్టింగ్"</string>
<string name="notification_automatic_title" msgid="3745465364578762652">"ఆటోమేటిక్"</string>
@@ -977,9 +982,13 @@
<string name="right_icon" msgid="1103955040645237425">"కుడివైపు ఉన్న చిహ్నం"</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"టైల్స్‌ను జోడించడానికి పట్టుకుని, లాగండి"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"టైల్స్‌ను వేరే క్రమంలో అమర్చడానికి వాటిని పట్టుకుని, లాగండి"</string>
+ <!-- no translation found for tap_to_position_tile (6282815817773342757) -->
+ <skip />
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"తీసివేయడానికి ఇక్కడికి లాగండి"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"మీ వద్ద కనీసం <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> టైల్‌లు ఉండాలి"</string>
<string name="qs_edit" msgid="5583565172803472437">"ఎడిట్ చేయండి"</string>
+ <!-- no translation found for qs_edit_tiles (2105215324060865035) -->
+ <skip />
<string name="tuner_time" msgid="2450785840990529997">"సమయం"</string>
<string-array name="clock_options">
<item msgid="3986445361435142273">"గంటలు, నిమిషాలు మరియు సెకన్లను చూపు"</item>
@@ -995,6 +1004,10 @@
<string name="other" msgid="429768510980739978">"ఇతరం"</string>
<string name="accessibility_qs_edit_toggle_tile_size_action" msgid="1485194410119733586">"టైల్ సైజ్‌ను టోగుల్ చేయండి"</string>
<string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"టైల్‌ను తీసివేయండి"</string>
+ <!-- no translation found for accessibility_qs_edit_toggle_placement_mode (3870429389210569610) -->
+ <skip />
+ <!-- no translation found for accessibility_qs_edit_toggle_selection (2201248304072372239) -->
+ <skip />
<string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"చివరి పొజిషన్‌కు టైల్‌ను జోడించండి"</string>
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"టైల్‌ను తరలించండి"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"కావలసిన పొజిషన్‌కు టైల్‌ను జోడించండి"</string>
@@ -1570,5 +1583,7 @@
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"తెలియదు"</string>
<string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"టైల్స్ అన్ని రీసెట్ చేయాలా?"</string>
<string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"అన్ని క్విక్ సెట్టింగ్‌ల టైల్స్, పరికరం తాలూకు ఒరిజినల్ సెట్టింగ్‌లకు రీసెట్ చేయబడతాయి"</string>
+ <!-- no translation found for demote_explain_text (1600426458580544250) -->
+ <skip />
<string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
</resources>
diff --git a/packages/SystemUI/res/values-te/tiles_states_strings.xml b/packages/SystemUI/res/values-te/tiles_states_strings.xml
index 72343a33a082..60a91f82ca9b 100644
--- a/packages/SystemUI/res/values-te/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-te/tiles_states_strings.xml
@@ -56,9 +56,11 @@
<item msgid="5376619709702103243">"ఆఫ్‌లో ఉంది"</item>
<item msgid="4875147066469902392">"ఆన్‌లో ఉంది"</item>
</string-array>
- <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) -->
- <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) -->
- <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) -->
+ <string-array name="tile_states_modes_dnd">
+ <item msgid="6509540227356524582">"అందుబాటులో లేదు"</item>
+ <item msgid="8589336868985358191">"ఆఫ్‌లో ఉంది"</item>
+ <item msgid="726072717827778234">"ఆన్‌లో ఉంది"</item>
+ </string-array>
<string-array name="tile_states_flashlight">
<item msgid="3465257127433353857">"అందుబాటులో లేదు"</item>
<item msgid="5044688398303285224">"ఆఫ్‌లో ఉంది"</item>
diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml
index 99aca811b873..920207bfa122 100644
--- a/packages/SystemUI/res/values-th/strings.xml
+++ b/packages/SystemUI/res/values-th/strings.xml
@@ -419,6 +419,11 @@
<string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"คลิกเพื่อจับคู่อุปกรณ์ใหม่"</string>
<string name="hearing_devices_presets_error" msgid="350363093458408536">"ไม่สามารถอัปเดตค่าที่กำหนดล่วงหน้า"</string>
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"ค่าที่กำหนดล่วงหน้า"</string>
+ <string name="hearing_devices_input_routing_label" msgid="730396728151232306">"ไมโครโฟนเริ่มต้นสำหรับการโทร"</string>
+ <string-array name="hearing_device_input_routing_options">
+ <item msgid="4582190415045337003">"ไมโครโฟนเครื่องช่วยฟัง"</item>
+ <item msgid="8501466270452446450">"ไมโครโฟนของโทรศัพท์เครื่องนี้"</item>
+ </string-array>
<string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"เลือกแล้ว"</string>
<string name="hearing_devices_ambient_label" msgid="629440938614895797">"เสียงแวดล้อม"</string>
<string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"ซ้าย"</string>
@@ -582,6 +587,7 @@
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"เมื่อกำลังแชร์แอป <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> จะมองเห็นทุกสิ่งที่แสดงหรือเล่นอยู่ในแอปดังกล่าว ดังนั้นโปรดระวังสิ่งต่างๆ อย่างเช่นรหัสผ่าน รายละเอียดการชำระเงิน ข้อความ รูปภาพ รวมถึงเสียงและวิดีโอ"</string>
<string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"แชร์หน้าจอ"</string>
<string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g> ปิดใช้ตัวเลือกนี้"</string>
+ <string name="media_projection_entry_app_permission_dialog_single_app_not_supported" msgid="4860247304058870233">"แอปไม่รองรับ"</string>
<string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"เลือกแอปที่จะแชร์"</string>
<string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"แคสต์หน้าจอของคุณไหม"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"แคสต์แอปเดียว"</string>
@@ -794,8 +800,7 @@
<string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"เปิด - ตามใบหน้า"</string>
<string name="inline_done_button" msgid="6043094985588909584">"เสร็จสิ้น"</string>
<string name="inline_ok_button" msgid="603075490581280343">"ใช้"</string>
- <!-- no translation found for inline_turn_off_notifications (2653064779176881329) -->
- <skip />
+ <string name="inline_turn_off_notifications" msgid="2653064779176881329">"ปิด"</string>
<string name="notification_silence_title" msgid="8608090968400832335">"ปิดเสียง"</string>
<string name="notification_alert_title" msgid="3656229781017543655">"ค่าเริ่มต้น"</string>
<string name="notification_automatic_title" msgid="3745465364578762652">"อัตโนมัติ"</string>
@@ -977,9 +982,13 @@
<string name="right_icon" msgid="1103955040645237425">"ไอคอนทางขวา"</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"กดค้างแล้วลากเพื่อเพิ่มการ์ด"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"กดค้างแล้วลากเพื่อจัดเรียงการ์ดใหม่"</string>
+ <!-- no translation found for tap_to_position_tile (6282815817773342757) -->
+ <skip />
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"ลากมาที่นี่เพื่อนำออก"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"คุณต้องมีการ์ดอย่างน้อย <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> รายการ"</string>
<string name="qs_edit" msgid="5583565172803472437">"แก้ไข"</string>
+ <!-- no translation found for qs_edit_tiles (2105215324060865035) -->
+ <skip />
<string name="tuner_time" msgid="2450785840990529997">"เวลา"</string>
<string-array name="clock_options">
<item msgid="3986445361435142273">"แสดงชั่วโมง นาที และวินาที"</item>
@@ -995,6 +1004,10 @@
<string name="other" msgid="429768510980739978">"อื่นๆ"</string>
<string name="accessibility_qs_edit_toggle_tile_size_action" msgid="1485194410119733586">"สลับขนาดของการ์ด"</string>
<string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"นำชิ้นส่วนออก"</string>
+ <!-- no translation found for accessibility_qs_edit_toggle_placement_mode (3870429389210569610) -->
+ <skip />
+ <!-- no translation found for accessibility_qs_edit_toggle_selection (2201248304072372239) -->
+ <skip />
<string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"เพิ่มการ์ดไปยังตำแหน่งสุดท้าย"</string>
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"ย้ายชิ้นส่วน"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"เพิ่มการ์ดไปยังตำแหน่งที่ต้องการ"</string>
@@ -1570,5 +1583,7 @@
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"ไม่ทราบ"</string>
<string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"รีเซ็ตการ์ดทั้งหมดใช่ไหม"</string>
<string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"การ์ดการตั้งค่าด่วนทั้งหมดจะรีเซ็ตเป็นการตั้งค่าเดิมของอุปกรณ์"</string>
+ <!-- no translation found for demote_explain_text (1600426458580544250) -->
+ <skip />
<string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
</resources>
diff --git a/packages/SystemUI/res/values-th/tiles_states_strings.xml b/packages/SystemUI/res/values-th/tiles_states_strings.xml
index b9690568d136..677888b16436 100644
--- a/packages/SystemUI/res/values-th/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-th/tiles_states_strings.xml
@@ -56,9 +56,11 @@
<item msgid="5376619709702103243">"ปิด"</item>
<item msgid="4875147066469902392">"เปิด"</item>
</string-array>
- <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) -->
- <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) -->
- <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) -->
+ <string-array name="tile_states_modes_dnd">
+ <item msgid="6509540227356524582">"ไม่พร้อมใช้งาน"</item>
+ <item msgid="8589336868985358191">"ปิด"</item>
+ <item msgid="726072717827778234">"เปิด"</item>
+ </string-array>
<string-array name="tile_states_flashlight">
<item msgid="3465257127433353857">"ไม่พร้อมใช้งาน"</item>
<item msgid="5044688398303285224">"ปิด"</item>
diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml
index 726cf21e84e4..3625e3a97dba 100644
--- a/packages/SystemUI/res/values-tl/strings.xml
+++ b/packages/SystemUI/res/values-tl/strings.xml
@@ -419,6 +419,11 @@
<string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"I-click para magpares ng bagong device"</string>
<string name="hearing_devices_presets_error" msgid="350363093458408536">"Hindi ma-update ang preset"</string>
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"Preset"</string>
+ <string name="hearing_devices_input_routing_label" msgid="730396728151232306">"Default na mikropono para sa mga tawag"</string>
+ <string-array name="hearing_device_input_routing_options">
+ <item msgid="4582190415045337003">"Mikropono ng hearing aid"</item>
+ <item msgid="8501466270452446450">"Ang mikropono ng teleponong ito"</item>
+ </string-array>
<string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"Napili"</string>
<string name="hearing_devices_ambient_label" msgid="629440938614895797">"Paligid"</string>
<string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Kaliwa"</string>
@@ -582,6 +587,7 @@
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Kapag nagshe-share ka ng app, makikita ng <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ang kahit anong ipinapakita o pine-play sa app na iyon. Kaya mag-ingat sa mga bagay-bagay tulad ng mga password, detalye ng pagbabayad, mensahe, larawan, at audio at video."</string>
<string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Ibahagi ang screen"</string>
<string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"Na-disable ng <xliff:g id="APP_NAME">%1$s</xliff:g> ang opsyong ito"</string>
+ <string name="media_projection_entry_app_permission_dialog_single_app_not_supported" msgid="4860247304058870233">"Hindi sinusuportahan ng app"</string>
<string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"Pumili ng app na ishe-share"</string>
<string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"I-cast ang iyong screen?"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Mag-cast ng isang app"</string>
@@ -794,8 +800,7 @@
<string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Naka-on - Batay sa mukha"</string>
<string name="inline_done_button" msgid="6043094985588909584">"Tapos na"</string>
<string name="inline_ok_button" msgid="603075490581280343">"Ilapat"</string>
- <!-- no translation found for inline_turn_off_notifications (2653064779176881329) -->
- <skip />
+ <string name="inline_turn_off_notifications" msgid="2653064779176881329">"I-off"</string>
<string name="notification_silence_title" msgid="8608090968400832335">"Naka-silent"</string>
<string name="notification_alert_title" msgid="3656229781017543655">"Default"</string>
<string name="notification_automatic_title" msgid="3745465364578762652">"Awtomatiko"</string>
@@ -977,9 +982,13 @@
<string name="right_icon" msgid="1103955040645237425">"Icon ng kanan"</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"Pindutin nang matagal at i-drag para magdagdag ng mga tile"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"Pindutin nang matagal at i-drag para ayusin ulit ang tile"</string>
+ <!-- no translation found for tap_to_position_tile (6282815817773342757) -->
+ <skip />
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"I-drag dito upang alisin"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"Kailangan mo ng kahit <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> (na) tile"</string>
<string name="qs_edit" msgid="5583565172803472437">"I-edit"</string>
+ <!-- no translation found for qs_edit_tiles (2105215324060865035) -->
+ <skip />
<string name="tuner_time" msgid="2450785840990529997">"Oras"</string>
<string-array name="clock_options">
<item msgid="3986445361435142273">"Ipakita ang oras, minuto at segundo"</item>
@@ -995,6 +1004,10 @@
<string name="other" msgid="429768510980739978">"Iba pa"</string>
<string name="accessibility_qs_edit_toggle_tile_size_action" msgid="1485194410119733586">"i-toggle ang laki ng tile"</string>
<string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"alisin ang tile"</string>
+ <!-- no translation found for accessibility_qs_edit_toggle_placement_mode (3870429389210569610) -->
+ <skip />
+ <!-- no translation found for accessibility_qs_edit_toggle_selection (2201248304072372239) -->
+ <skip />
<string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"magdagdag ng tile sa huling posisyon"</string>
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Ilipat ang tile"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"Magdagdag ng tile sa gustong posisyon"</string>
@@ -1570,5 +1583,7 @@
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Hindi Alam"</string>
<string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"I-reset ang lahat ng tile?"</string>
<string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Magre-reset sa mga orihinal na setting ng device ang lahat ng tile ng Mga Mabilisang Setting"</string>
+ <!-- no translation found for demote_explain_text (1600426458580544250) -->
+ <skip />
<string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
</resources>
diff --git a/packages/SystemUI/res/values-tl/tiles_states_strings.xml b/packages/SystemUI/res/values-tl/tiles_states_strings.xml
index 92a122dcf437..84213d5a51c3 100644
--- a/packages/SystemUI/res/values-tl/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-tl/tiles_states_strings.xml
@@ -56,9 +56,11 @@
<item msgid="5376619709702103243">"Naka-off"</item>
<item msgid="4875147066469902392">"Naka-on"</item>
</string-array>
- <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) -->
- <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) -->
- <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) -->
+ <string-array name="tile_states_modes_dnd">
+ <item msgid="6509540227356524582">"Hindi available"</item>
+ <item msgid="8589336868985358191">"Naka-off"</item>
+ <item msgid="726072717827778234">"Naka-on"</item>
+ </string-array>
<string-array name="tile_states_flashlight">
<item msgid="3465257127433353857">"Hindi available"</item>
<item msgid="5044688398303285224">"Naka-off"</item>
diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml
index a68bc74df470..f2d2ae889657 100644
--- a/packages/SystemUI/res/values-tr/strings.xml
+++ b/packages/SystemUI/res/values-tr/strings.xml
@@ -419,6 +419,11 @@
<string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Yeni cihaz eşlemek için tıklayın"</string>
<string name="hearing_devices_presets_error" msgid="350363093458408536">"Hazır ayar güncellenemedi"</string>
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"Hazır Ayar"</string>
+ <string name="hearing_devices_input_routing_label" msgid="730396728151232306">"Aramalar için varsayılan mikrofon"</string>
+ <string-array name="hearing_device_input_routing_options">
+ <item msgid="4582190415045337003">"İşitme cihazı mikrofonu"</item>
+ <item msgid="8501466270452446450">"Bu telefonun mikrofonu"</item>
+ </string-array>
<string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"Seçili"</string>
<string name="hearing_devices_ambient_label" msgid="629440938614895797">"Çevredeki sesler"</string>
<string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Sol"</string>
@@ -582,6 +587,8 @@
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>, bir uygulamayı paylaştığınızda o uygulamada gösterilen veya oynatılan her şeyi görebilir. Bu nedenle şifre, ödeme ayrıntıları, mesaj, fotoğraf, ses ve video gibi öğeler konusunda dikkatli olun."</string>
<string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Ekranı paylaş"</string>
<string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g> bu seçeneği devre dışı bıraktı"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_not_supported (4860247304058870233) -->
+ <skip />
<string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"Paylaşılacak uygulamayı seçin"</string>
<string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Ekranınız yayınlansın mı?"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"1 uygulamayı yayınla"</string>
@@ -794,8 +801,7 @@
<string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Açık - Yüze göre"</string>
<string name="inline_done_button" msgid="6043094985588909584">"Bitti"</string>
<string name="inline_ok_button" msgid="603075490581280343">"Uygula"</string>
- <!-- no translation found for inline_turn_off_notifications (2653064779176881329) -->
- <skip />
+ <string name="inline_turn_off_notifications" msgid="2653064779176881329">"Kapat"</string>
<string name="notification_silence_title" msgid="8608090968400832335">"Sessiz"</string>
<string name="notification_alert_title" msgid="3656229781017543655">"Varsayılan"</string>
<string name="notification_automatic_title" msgid="3745465364578762652">"Otomatik"</string>
@@ -977,9 +983,13 @@
<string name="right_icon" msgid="1103955040645237425">"Sağ simge"</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"Kutu eklemek için basılı tutup sürükleyin"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"Kutuları yeniden düzenlemek için basılı tutun ve sürükleyin"</string>
+ <!-- no translation found for tap_to_position_tile (6282815817773342757) -->
+ <skip />
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"Kaldırmak için buraya sürükleyin"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"En az <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> kutu gerekiyor"</string>
<string name="qs_edit" msgid="5583565172803472437">"Düzenle"</string>
+ <!-- no translation found for qs_edit_tiles (2105215324060865035) -->
+ <skip />
<string name="tuner_time" msgid="2450785840990529997">"Saat"</string>
<string-array name="clock_options">
<item msgid="3986445361435142273">"Saati, dakikayı ve saniyeyi göster"</item>
@@ -995,6 +1005,10 @@
<string name="other" msgid="429768510980739978">"Diğer"</string>
<string name="accessibility_qs_edit_toggle_tile_size_action" msgid="1485194410119733586">"kutu boyutunu değiştir"</string>
<string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"Kutuyu kaldırmak için"</string>
+ <!-- no translation found for accessibility_qs_edit_toggle_placement_mode (3870429389210569610) -->
+ <skip />
+ <!-- no translation found for accessibility_qs_edit_toggle_selection (2201248304072372239) -->
+ <skip />
<string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"kutuyu son konuma ekleyin"</string>
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Kutuyu taşı"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"kutuyu istediğiniz konuma ekleyin"</string>
@@ -1570,5 +1584,7 @@
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Bilinmiyor"</string>
<string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"Tüm ayar kutuları sıfırlansın mı?"</string>
<string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Tüm Hızlı Ayarlar kutuları cihazın özgün ayarlarına sıfırlanır"</string>
+ <!-- no translation found for demote_explain_text (1600426458580544250) -->
+ <skip />
<string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
</resources>
diff --git a/packages/SystemUI/res/values-tr/tiles_states_strings.xml b/packages/SystemUI/res/values-tr/tiles_states_strings.xml
index 986981f21237..3efa8ea96fb3 100644
--- a/packages/SystemUI/res/values-tr/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-tr/tiles_states_strings.xml
@@ -56,9 +56,11 @@
<item msgid="5376619709702103243">"Kapalı"</item>
<item msgid="4875147066469902392">"Açık"</item>
</string-array>
- <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) -->
- <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) -->
- <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) -->
+ <string-array name="tile_states_modes_dnd">
+ <item msgid="6509540227356524582">"Kullanılamıyor"</item>
+ <item msgid="8589336868985358191">"Kapalı"</item>
+ <item msgid="726072717827778234">"Açık"</item>
+ </string-array>
<string-array name="tile_states_flashlight">
<item msgid="3465257127433353857">"Kullanılamıyor"</item>
<item msgid="5044688398303285224">"Kapalı"</item>
diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml
index 2e0229393897..d9dc96b05152 100644
--- a/packages/SystemUI/res/values-uk/strings.xml
+++ b/packages/SystemUI/res/values-uk/strings.xml
@@ -419,6 +419,11 @@
<string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Натисніть, щоб підключити новий пристрій"</string>
<string name="hearing_devices_presets_error" msgid="350363093458408536">"Не вдалось оновити набір налаштувань"</string>
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"Набір налаштувань"</string>
+ <string name="hearing_devices_input_routing_label" msgid="730396728151232306">"Мікрофон за умовчанням для дзвінків"</string>
+ <string-array name="hearing_device_input_routing_options">
+ <item msgid="4582190415045337003">"Мікрофон слухового апарата"</item>
+ <item msgid="8501466270452446450">"Мікрофон цього телефона"</item>
+ </string-array>
<string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"Вибрано"</string>
<string name="hearing_devices_ambient_label" msgid="629440938614895797">"Звуки оточення"</string>
<string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Ліворуч"</string>
@@ -582,6 +587,8 @@
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Коли ви показуєте вікно додатка, увесь контент, що відображається або відтворюється в ньому, стає видимим у додатку <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Тому будьте обережні з паролями, повідомленнями, фотографіями, аудіо, відео, платіжною інформацією тощо."</string>
<string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Показати екран"</string>
<string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"Додаток <xliff:g id="APP_NAME">%1$s</xliff:g> вимкнув цю опцію"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_not_supported (4860247304058870233) -->
+ <skip />
<string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"Виберіть додаток для показу"</string>
<string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Транслювати екран?"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Транслювати один додаток"</string>
@@ -794,8 +801,7 @@
<string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Увімкнути (за обличчям)"</string>
<string name="inline_done_button" msgid="6043094985588909584">"Готово"</string>
<string name="inline_ok_button" msgid="603075490581280343">"Застосувати"</string>
- <!-- no translation found for inline_turn_off_notifications (2653064779176881329) -->
- <skip />
+ <string name="inline_turn_off_notifications" msgid="2653064779176881329">"Вимкнути"</string>
<string name="notification_silence_title" msgid="8608090968400832335">"Без звуку"</string>
<string name="notification_alert_title" msgid="3656229781017543655">"За умовчанням"</string>
<string name="notification_automatic_title" msgid="3745465364578762652">"Автоматично"</string>
@@ -977,9 +983,13 @@
<string name="right_icon" msgid="1103955040645237425">"Значок праворуч"</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"Перетягніть потрібні елементи, щоб додати їх"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"Щоб змінити порядок елементів, перетягуйте їх"</string>
+ <!-- no translation found for tap_to_position_tile (6282815817773342757) -->
+ <skip />
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"Перетягніть сюди, щоб видалити"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"Мінімальна кількість фрагментів: <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g>"</string>
<string name="qs_edit" msgid="5583565172803472437">"Редагувати"</string>
+ <!-- no translation found for qs_edit_tiles (2105215324060865035) -->
+ <skip />
<string name="tuner_time" msgid="2450785840990529997">"Час"</string>
<string-array name="clock_options">
<item msgid="3986445361435142273">"Показувати години, хвилини та секунди"</item>
@@ -995,6 +1005,10 @@
<string name="other" msgid="429768510980739978">"Інше"</string>
<string name="accessibility_qs_edit_toggle_tile_size_action" msgid="1485194410119733586">"змінити розмір плитки"</string>
<string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"вилучити опцію"</string>
+ <!-- no translation found for accessibility_qs_edit_toggle_placement_mode (3870429389210569610) -->
+ <skip />
+ <!-- no translation found for accessibility_qs_edit_toggle_selection (2201248304072372239) -->
+ <skip />
<string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"додати панель на останню позицію"</string>
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Перемістити опцію"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"додати панель на потрібну позицію"</string>
@@ -1570,5 +1584,7 @@
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Невідомо"</string>
<string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"Скинути всі панелі?"</string>
<string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Усі панелі швидких налаштувань буде скинуто до стандартних налаштувань пристрою"</string>
+ <!-- no translation found for demote_explain_text (1600426458580544250) -->
+ <skip />
<string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
</resources>
diff --git a/packages/SystemUI/res/values-uk/tiles_states_strings.xml b/packages/SystemUI/res/values-uk/tiles_states_strings.xml
index a31e858074e0..f5b0f80d32f7 100644
--- a/packages/SystemUI/res/values-uk/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-uk/tiles_states_strings.xml
@@ -56,9 +56,11 @@
<item msgid="5376619709702103243">"Вимкнено"</item>
<item msgid="4875147066469902392">"Увімкнено"</item>
</string-array>
- <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) -->
- <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) -->
- <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) -->
+ <string-array name="tile_states_modes_dnd">
+ <item msgid="6509540227356524582">"Недоступно"</item>
+ <item msgid="8589336868985358191">"Вимкнено"</item>
+ <item msgid="726072717827778234">"Увімкнено"</item>
+ </string-array>
<string-array name="tile_states_flashlight">
<item msgid="3465257127433353857">"Недоступно"</item>
<item msgid="5044688398303285224">"Вимкнено"</item>
diff --git a/packages/SystemUI/res/values-ur/strings.xml b/packages/SystemUI/res/values-ur/strings.xml
index c5a96096a86b..0658c23e817e 100644
--- a/packages/SystemUI/res/values-ur/strings.xml
+++ b/packages/SystemUI/res/values-ur/strings.xml
@@ -419,6 +419,11 @@
<string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"نئے آلے کا جوڑا بنانے کے لیے کلک کریں"</string>
<string name="hearing_devices_presets_error" msgid="350363093458408536">"پہلے سے ترتیب شدہ کو اپ ڈیٹ نہیں کیا جا سکا"</string>
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"پہلے سے ترتیب شدہ"</string>
+ <string name="hearing_devices_input_routing_label" msgid="730396728151232306">"کالز کے لیے ڈیفالٹ مائیکروفون"</string>
+ <string-array name="hearing_device_input_routing_options">
+ <item msgid="4582190415045337003">"سماعتی آلہ مائیکروفون"</item>
+ <item msgid="8501466270452446450">"اس فون کا مائیکروفون"</item>
+ </string-array>
<string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"منتخب کردہ"</string>
<string name="hearing_devices_ambient_label" msgid="629440938614895797">"اطراف"</string>
<string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"دائیں"</string>
@@ -582,6 +587,8 @@
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"آپ کے کسی ایپ کا اشتراک کرنے پر اس ایپ میں دکھائی گئی یا چلائی گئی ہر چیز <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> کیلئے مرئی ہو جاتی ہے۔ لہذا، پاس ورڈز، ادائیگی کی تفصیلات، پیغامات، تصاویر، ساتھ ہی آڈیو اور ویڈیو جیسی چیزوں کے سلسلے میں محتاط رہیں۔"</string>
<string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"اسکرین کا اشتراک کریں"</string>
<string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g> نے اس اختیار کو غیر فعال کر دیا ہے"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_not_supported (4860247304058870233) -->
+ <skip />
<string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"اشتراک کرنے کیلئے ایپ منتخب کریں"</string>
<string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"اپنی اسکرین کاسٹ کریں؟"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"ایک ایپ کاسٹ کریں"</string>
@@ -794,8 +801,7 @@
<string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"آن - چہرے پر مبنی"</string>
<string name="inline_done_button" msgid="6043094985588909584">"ہو گیا"</string>
<string name="inline_ok_button" msgid="603075490581280343">"لاگو کریں"</string>
- <!-- no translation found for inline_turn_off_notifications (2653064779176881329) -->
- <skip />
+ <string name="inline_turn_off_notifications" msgid="2653064779176881329">"آف کریں"</string>
<string name="notification_silence_title" msgid="8608090968400832335">"خاموش"</string>
<string name="notification_alert_title" msgid="3656229781017543655">"ڈیفالٹ"</string>
<string name="notification_automatic_title" msgid="3745465364578762652">"خودکار"</string>
@@ -977,9 +983,13 @@
<string name="right_icon" msgid="1103955040645237425">"دائيں جانب کا آئيکن"</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"ٹائلز شامل کرنے کے لئے پکڑ کر گھسیٹیں"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"ٹائلز کو دوبارہ ترتیب دینے کیلئے پکڑ کر گھسیٹیں"</string>
+ <!-- no translation found for tap_to_position_tile (6282815817773342757) -->
+ <skip />
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"ہٹانے کیلئے یہاں گھسیٹیں؟"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"آپ کو کم از کم <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> ٹائلز کی ضرورت ہے"</string>
<string name="qs_edit" msgid="5583565172803472437">"ترمیم کریں"</string>
+ <!-- no translation found for qs_edit_tiles (2105215324060865035) -->
+ <skip />
<string name="tuner_time" msgid="2450785840990529997">"وقت"</string>
<string-array name="clock_options">
<item msgid="3986445361435142273">"گھنٹے، منٹ اور سیکنڈ دکھائیں"</item>
@@ -995,6 +1005,10 @@
<string name="other" msgid="429768510980739978">"دیگر"</string>
<string name="accessibility_qs_edit_toggle_tile_size_action" msgid="1485194410119733586">"ٹائل کے سائز کو ٹوگل کریں"</string>
<string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"ٹائل ہٹائیں"</string>
+ <!-- no translation found for accessibility_qs_edit_toggle_placement_mode (3870429389210569610) -->
+ <skip />
+ <!-- no translation found for accessibility_qs_edit_toggle_selection (2201248304072372239) -->
+ <skip />
<string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"ٹائل کو آخری پوزیشن پر شامل کریں"</string>
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"ٹائل منتقل کریں"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"ٹائل کو مطلوبہ پوزیشن پر شامل کریں"</string>
@@ -1570,5 +1584,7 @@
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"نامعلوم"</string>
<string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"سبھی ٹائلز ری سیٹ کریں؟"</string>
<string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"سبھی فوری ترتیبات کی ٹائلز آلہ کی اصل ترتیبات پر ری سیٹ ہو جائیں گی"</string>
+ <!-- no translation found for demote_explain_text (1600426458580544250) -->
+ <skip />
<string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>، <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ur/tiles_states_strings.xml b/packages/SystemUI/res/values-ur/tiles_states_strings.xml
index ea032f1451bd..88698b5c7514 100644
--- a/packages/SystemUI/res/values-ur/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-ur/tiles_states_strings.xml
@@ -56,9 +56,11 @@
<item msgid="5376619709702103243">"آف ہے"</item>
<item msgid="4875147066469902392">"آن ہے"</item>
</string-array>
- <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) -->
- <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) -->
- <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) -->
+ <string-array name="tile_states_modes_dnd">
+ <item msgid="6509540227356524582">"دستیاب نہیں ہے"</item>
+ <item msgid="8589336868985358191">"آف"</item>
+ <item msgid="726072717827778234">"آن"</item>
+ </string-array>
<string-array name="tile_states_flashlight">
<item msgid="3465257127433353857">"دستیاب نہیں ہے"</item>
<item msgid="5044688398303285224">"آف ہے"</item>
diff --git a/packages/SystemUI/res/values-uz/strings.xml b/packages/SystemUI/res/values-uz/strings.xml
index fee0eb9c5473..3521d16d1e20 100644
--- a/packages/SystemUI/res/values-uz/strings.xml
+++ b/packages/SystemUI/res/values-uz/strings.xml
@@ -419,6 +419,11 @@
<string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Yangi qurilmani ulash uchun bosing"</string>
<string name="hearing_devices_presets_error" msgid="350363093458408536">"Andoza yangilanmadi"</string>
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"Andoza"</string>
+ <string name="hearing_devices_input_routing_label" msgid="730396728151232306">"Chaqiruvlar uchun asosiy mikrofon"</string>
+ <string-array name="hearing_device_input_routing_options">
+ <item msgid="4582190415045337003">"Eshitish moslamasi mikrofoni"</item>
+ <item msgid="8501466270452446450">"Bu telefonning mikrofoni"</item>
+ </string-array>
<string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"Tanlangan"</string>
<string name="hearing_devices_ambient_label" msgid="629440938614895797">"Atrof-muhit"</string>
<string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Chap"</string>
@@ -582,6 +587,7 @@
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Ilovani namoyish qilayotganingizda oʻsha ilova ichida koʻrsatilayotgan yoki ijro qilinayotganlar <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>ga koʻrinadi. Shu sababli parollar, toʻlov tafsilotlari, xabarlar, suratlar, audio va video chiqmasligi uchun ehtiyot boʻling."</string>
<string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Ekranni namoyish qilish"</string>
<string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g> bu sozlamani faolsizlantirgan"</string>
+ <string name="media_projection_entry_app_permission_dialog_single_app_not_supported" msgid="4860247304058870233">"Ilova tomonidan dastaklanmaydi"</string>
<string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"Ulashiladigan ilovani tanlash"</string>
<string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Ekraningiz uzatilsinmi?"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Bitta ilovani uzatish"</string>
@@ -794,8 +800,7 @@
<string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Yoqish - Yuz asosida"</string>
<string name="inline_done_button" msgid="6043094985588909584">"Tayyor"</string>
<string name="inline_ok_button" msgid="603075490581280343">"Tatbiq etish"</string>
- <!-- no translation found for inline_turn_off_notifications (2653064779176881329) -->
- <skip />
+ <string name="inline_turn_off_notifications" msgid="2653064779176881329">"Faolsizlantirish"</string>
<string name="notification_silence_title" msgid="8608090968400832335">"Sokin"</string>
<string name="notification_alert_title" msgid="3656229781017543655">"Standart"</string>
<string name="notification_automatic_title" msgid="3745465364578762652">"Avtomatik"</string>
@@ -977,9 +982,13 @@
<string name="right_icon" msgid="1103955040645237425">"O‘ngga belgisi"</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"Keraklisini ushlab torting"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"Qayta tartiblash uchun ushlab torting"</string>
+ <!-- no translation found for tap_to_position_tile (6282815817773342757) -->
+ <skip />
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"O‘chirish uchun bu yerga torting"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"Kamida <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> ta katakcha lozim"</string>
<string name="qs_edit" msgid="5583565172803472437">"Tahrirlash"</string>
+ <!-- no translation found for qs_edit_tiles (2105215324060865035) -->
+ <skip />
<string name="tuner_time" msgid="2450785840990529997">"Vaqt"</string>
<string-array name="clock_options">
<item msgid="3986445361435142273">"Soat, daqiqa va soniyalar ko‘rsatilsin"</item>
@@ -995,6 +1004,10 @@
<string name="other" msgid="429768510980739978">"Boshqa"</string>
<string name="accessibility_qs_edit_toggle_tile_size_action" msgid="1485194410119733586">"katak oʻlchamini almashtirish"</string>
<string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"katakchani olib tashlash"</string>
+ <!-- no translation found for accessibility_qs_edit_toggle_placement_mode (3870429389210569610) -->
+ <skip />
+ <!-- no translation found for accessibility_qs_edit_toggle_selection (2201248304072372239) -->
+ <skip />
<string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"kartochkani oxirgi oʻringa qoʻshish"</string>
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Katakchani boshqa joyga olish"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"Kartochkani kerakli oʻringa qoʻshish"</string>
@@ -1570,5 +1583,7 @@
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Noaniq"</string>
<string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"Barcha katakchalar asliga qaytarilsinmi?"</string>
<string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Barcha Tezkor sozlamalar katakchalari qurilmaning asl sozlamalariga qaytariladi"</string>
+ <!-- no translation found for demote_explain_text (1600426458580544250) -->
+ <skip />
<string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
</resources>
diff --git a/packages/SystemUI/res/values-uz/tiles_states_strings.xml b/packages/SystemUI/res/values-uz/tiles_states_strings.xml
index 1433a9b79071..7afb9bdc8034 100644
--- a/packages/SystemUI/res/values-uz/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-uz/tiles_states_strings.xml
@@ -56,9 +56,11 @@
<item msgid="5376619709702103243">"Oʻchiq"</item>
<item msgid="4875147066469902392">"Yoniq"</item>
</string-array>
- <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) -->
- <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) -->
- <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) -->
+ <string-array name="tile_states_modes_dnd">
+ <item msgid="6509540227356524582">"Mavjud emas"</item>
+ <item msgid="8589336868985358191">"Yoqilmagan"</item>
+ <item msgid="726072717827778234">"Yoniq"</item>
+ </string-array>
<string-array name="tile_states_flashlight">
<item msgid="3465257127433353857">"Ishlamaydi"</item>
<item msgid="5044688398303285224">"Oʻchiq"</item>
diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml
index c79eef76e6e4..cf36d6908c15 100644
--- a/packages/SystemUI/res/values-vi/strings.xml
+++ b/packages/SystemUI/res/values-vi/strings.xml
@@ -419,6 +419,11 @@
<string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Nhấp để ghép nối thiết bị mới"</string>
<string name="hearing_devices_presets_error" msgid="350363093458408536">"Không cập nhật được giá trị đặt trước"</string>
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"Chế độ đặt sẵn"</string>
+ <string name="hearing_devices_input_routing_label" msgid="730396728151232306">"Micrô mặc định cho cuộc gọi"</string>
+ <string-array name="hearing_device_input_routing_options">
+ <item msgid="4582190415045337003">"Micrô trên thiết bị trợ thính"</item>
+ <item msgid="8501466270452446450">"Micrô của điện thoại này"</item>
+ </string-array>
<string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"Đã chọn"</string>
<string name="hearing_devices_ambient_label" msgid="629440938614895797">"Âm lượng xung quanh"</string>
<string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Trái"</string>
@@ -582,6 +587,8 @@
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Khi bạn chia sẻ một ứng dụng, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> sẽ thấy được mọi nội dung hiển thị hoặc phát trong ứng dụng đó. Vì vậy, hãy thận trọng để không làm lộ thông tin như mật khẩu, thông tin thanh toán, tin nhắn, ảnh, âm thanh và video."</string>
<string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Chia sẻ màn hình"</string>
<string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g> đã tắt lựa chọn này"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_not_supported (4860247304058870233) -->
+ <skip />
<string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"Chọn ứng dụng để chia sẻ"</string>
<string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Truyền màn hình?"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Truyền một ứng dụng"</string>
@@ -794,8 +801,7 @@
<string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Đang bật – Dựa trên khuôn mặt"</string>
<string name="inline_done_button" msgid="6043094985588909584">"Xong"</string>
<string name="inline_ok_button" msgid="603075490581280343">"Áp dụng"</string>
- <!-- no translation found for inline_turn_off_notifications (2653064779176881329) -->
- <skip />
+ <string name="inline_turn_off_notifications" msgid="2653064779176881329">"Tắt"</string>
<string name="notification_silence_title" msgid="8608090968400832335">"Im lặng"</string>
<string name="notification_alert_title" msgid="3656229781017543655">"Mặc định"</string>
<string name="notification_automatic_title" msgid="3745465364578762652">"Tự động"</string>
@@ -977,9 +983,13 @@
<string name="right_icon" msgid="1103955040645237425">"Biểu tượng bên phải"</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"Giữ và kéo để thêm ô"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"Giữ và kéo để sắp xếp lại các ô"</string>
+ <!-- no translation found for tap_to_position_tile (6282815817773342757) -->
+ <skip />
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"Kéo vào đây để xóa"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"Bạn cần ít nhất <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> ô"</string>
<string name="qs_edit" msgid="5583565172803472437">"Chỉnh sửa"</string>
+ <!-- no translation found for qs_edit_tiles (2105215324060865035) -->
+ <skip />
<string name="tuner_time" msgid="2450785840990529997">"Thời gian"</string>
<string-array name="clock_options">
<item msgid="3986445361435142273">"Hiển thị giờ, phút và giây"</item>
@@ -995,6 +1005,10 @@
<string name="other" msgid="429768510980739978">"Khác"</string>
<string name="accessibility_qs_edit_toggle_tile_size_action" msgid="1485194410119733586">"bật hoặc tắt kích thước của ô"</string>
<string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"xóa ô"</string>
+ <!-- no translation found for accessibility_qs_edit_toggle_placement_mode (3870429389210569610) -->
+ <skip />
+ <!-- no translation found for accessibility_qs_edit_toggle_selection (2201248304072372239) -->
+ <skip />
<string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"thêm ô vào vị trí cuối cùng"</string>
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Di chuyển ô"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"Thêm ô vào vị trí mong muốn"</string>
@@ -1570,5 +1584,7 @@
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Không xác định"</string>
<string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"Đặt lại mọi ô?"</string>
<string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Mọi ô Cài đặt nhanh sẽ được đặt lại về chế độ cài đặt ban đầu của thiết bị"</string>
+ <!-- no translation found for demote_explain_text (1600426458580544250) -->
+ <skip />
<string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
</resources>
diff --git a/packages/SystemUI/res/values-vi/tiles_states_strings.xml b/packages/SystemUI/res/values-vi/tiles_states_strings.xml
index 02d2bb6be45f..78e39a75e117 100644
--- a/packages/SystemUI/res/values-vi/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-vi/tiles_states_strings.xml
@@ -56,9 +56,11 @@
<item msgid="5376619709702103243">"Đang tắt"</item>
<item msgid="4875147066469902392">"Đang bật"</item>
</string-array>
- <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) -->
- <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) -->
- <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) -->
+ <string-array name="tile_states_modes_dnd">
+ <item msgid="6509540227356524582">"Không có"</item>
+ <item msgid="8589336868985358191">"Đang tắt"</item>
+ <item msgid="726072717827778234">"Đang bật"</item>
+ </string-array>
<string-array name="tile_states_flashlight">
<item msgid="3465257127433353857">"Không hoạt động"</item>
<item msgid="5044688398303285224">"Đang tắt"</item>
diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml
index f0f55a827930..e469e38fb754 100644
--- a/packages/SystemUI/res/values-zh-rCN/strings.xml
+++ b/packages/SystemUI/res/values-zh-rCN/strings.xml
@@ -106,7 +106,7 @@
<string name="backlinks_include_link" msgid="4562093591148248158">"包括链接"</string>
<string name="backlinks_duplicate_label_format" msgid="558445128952827926">"<xliff:g id="APPNAME">%1$s</xliff:g> <xliff:g id="FREQUENCYCOUNT">(%2$d)</xliff:g>⁠"</string>
<string name="backlinks_cross_profile_error" msgid="1355798585727802282">"无法添加来自其他个人资料的链接"</string>
- <string name="screenrecord_title" msgid="4257171601439507792">"屏幕录制器"</string>
+ <string name="screenrecord_title" msgid="4257171601439507792">"屏幕录制"</string>
<string name="screenrecord_background_processing_label" msgid="7244617554884238898">"正在处理屏幕录制视频"</string>
<string name="screenrecord_channel_description" msgid="4147077128486138351">"持续显示屏幕录制会话通知"</string>
<string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"要录制屏幕吗?"</string>
@@ -419,6 +419,11 @@
<string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"点击即可与新设备配对"</string>
<string name="hearing_devices_presets_error" msgid="350363093458408536">"无法更新预设"</string>
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"预设"</string>
+ <string name="hearing_devices_input_routing_label" msgid="730396728151232306">"通话的默认麦克风"</string>
+ <string-array name="hearing_device_input_routing_options">
+ <item msgid="4582190415045337003">"助听器麦克风"</item>
+ <item msgid="8501466270452446450">"此手机的麦克风"</item>
+ </string-array>
<string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"已选择"</string>
<string name="hearing_devices_ambient_label" msgid="629440938614895797">"周围声音"</string>
<string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"左侧"</string>
@@ -582,6 +587,8 @@
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"当您共享一个应用时,该应用中显示或播放的所有内容均对“<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>”可见。因此,请务必小心操作,谨防泄露密码、付款信息、消息、照片、音频、视频等。"</string>
<string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"共享屏幕"</string>
<string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"“<xliff:g id="APP_NAME">%1$s</xliff:g>”已停用此选项"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_not_supported (4860247304058870233) -->
+ <skip />
<string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"选择要分享的应用"</string>
<string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"投放您的屏幕?"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"投放单个应用"</string>
@@ -794,8 +801,7 @@
<string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"已开启 - 基于人脸"</string>
<string name="inline_done_button" msgid="6043094985588909584">"完成"</string>
<string name="inline_ok_button" msgid="603075490581280343">"应用"</string>
- <!-- no translation found for inline_turn_off_notifications (2653064779176881329) -->
- <skip />
+ <string name="inline_turn_off_notifications" msgid="2653064779176881329">"关闭"</string>
<string name="notification_silence_title" msgid="8608090968400832335">"静音"</string>
<string name="notification_alert_title" msgid="3656229781017543655">"默认"</string>
<string name="notification_automatic_title" msgid="3745465364578762652">"自动"</string>
@@ -977,9 +983,13 @@
<string name="right_icon" msgid="1103955040645237425">"向右图标"</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"按住并拖动即可添加功能块"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"按住并拖动即可重新排列功能块"</string>
+ <!-- no translation found for tap_to_position_tile (6282815817773342757) -->
+ <skip />
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"拖动到此处即可移除"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"您至少需要 <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> 个卡片"</string>
<string name="qs_edit" msgid="5583565172803472437">"编辑"</string>
+ <!-- no translation found for qs_edit_tiles (2105215324060865035) -->
+ <skip />
<string name="tuner_time" msgid="2450785840990529997">"时间"</string>
<string-array name="clock_options">
<item msgid="3986445361435142273">"显示小时、分钟和秒"</item>
@@ -995,6 +1005,10 @@
<string name="other" msgid="429768510980739978">"其他"</string>
<string name="accessibility_qs_edit_toggle_tile_size_action" msgid="1485194410119733586">"切换功能块大小"</string>
<string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"移除功能块"</string>
+ <!-- no translation found for accessibility_qs_edit_toggle_placement_mode (3870429389210569610) -->
+ <skip />
+ <!-- no translation found for accessibility_qs_edit_toggle_selection (2201248304072372239) -->
+ <skip />
<string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"将功能块添加到最后一个位置"</string>
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"移动功能块"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"将功能块添加到所需位置"</string>
@@ -1570,5 +1584,7 @@
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"未知"</string>
<string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"要重置所有功能块吗?"</string>
<string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"所有“快捷设置”功能块都将重置为设备的原始设置"</string>
+ <!-- no translation found for demote_explain_text (1600426458580544250) -->
+ <skip />
<string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>,<xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
</resources>
diff --git a/packages/SystemUI/res/values-zh-rCN/tiles_states_strings.xml b/packages/SystemUI/res/values-zh-rCN/tiles_states_strings.xml
index 5980c314d0d6..b0fd9be8b2fd 100644
--- a/packages/SystemUI/res/values-zh-rCN/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-zh-rCN/tiles_states_strings.xml
@@ -56,9 +56,11 @@
<item msgid="5376619709702103243">"已关闭"</item>
<item msgid="4875147066469902392">"已开启"</item>
</string-array>
- <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) -->
- <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) -->
- <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) -->
+ <string-array name="tile_states_modes_dnd">
+ <item msgid="6509540227356524582">"不可用"</item>
+ <item msgid="8589336868985358191">"关"</item>
+ <item msgid="726072717827778234">"开"</item>
+ </string-array>
<string-array name="tile_states_flashlight">
<item msgid="3465257127433353857">"不可用"</item>
<item msgid="5044688398303285224">"已关闭"</item>
diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml
index caac7437b4e0..4a1291e07894 100644
--- a/packages/SystemUI/res/values-zh-rHK/strings.xml
+++ b/packages/SystemUI/res/values-zh-rHK/strings.xml
@@ -419,6 +419,11 @@
<string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"㩒一下就可以配對新裝置"</string>
<string name="hearing_devices_presets_error" msgid="350363093458408536">"無法更新預設"</string>
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"預設"</string>
+ <string name="hearing_devices_input_routing_label" msgid="730396728151232306">"預設通話用麥克風"</string>
+ <string-array name="hearing_device_input_routing_options">
+ <item msgid="4582190415045337003">"助聽器麥克風"</item>
+ <item msgid="8501466270452446450">"此手機的麥克風"</item>
+ </string-array>
<string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"揀咗"</string>
<string name="hearing_devices_ambient_label" msgid="629440938614895797">"環境聲音"</string>
<string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"左"</string>
@@ -582,6 +587,8 @@
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"當你分享應用程式時,「<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>」可看到該應用程式中顯示或播放的任何內容。因此,請謹慎處理密碼、付款資料、訊息、相片、音訊和影片等。"</string>
<string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"分享螢幕畫面"</string>
<string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"「<xliff:g id="APP_NAME">%1$s</xliff:g>」已停用此選項"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_not_supported (4860247304058870233) -->
+ <skip />
<string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"選擇要分享的應用程式"</string>
<string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"要投放螢幕嗎?"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"投放一個應用程式"</string>
@@ -794,8 +801,7 @@
<string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"已開啟 - 根據面孔偵測"</string>
<string name="inline_done_button" msgid="6043094985588909584">"完成"</string>
<string name="inline_ok_button" msgid="603075490581280343">"套用"</string>
- <!-- no translation found for inline_turn_off_notifications (2653064779176881329) -->
- <skip />
+ <string name="inline_turn_off_notifications" msgid="2653064779176881329">"關閉"</string>
<string name="notification_silence_title" msgid="8608090968400832335">"靜音"</string>
<string name="notification_alert_title" msgid="3656229781017543655">"預設"</string>
<string name="notification_automatic_title" msgid="3745465364578762652">"自動"</string>
@@ -977,9 +983,13 @@
<string name="right_icon" msgid="1103955040645237425">"向右圖示"</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"按住並拖曳即可新增圖塊"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"按住並拖曳即可重新排列圖塊"</string>
+ <!-- no translation found for tap_to_position_tile (6282815817773342757) -->
+ <skip />
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"拖曳這裡即可移除"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"你需要有至少 <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> 個資訊方塊"</string>
<string name="qs_edit" msgid="5583565172803472437">"編輯"</string>
+ <!-- no translation found for qs_edit_tiles (2105215324060865035) -->
+ <skip />
<string name="tuner_time" msgid="2450785840990529997">"時間"</string>
<string-array name="clock_options">
<item msgid="3986445361435142273">"顯示小時、分鐘和秒"</item>
@@ -995,6 +1005,10 @@
<string name="other" msgid="429768510980739978">"其他"</string>
<string name="accessibility_qs_edit_toggle_tile_size_action" msgid="1485194410119733586">"切換圖塊大小"</string>
<string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"移除圖塊"</string>
+ <!-- no translation found for accessibility_qs_edit_toggle_placement_mode (3870429389210569610) -->
+ <skip />
+ <!-- no translation found for accessibility_qs_edit_toggle_selection (2201248304072372239) -->
+ <skip />
<string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"加圖塊去上一個位置"</string>
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"移動圖塊"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"加圖塊去目標位置"</string>
@@ -1570,5 +1584,7 @@
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"不明"</string>
<string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"要重設所有圖塊嗎?"</string>
<string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"所有「快速設定」圖塊將重設為裝置的原始設定"</string>
+ <!-- no translation found for demote_explain_text (1600426458580544250) -->
+ <skip />
<string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>,<xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
</resources>
diff --git a/packages/SystemUI/res/values-zh-rHK/tiles_states_strings.xml b/packages/SystemUI/res/values-zh-rHK/tiles_states_strings.xml
index 7369d9519b34..2dcb0cd290e1 100644
--- a/packages/SystemUI/res/values-zh-rHK/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-zh-rHK/tiles_states_strings.xml
@@ -56,9 +56,11 @@
<item msgid="5376619709702103243">"已關閉"</item>
<item msgid="4875147066469902392">"已開啟"</item>
</string-array>
- <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) -->
- <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) -->
- <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) -->
+ <string-array name="tile_states_modes_dnd">
+ <item msgid="6509540227356524582">"無法使用"</item>
+ <item msgid="8589336868985358191">"已關閉"</item>
+ <item msgid="726072717827778234">"已開啟"</item>
+ </string-array>
<string-array name="tile_states_flashlight">
<item msgid="3465257127433353857">"無法使用"</item>
<item msgid="5044688398303285224">"已關閉"</item>
diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml
index d3a5544ac694..89f2018cfed4 100644
--- a/packages/SystemUI/res/values-zh-rTW/strings.xml
+++ b/packages/SystemUI/res/values-zh-rTW/strings.xml
@@ -419,6 +419,11 @@
<string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"按一下即可配對新裝置"</string>
<string name="hearing_devices_presets_error" msgid="350363093458408536">"無法更新預設設定"</string>
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"預設"</string>
+ <string name="hearing_devices_input_routing_label" msgid="730396728151232306">"預設通話麥克風"</string>
+ <string-array name="hearing_device_input_routing_options">
+ <item msgid="4582190415045337003">"助聽器麥克風"</item>
+ <item msgid="8501466270452446450">"這支手機的麥克風"</item>
+ </string-array>
<string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"已選取"</string>
<string name="hearing_devices_ambient_label" msgid="629440938614895797">"環境"</string>
<string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"左"</string>
@@ -582,6 +587,8 @@
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"當你分享應用程式畫面時,「<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>」可存取該應用程式顯示或播放的所有內容。因此,請謹慎處理密碼、付款資料、訊息、相片和影音內容等資訊。"</string>
<string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"分享畫面"</string>
<string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"「<xliff:g id="APP_NAME">%1$s</xliff:g>」已停用此選項"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_not_supported (4860247304058870233) -->
+ <skip />
<string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"選擇要分享的應用程式"</string>
<string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"要投放畫面嗎?"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"投放一個應用程式"</string>
@@ -794,8 +801,7 @@
<string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"已開啟 - 依臉部方向旋轉"</string>
<string name="inline_done_button" msgid="6043094985588909584">"完成"</string>
<string name="inline_ok_button" msgid="603075490581280343">"套用"</string>
- <!-- no translation found for inline_turn_off_notifications (2653064779176881329) -->
- <skip />
+ <string name="inline_turn_off_notifications" msgid="2653064779176881329">"關閉"</string>
<string name="notification_silence_title" msgid="8608090968400832335">"靜音"</string>
<string name="notification_alert_title" msgid="3656229781017543655">"預設"</string>
<string name="notification_automatic_title" msgid="3745465364578762652">"自動"</string>
@@ -977,9 +983,13 @@
<string name="right_icon" msgid="1103955040645237425">"向右圖示"</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"按住並拖曳即可新增設定方塊"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"按住並拖曳即可重新排列設定方塊"</string>
+ <!-- no translation found for tap_to_position_tile (6282815817773342757) -->
+ <skip />
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"拖曳到這裡即可移除"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"你至少必須要有 <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> 個設定方塊"</string>
<string name="qs_edit" msgid="5583565172803472437">"編輯"</string>
+ <!-- no translation found for qs_edit_tiles (2105215324060865035) -->
+ <skip />
<string name="tuner_time" msgid="2450785840990529997">"時間"</string>
<string-array name="clock_options">
<item msgid="3986445361435142273">"顯示小時、分鐘和秒"</item>
@@ -995,6 +1005,10 @@
<string name="other" msgid="429768510980739978">"其他"</string>
<string name="accessibility_qs_edit_toggle_tile_size_action" msgid="1485194410119733586">"切換設定方塊大小"</string>
<string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"移除圖塊"</string>
+ <!-- no translation found for accessibility_qs_edit_toggle_placement_mode (3870429389210569610) -->
+ <skip />
+ <!-- no translation found for accessibility_qs_edit_toggle_selection (2201248304072372239) -->
+ <skip />
<string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"將設定方塊新增到最後一個位置"</string>
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"移動圖塊"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"將設定方塊新增到所需位置"</string>
@@ -1570,5 +1584,7 @@
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"不明"</string>
<string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"要重設所有設定方塊嗎?"</string>
<string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"所有快速設定方塊都會恢復裝置的原始設定"</string>
+ <!-- no translation found for demote_explain_text (1600426458580544250) -->
+ <skip />
<string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>、<xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
</resources>
diff --git a/packages/SystemUI/res/values-zh-rTW/tiles_states_strings.xml b/packages/SystemUI/res/values-zh-rTW/tiles_states_strings.xml
index 18a6d12dcff9..33313ac14d53 100644
--- a/packages/SystemUI/res/values-zh-rTW/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-zh-rTW/tiles_states_strings.xml
@@ -56,9 +56,11 @@
<item msgid="5376619709702103243">"已關閉"</item>
<item msgid="4875147066469902392">"已開啟"</item>
</string-array>
- <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) -->
- <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) -->
- <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) -->
+ <string-array name="tile_states_modes_dnd">
+ <item msgid="6509540227356524582">"無法使用"</item>
+ <item msgid="8589336868985358191">"已關閉"</item>
+ <item msgid="726072717827778234">"已開啟"</item>
+ </string-array>
<string-array name="tile_states_flashlight">
<item msgid="3465257127433353857">"無法使用"</item>
<item msgid="5044688398303285224">"已關閉"</item>
diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml
index 08657d1bfe54..3bef836a9195 100644
--- a/packages/SystemUI/res/values-zu/strings.xml
+++ b/packages/SystemUI/res/values-zu/strings.xml
@@ -421,6 +421,11 @@
<string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Chofoza ukuze ubhangqe idivayisi entsha"</string>
<string name="hearing_devices_presets_error" msgid="350363093458408536">"Ayikwazanga ukubuyekeza ukusetha ngaphambilini"</string>
<string name="hearing_devices_preset_label" msgid="7878267405046232358">"Ukusetha ngaphambilini"</string>
+ <string name="hearing_devices_input_routing_label" msgid="730396728151232306">"Imakrofoni ezenzekelayo yocingo"</string>
+ <string-array name="hearing_device_input_routing_options">
+ <item msgid="4582190415045337003">"Imakrofoni yomshini wendlebe"</item>
+ <item msgid="8501466270452446450">"Imakrofoni yale foni"</item>
+ </string-array>
<string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"Okukhethiwe"</string>
<string name="hearing_devices_ambient_label" msgid="629440938614895797">"Izindawo ezizungezile"</string>
<string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Kwesokunxele"</string>
@@ -584,6 +589,8 @@
<string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Uma wabelana nge-app, noma yini eboniswayo noma edlalwayo kuleyo app ibonakala ku-<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Ngakho-ke qaphela ngezinto ezifana namaphasiwedi, imininingwane yenkokhelo, imilayezo, izithombe, nomsindo nevidiyo."</string>
<string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Yabelana ngesikrini"</string>
<string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g> ivale le nketho"</string>
+ <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_not_supported (4860247304058870233) -->
+ <skip />
<string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"Khetha i-app yokwabelana"</string>
<string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Sakaza isikrini sakho?"</string>
<string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Sakaza i-app eyodwa"</string>
@@ -796,8 +803,7 @@
<string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Vuliwe - Kususelwe kubuso"</string>
<string name="inline_done_button" msgid="6043094985588909584">"Kwenziwe"</string>
<string name="inline_ok_button" msgid="603075490581280343">"Faka"</string>
- <!-- no translation found for inline_turn_off_notifications (2653064779176881329) -->
- <skip />
+ <string name="inline_turn_off_notifications" msgid="2653064779176881329">"Vala"</string>
<string name="notification_silence_title" msgid="8608090968400832335">"Kuthulile"</string>
<string name="notification_alert_title" msgid="3656229781017543655">"Okuzenzekelayo"</string>
<string name="notification_automatic_title" msgid="3745465364578762652">"Okuzenzekelayo"</string>
@@ -979,9 +985,13 @@
<string name="right_icon" msgid="1103955040645237425">"Isithonjana sangakwesokudla"</string>
<string name="drag_to_add_tiles" msgid="8933270127508303672">"Bamba uphinde uhudule ukuze ungeze amathayela"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"Bamba uphinde uhudule ukuze uphinde ulungise amathayela"</string>
+ <!-- no translation found for tap_to_position_tile (6282815817773342757) -->
+ <skip />
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"Hudulela lapha ukuze ususe"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"Udinga okungenani amathayela angu-<xliff:g id="MIN_NUM_TILES">%1$d</xliff:g>"</string>
<string name="qs_edit" msgid="5583565172803472437">"Hlela"</string>
+ <!-- no translation found for qs_edit_tiles (2105215324060865035) -->
+ <skip />
<string name="tuner_time" msgid="2450785840990529997">"Isikhathi"</string>
<string-array name="clock_options">
<item msgid="3986445361435142273">"Bonisa amahora, amaminithi, namasekhondi"</item>
@@ -997,6 +1007,10 @@
<string name="other" msgid="429768510980739978">"Okunye"</string>
<string name="accessibility_qs_edit_toggle_tile_size_action" msgid="1485194410119733586">"guqula usayizi wethayela"</string>
<string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"susa ithayela"</string>
+ <!-- no translation found for accessibility_qs_edit_toggle_placement_mode (3870429389210569610) -->
+ <skip />
+ <!-- no translation found for accessibility_qs_edit_toggle_selection (2201248304072372239) -->
+ <skip />
<string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"faka ithayela endaweni yokugcina"</string>
<string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Hambisa ithayela"</string>
<string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"Faka ithayela endaweni oyifunayo"</string>
@@ -1572,5 +1586,7 @@
<string name="qs_edit_mode_category_unknown" msgid="509314252124053550">"Akwaziwa"</string>
<string name="qs_edit_mode_reset_dialog_title" msgid="5344853290033761627">"Qala kabusha onke amathayela?"</string>
<string name="qs_edit_mode_reset_dialog_content" msgid="7474773130622653653">"Ithayela Lamasethingi Asheshayo lizosetha kabusha libuyele kumasethingi okuqala edivayisi"</string>
+ <!-- no translation found for demote_explain_text (1600426458580544250) -->
+ <skip />
<string name="volume_slider_disabled_message_template" msgid="1305088816797803460">"<xliff:g id="STREAM_NAME">%1$s</xliff:g>, <xliff:g id="DISABLED_MESSAGE">%2$s</xliff:g>"</string>
</resources>
diff --git a/packages/SystemUI/res/values-zu/tiles_states_strings.xml b/packages/SystemUI/res/values-zu/tiles_states_strings.xml
index 674aeaa3b890..10b26fc31dce 100644
--- a/packages/SystemUI/res/values-zu/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-zu/tiles_states_strings.xml
@@ -56,9 +56,11 @@
<item msgid="5376619709702103243">"Valiwe"</item>
<item msgid="4875147066469902392">"Vuliwe"</item>
</string-array>
- <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) -->
- <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) -->
- <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) -->
+ <string-array name="tile_states_modes_dnd">
+ <item msgid="6509540227356524582">"Ayitholakali"</item>
+ <item msgid="8589336868985358191">"Valiwe"</item>
+ <item msgid="726072717827778234">"Vuliwe"</item>
+ </string-array>
<string-array name="tile_states_flashlight">
<item msgid="3465257127433353857">"Akutholakali"</item>
<item msgid="5044688398303285224">"Valiwe"</item>
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index 15519ff37d0c..7c6a1b1bf63d 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -146,7 +146,8 @@
<color name="smart_reply_button_stroke">@*android:color/accent_device_default</color>
<!-- Magic Action colors -->
- <color name="magic_action_button_text_color">@androidprv:color/materialColorOnSurfaceVariant</color>
+ <color name="magic_action_button_text_color">@androidprv:color/materialColorOnSurface</color>
+ <color name="magic_action_button_stroke_color">@androidprv:color/materialColorOnSurface</color>
<!-- Biometric dialog colors -->
<color name="biometric_dialog_gray">#ff757575</color>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index d0ae307b6919..55e94028b95e 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -989,7 +989,7 @@
<dimen name="keyguard_security_container_padding_top">20dp</dimen>
- <dimen name="keyguard_translate_distance_on_swipe_up">-200dp</dimen>
+ <dimen name="keyguard_translate_distance_on_swipe_up">-180dp</dimen>
<dimen name="keyguard_indication_margin_bottom">32dp</dimen>
<dimen name="ambient_indication_margin_bottom">71dp</dimen>
@@ -2158,28 +2158,25 @@
<dimen name="contextual_edu_dialog_elevation">2dp</dimen>
<!-- Volume start -->
+ <dimen name="volume_dialog_window_width">176dp</dimen>
<dimen name="volume_dialog_width">60dp</dimen>
<dimen name="volume_dialog_background_corner_radius">30dp</dimen>
- <dimen name="volume_dialog_background_vertical_margin">
- @dimen/volume_dialog_buttons_margin_negative
- </dimen>
<!-- top margin covers half the ringer button + components spacing -->
<dimen name="volume_dialog_background_top_margin">-28dp</dimen>
+ <dimen name="volume_dialog_background_margin">10dp</dimen>
+ <dimen name="volume_dialog_background_margin_negative">-10dp</dimen>
- <dimen name="volume_dialog_window_margin">14dp</dimen>
+ <dimen name="volume_dialog_window_margin">12dp</dimen>
<dimen name="volume_dialog_components_spacing">10dp</dimen>
<dimen name="volume_dialog_floating_sliders_spacing">8dp</dimen>
- <dimen name="volume_dialog_floating_sliders_vertical_padding">10dp</dimen>
<dimen name="volume_dialog_floating_sliders_vertical_padding_negative">
- @dimen/volume_dialog_buttons_margin_negative
+ @dimen/volume_dialog_background_margin_negative
</dimen>
- <dimen name="volume_dialog_floating_sliders_horizontal_padding">4dp</dimen>
+ <dimen name="volume_dialog_floating_sliders_padding">4dp</dimen>
<dimen name="volume_dialog_button_size">40dp</dimen>
<dimen name="volume_dialog_slider_width">52dp</dimen>
<dimen name="volume_dialog_slider_height">254dp</dimen>
- <dimen name="volume_dialog_buttons_margin">10dp</dimen>
- <dimen name="volume_dialog_buttons_margin_negative">-10dp</dimen>
<!--
A primary goal of this margin is to vertically constraint slider height in the landscape
orientation when the vertical space is limited
@@ -2190,8 +2187,10 @@
<dimen name="volume_dialog_background_square_corner_radius">12dp</dimen>
- <dimen name="volume_dialog_ringer_drawer_margin">@dimen/volume_dialog_buttons_margin</dimen>
<dimen name="volume_dialog_ringer_drawer_button_size">@dimen/volume_dialog_button_size</dimen>
+ <dimen name="volume_dialog_ringer_drawer_buttons_spacing">
+ @dimen/volume_dialog_components_spacing
+ </dimen>
<dimen name="volume_dialog_ringer_drawer_button_icon_radius">10dp</dimen>
<dimen name="volume_dialog_ringer_selected_button_background_radius">20dp</dimen>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 3fdb98b4e00b..681bd53f1a40 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -1483,6 +1483,8 @@
<string name="media_projection_entry_app_permission_dialog_continue_entire_screen">Share screen</string>
<!-- 1P/3P apps disabled the single app projection option. [CHAR LIMIT=NONE] -->
<string name="media_projection_entry_app_permission_dialog_single_app_disabled"><xliff:g id="app_name" example="Meet">%1$s</xliff:g> has disabled this option</string>
+ <!-- Explanation that the app requesting the projection does not support single app sharing[CHAR LIMIT=70] -->
+ <string name="media_projection_entry_app_permission_dialog_single_app_not_supported">Not supported by the app</string>
<!-- Title of the activity that allows users to select an app to share to a 1P/3P app [CHAR LIMIT=70] -->
<string name="media_projection_entry_share_app_selector_title">Choose app to share</string>
@@ -2549,6 +2551,9 @@
<!-- Label for header of customize QS [CHAR LIMIT=60] -->
<string name="drag_to_rearrange_tiles">Hold and drag to rearrange tiles</string>
+ <!-- Label for placing tiles in edit mode for QS [CHAR LIMIT=60] -->
+ <string name="tap_to_position_tile">Tap to position tile</string>
+
<!-- Label for area where tiles can be dragged in to [CHAR LIMIT=60] -->
<string name="drag_to_remove_tiles">Drag here to remove</string>
@@ -2558,6 +2563,9 @@
<!-- Button to edit the tile ordering of quick settings [CHAR LIMIT=60] -->
<string name="qs_edit">Edit</string>
+ <!-- Title for QS Edit mode screen [CHAR LIMIT=30] -->
+ <string name="qs_edit_tiles">Edit tiles</string>
+
<!-- SysUI Tuner: Options for how clock is displayed [CHAR LIMIT=NONE] -->
<string name="tuner_time">Time</string>
@@ -2587,6 +2595,12 @@
<!-- Accessibility description of action to remove QS tile on click. It will read as "Double-tap to remove tile" in screen readers [CHAR LIMIT=NONE] -->
<string name="accessibility_qs_edit_remove_tile_action">remove tile</string>
+ <!-- Accessibility description of action to select the QS tile to place on click. It will read as "Double-tap to toggle placement mode" in screen readers [CHAR LIMIT=NONE] -->
+ <string name="accessibility_qs_edit_toggle_placement_mode">toggle placement mode</string>
+
+ <!-- Accessibility description of action to toggle the QS tile selection. It will read as "Double-tap to toggle selection" in screen readers [CHAR LIMIT=NONE] -->
+ <string name="accessibility_qs_edit_toggle_selection">toggle selection</string>
+
<!-- Accessibility action of action to add QS tile to end. It will read as "Double-tap to add tile to the last position" in screen readers [CHAR LIMIT=NONE] -->
<string name="accessibility_qs_edit_tile_add_action">add tile to the last position</string>
@@ -4195,6 +4209,12 @@
All Quick Settings tiles will reset to the device’s original settings
</string>
+
+ <!-- Content of the Reset Tiles dialog in QS Edit mode. [CHAR LIMIT=NONE] -->
+ <string name="demote_explain_text">
+ <xliff:g id="application" example= "Superfast Food Delivery">%1$s</xliff:g> will no longer show Live Updates here. You can change this any time in Settings.
+ </string>
+
<!-- Template that joins disabled message with the label for the voice over. [CHAR LIMIT=NONE] -->
<string name="volume_slider_disabled_message_template"><xliff:g example="Notification" id="stream_name">%1$s</xliff:g>, <xliff:g example="Disabled because ring is muted" id="disabled_message">%2$s</xliff:g></string>
</resources>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 7895ff7b90f6..a479f1841ca4 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -19,7 +19,8 @@
<style name="TextAppearance.StatusBar.Clock" parent="@*android:style/TextAppearance.StatusBar.Icon">
<item name="android:textSize">@dimen/status_bar_clock_size</item>
- <item name="android:fontFamily">@*android:string/config_headlineFontFamilyMedium</item>
+ <item name="android:fontFamily" android:featureFlag="!com.android.systemui.status_bar_font_updates">@*android:string/config_headlineFontFamilyMedium</item>
+ <item name="android:fontFamily" android:featureFlag="com.android.systemui.status_bar_font_updates">"variable-label-large-emphasized"</item>
<item name="android:textColor">@color/status_bar_clock_color</item>
<item name="android:fontFeatureSettings">tnum</item>
</style>
diff --git a/packages/SystemUI/shared/biometrics/Android.bp b/packages/SystemUI/shared/biometrics/Android.bp
index 63de81d4a680..208157c69adf 100644
--- a/packages/SystemUI/shared/biometrics/Android.bp
+++ b/packages/SystemUI/shared/biometrics/Android.bp
@@ -14,6 +14,9 @@ android_library {
"src/**/*.java",
"src/**/*.kt",
],
+ static_libs: [
+ "SystemUI-shared-utils",
+ ],
resource_dirs: [
"res",
],
diff --git a/packages/SystemUI/shared/biometrics/src/com/android/systemui/biometrics/Utils.kt b/packages/SystemUI/shared/biometrics/src/com/android/systemui/biometrics/Utils.kt
index 5b99a3f16fc2..7aa09cf64405 100644
--- a/packages/SystemUI/shared/biometrics/src/com/android/systemui/biometrics/Utils.kt
+++ b/packages/SystemUI/shared/biometrics/src/com/android/systemui/biometrics/Utils.kt
@@ -41,6 +41,7 @@ import com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PASSWORD
import com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PATTERN
import com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PIN
import com.android.systemui.biometrics.shared.model.PromptKind
+import com.android.systemui.utils.windowmanager.WindowManagerUtils
object Utils {
private const val TAG = "SysUIBiometricUtils"
@@ -117,10 +118,9 @@ object Utils {
@JvmStatic
fun getNavbarInsets(context: Context): Insets {
- val windowManager: WindowManager? = context.getSystemService(WindowManager::class.java)
- val windowMetrics: WindowMetrics? = windowManager?.maximumWindowMetrics
- return windowMetrics?.windowInsets?.getInsets(WindowInsets.Type.navigationBars())
- ?: Insets.NONE
+ val windowManager: WindowManager = WindowManagerUtils.getWindowManager(context)
+ val windowMetrics: WindowMetrics = windowManager.maximumWindowMetrics
+ return windowMetrics.windowInsets.getInsets(WindowInsets.Type.navigationBars())
}
/** Converts `drawable` to a [Bitmap]. */
diff --git a/packages/SystemUI/shared/res/values/bools.xml b/packages/SystemUI/shared/res/values/bools.xml
index 98e5cea0e78f..a7ad48d17abb 100644
--- a/packages/SystemUI/shared/res/values/bools.xml
+++ b/packages/SystemUI/shared/res/values/bools.xml
@@ -22,7 +22,4 @@
<resources>
<!-- Whether to add padding at the bottom of the complication clock -->
<bool name="dream_overlay_complication_clock_bottom_padding">false</bool>
-
- <!-- Whether to mark tasks that are present in the UI as perceptible tasks. -->
- <bool name="config_usePerceptibleTasks">false</bool>
</resources>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/PreviewPositionHelper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/PreviewPositionHelper.java
index ea7321627322..b8cd5bec2cbe 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/PreviewPositionHelper.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/PreviewPositionHelper.java
@@ -208,4 +208,19 @@ public class PreviewPositionHelper {
}
mMatrix.postTranslate(translateX, translateY);
}
+
+ /**
+ * A factory that returns a new instance of the {@link PreviewPositionHelper}.
+ * <p>{@link PreviewPositionHelper} is a stateful helper, and hence when using it in distinct
+ * scenarios, prefer fetching an object using this factory</p>
+ * <p>Additionally, helpful for injecting mocks in tests</p>
+ */
+ public static class PreviewPositionHelperFactory {
+ /**
+ * Returns a new {@link PreviewPositionHelper} for use in a distinct scenario.
+ */
+ public PreviewPositionHelper create() {
+ return new PreviewPositionHelper();
+ }
+ }
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java
index c82243934b8b..b1fc560f406b 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java
@@ -37,6 +37,7 @@ import android.view.Surface;
import android.view.WindowManager;
import com.android.systemui.shared.recents.model.Task;
+import com.android.systemui.utils.windowmanager.WindowManagerUtils;
/* Common code */
public class Utilities {
@@ -152,7 +153,7 @@ public class Utilities {
/** @return whether or not {@param context} represents that of a large screen device or not */
@TargetApi(Build.VERSION_CODES.R)
public static boolean isLargeScreen(Context context) {
- return isLargeScreen(context.getSystemService(WindowManager.class), context.getResources());
+ return isLargeScreen(WindowManagerUtils.getWindowManager(context), context.getResources());
}
/** @return whether or not {@param context} represents that of a large screen device or not */
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/FloatingRotationButton.java b/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/FloatingRotationButton.java
index 4db6ab6ea579..570d774b95e9 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/FloatingRotationButton.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/FloatingRotationButton.java
@@ -16,9 +16,6 @@
package com.android.systemui.shared.rotation;
-import static com.android.app.viewcapture.ViewCaptureFactory.getViewCaptureAwareWindowManagerInstance;
-import static com.android.systemui.Flags.enableViewCaptureTracing;
-
import android.annotation.DimenRes;
import android.annotation.IdRes;
import android.annotation.LayoutRes;
@@ -33,6 +30,7 @@ import android.graphics.drawable.Drawable;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
+import android.view.WindowManager;
import android.view.WindowManager.LayoutParams;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.widget.FrameLayout;
@@ -40,8 +38,8 @@ import android.widget.FrameLayout;
import androidx.annotation.BoolRes;
import androidx.core.view.OneShotPreDrawListener;
-import com.android.app.viewcapture.ViewCaptureAwareWindowManager;
import com.android.systemui.shared.rotation.FloatingRotationButtonPositionCalculator.Position;
+import com.android.systemui.utils.windowmanager.WindowManagerUtils;
/**
* Containing logic for the rotation button on the physical left bottom corner of the screen.
@@ -50,7 +48,7 @@ public class FloatingRotationButton implements RotationButton {
private static final int MARGIN_ANIMATION_DURATION_MILLIS = 300;
- private final ViewCaptureAwareWindowManager mWindowManager;
+ private final WindowManager mWindowManager;
private final ViewGroup mKeyButtonContainer;
private final FloatingRotationButtonView mKeyButtonView;
@@ -91,8 +89,7 @@ public class FloatingRotationButton implements RotationButton {
@DimenRes int taskbarBottomMargin, @DimenRes int buttonDiameter,
@DimenRes int rippleMaxWidth, @BoolRes int floatingRotationBtnPositionLeftResource) {
mContext = context;
- mWindowManager = getViewCaptureAwareWindowManagerInstance(mContext,
- enableViewCaptureTracing());
+ mWindowManager = WindowManagerUtils.getWindowManager(mContext);
mKeyButtonContainer = (ViewGroup) LayoutInflater.from(mContext).inflate(layout, null);
mKeyButtonView = mKeyButtonContainer.findViewById(keyButtonId);
mKeyButtonView.setVisibility(View.VISIBLE);
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
index 487d1ce2514e..b981f9837787 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
@@ -46,8 +46,6 @@ import android.view.Display;
import android.window.TaskSnapshot;
import com.android.internal.app.IVoiceInteractionManagerService;
-import com.android.server.am.Flags;
-import com.android.systemui.shared.R;
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.recents.model.ThumbnailData;
@@ -324,14 +322,6 @@ public class ActivityManagerWrapper {
}
/**
- * Returns true if tasks with a presence in the UI should be marked as perceptible tasks.
- */
- public static boolean usePerceptibleTasks(Context context) {
- return Flags.perceptibleTasks()
- && context.getResources().getBoolean(R.bool.config_usePerceptibleTasks);
- }
-
- /**
* Returns true if the running task represents the home task
*/
public static boolean isHomeTask(RunningTaskInfo info) {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardViewController.java
index 892851cd7056..8a307145023d 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardViewController.java
@@ -160,7 +160,7 @@ public interface KeyguardViewController {
/**
* Shows the primary bouncer.
*/
- void showPrimaryBouncer(boolean scrimmed);
+ void showPrimaryBouncer(boolean scrimmed, String reason);
/**
* When the primary bouncer is fully visible or is showing but animation didn't finish yet.
diff --git a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
index 19da5de6b531..59ec6923ce91 100644
--- a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
+++ b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
@@ -66,7 +66,6 @@ import android.widget.FrameLayout;
import androidx.annotation.VisibleForTesting;
-import com.android.app.viewcapture.ViewCaptureAwareWindowManager;
import com.android.internal.util.Preconditions;
import com.android.settingslib.Utils;
import com.android.systemui.biometrics.data.repository.FacePropertyRepository;
@@ -167,7 +166,7 @@ public class ScreenDecorations implements
ViewGroup mScreenDecorHwcWindow;
@VisibleForTesting
ScreenDecorHwcLayer mScreenDecorHwcLayer;
- private ViewCaptureAwareWindowManager mWindowManager;
+ private WindowManager mWindowManager;
private int mRotation;
private UserSettingObserver mColorInversionSetting;
private DelayableExecutor mExecutor;
@@ -337,7 +336,7 @@ public class ScreenDecorations implements
FacePropertyRepository facePropertyRepository,
JavaAdapter javaAdapter,
CameraProtectionLoader cameraProtectionLoader,
- ViewCaptureAwareWindowManager viewCaptureAwareWindowManager,
+ WindowManager windowManager,
@ScreenDecorationsThread Handler handler,
@ScreenDecorationsThread DelayableExecutor executor) {
mContext = context;
@@ -353,7 +352,7 @@ public class ScreenDecorations implements
mLogger = logger;
mFacePropertyRepository = facePropertyRepository;
mJavaAdapter = javaAdapter;
- mWindowManager = viewCaptureAwareWindowManager;
+ mWindowManager = windowManager;
mHandler = handler;
mExecutor = executor;
}
diff --git a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
index 089466707298..d017754ae653 100644
--- a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
@@ -778,18 +778,26 @@ public class SwipeHelper implements Gefingerpoken, Dumpable {
protected boolean swipedFarEnough() {
float translation = getTranslation(mTouchedView);
- return Math.abs(translation) > SWIPED_FAR_ENOUGH_SIZE_FRACTION * getSize(
- mTouchedView);
+ return Math.abs(translation) > SWIPED_FAR_ENOUGH_SIZE_FRACTION * getSize(mTouchedView);
}
public boolean isDismissGesture(MotionEvent ev) {
float translation = getTranslation(mTouchedView);
return ev.getActionMasked() == MotionEvent.ACTION_UP
&& !mFalsingManager.isUnlockingDisabled()
- && !isFalseGesture() && (swipedFastEnough() || swipedFarEnough())
+ && !isFalseGesture() && isSwipeDismissible()
&& mCallback.canChildBeDismissedInDirection(mTouchedView, translation > 0);
}
+ /** Can the swipe gesture on the touched view be considered as a dismiss intention */
+ public boolean isSwipeDismissible() {
+ if (magneticNotificationSwipes()) {
+ return mCallback.isMagneticViewDetached(mTouchedView) || swipedFastEnough();
+ } else {
+ return swipedFastEnough() || swipedFarEnough();
+ }
+ }
+
/** Returns true if the gesture should be rejected. */
public boolean isFalseGesture() {
boolean falsingDetected = mCallback.isAntiFalsingNeeded();
@@ -970,6 +978,13 @@ public class SwipeHelper implements Gefingerpoken, Dumpable {
void onMagneticInteractionEnd(View view, float velocity);
/**
+ * Determine if a view managed by magnetic interactions is magnetically detached
+ * @param view The magnetic view
+ * @return if the view is detached according to its magnetic state.
+ */
+ boolean isMagneticViewDetached(View view);
+
+ /**
* Called when the child is long pressed and available to start drag and drop.
*
* @param v the view that was long pressed.
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationImpl.java b/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationImpl.java
index 115242eb13aa..375137c67f7c 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationImpl.java
@@ -44,7 +44,6 @@ import android.window.InputTransferToken;
import androidx.annotation.NonNull;
-import com.android.app.viewcapture.ViewCaptureAwareWindowManager;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
import com.android.systemui.dagger.SysUISingleton;
@@ -54,6 +53,7 @@ import com.android.systemui.recents.LauncherProxyService;
import com.android.systemui.settings.DisplayTracker;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.util.settings.SecureSettings;
+import com.android.systemui.utils.windowmanager.WindowManagerProvider;
import java.io.PrintWriter;
import java.util.concurrent.Executor;
@@ -97,20 +97,19 @@ public class MagnificationImpl implements Magnification, CommandQueue.Callbacks
private final WindowMagnifierCallback mWindowMagnifierCallback;
private final SysUiState mSysUiState;
private final SecureSettings mSecureSettings;
- private final ViewCaptureAwareWindowManager mViewCaptureAwareWindowManager;
+ private final WindowManagerProvider mWindowManagerProvider;
WindowMagnificationControllerSupplier(Context context, Handler handler,
WindowMagnifierCallback windowMagnifierCallback,
DisplayManager displayManager, SysUiState sysUiState,
- SecureSettings secureSettings,
- ViewCaptureAwareWindowManager viewCaptureAwareWindowManager) {
+ SecureSettings secureSettings, WindowManagerProvider windowManagerProvider) {
super(displayManager);
mContext = context;
mHandler = handler;
mWindowMagnifierCallback = windowMagnifierCallback;
mSysUiState = sysUiState;
mSecureSettings = secureSettings;
- mViewCaptureAwareWindowManager = viewCaptureAwareWindowManager;
+ mWindowManagerProvider = windowManagerProvider;
}
@Override
@@ -118,6 +117,8 @@ public class MagnificationImpl implements Magnification, CommandQueue.Callbacks
final Context windowContext = mContext.createWindowContext(display,
TYPE_ACCESSIBILITY_OVERLAY,
/* options */ null);
+ final WindowManager windowManager = mWindowManagerProvider
+ .getWindowManager(windowContext);
windowContext.setTheme(com.android.systemui.res.R.style.Theme_SystemUI);
Supplier<SurfaceControlViewHost> scvhSupplier = () ->
@@ -133,7 +134,8 @@ public class MagnificationImpl implements Magnification, CommandQueue.Callbacks
mWindowMagnifierCallback,
mSysUiState,
mSecureSettings,
- scvhSupplier);
+ scvhSupplier,
+ windowManager);
}
}
@@ -148,17 +150,20 @@ public class MagnificationImpl implements Magnification, CommandQueue.Callbacks
private final Executor mExecutor;
private final DisplayManager mDisplayManager;
private final IWindowManager mIWindowManager;
+ private final WindowManagerProvider mWindowManagerProvider;
FullscreenMagnificationControllerSupplier(Context context,
DisplayManager displayManager,
Handler handler,
- Executor executor, IWindowManager iWindowManager) {
+ Executor executor, IWindowManager iWindowManager,
+ WindowManagerProvider windowManagerProvider) {
super(displayManager);
mContext = context;
mHandler = handler;
mExecutor = executor;
mDisplayManager = displayManager;
mIWindowManager = iWindowManager;
+ mWindowManagerProvider = windowManagerProvider;
}
@Override
@@ -174,7 +179,7 @@ public class MagnificationImpl implements Magnification, CommandQueue.Callbacks
mExecutor,
mDisplayManager,
windowContext.getSystemService(AccessibilityManager.class),
- windowContext.getSystemService(WindowManager.class),
+ mWindowManagerProvider.getWindowManager(windowContext),
mIWindowManager,
scvhSupplier);
}
@@ -190,31 +195,32 @@ public class MagnificationImpl implements Magnification, CommandQueue.Callbacks
private final Context mContext;
private final MagnificationSettingsController.Callback mSettingsControllerCallback;
private final SecureSettings mSecureSettings;
- private final ViewCaptureAwareWindowManager mViewCaptureAwareWindowManager;
+ private final WindowManagerProvider mWindowManagerProvider;
SettingsSupplier(Context context,
MagnificationSettingsController.Callback settingsControllerCallback,
DisplayManager displayManager,
- SecureSettings secureSettings,
- ViewCaptureAwareWindowManager viewCaptureAwareWindowManager) {
+ SecureSettings secureSettings, WindowManagerProvider windowManagerProvider) {
super(displayManager);
mContext = context;
mSettingsControllerCallback = settingsControllerCallback;
mSecureSettings = secureSettings;
- mViewCaptureAwareWindowManager = viewCaptureAwareWindowManager;
+ mWindowManagerProvider = windowManagerProvider;
}
@Override
protected MagnificationSettingsController createInstance(Display display) {
final Context windowContext = mContext.createWindowContext(display,
TYPE_ACCESSIBILITY_OVERLAY, /* options */ null);
+ final WindowManager windowManager = mWindowManagerProvider
+ .getWindowManager(windowContext);
windowContext.setTheme(com.android.systemui.res.R.style.Theme_SystemUI);
return new MagnificationSettingsController(
windowContext,
new SfVsyncFrameCallbackProvider(),
mSettingsControllerCallback,
mSecureSettings,
- mViewCaptureAwareWindowManager);
+ windowManager);
}
}
@@ -229,11 +235,11 @@ public class MagnificationImpl implements Magnification, CommandQueue.Callbacks
SecureSettings secureSettings, DisplayTracker displayTracker,
DisplayManager displayManager, AccessibilityLogger a11yLogger,
IWindowManager iWindowManager, AccessibilityManager accessibilityManager,
- ViewCaptureAwareWindowManager viewCaptureAwareWindowManager) {
+ WindowManagerProvider windowManagerProvider) {
this(context, mainHandler.getLooper(), executor, commandQueue,
modeSwitchesController, sysUiState, launcherProxyService, secureSettings,
displayTracker, displayManager, a11yLogger, iWindowManager, accessibilityManager,
- viewCaptureAwareWindowManager);
+ windowManagerProvider);
}
@VisibleForTesting
@@ -244,7 +250,7 @@ public class MagnificationImpl implements Magnification, CommandQueue.Callbacks
DisplayManager displayManager, AccessibilityLogger a11yLogger,
IWindowManager iWindowManager,
AccessibilityManager accessibilityManager,
- ViewCaptureAwareWindowManager viewCaptureAwareWindowManager) {
+ WindowManagerProvider windowManagerProvider) {
mHandler = new Handler(looper) {
@Override
public void handleMessage(@NonNull Message msg) {
@@ -263,12 +269,13 @@ public class MagnificationImpl implements Magnification, CommandQueue.Callbacks
mA11yLogger = a11yLogger;
mWindowMagnificationControllerSupplier = new WindowMagnificationControllerSupplier(context,
mHandler, mWindowMagnifierCallback,
- displayManager, sysUiState, secureSettings, viewCaptureAwareWindowManager);
+ displayManager, sysUiState, secureSettings, windowManagerProvider);
mFullscreenMagnificationControllerSupplier = new FullscreenMagnificationControllerSupplier(
- context, displayManager, mHandler, mExecutor, iWindowManager);
+ context, displayManager, mHandler, mExecutor, iWindowManager,
+ windowManagerProvider);
mMagnificationSettingsSupplier = new SettingsSupplier(context,
mMagnificationSettingsControllerCallback, displayManager, secureSettings,
- viewCaptureAwareWindowManager);
+ windowManagerProvider);
mModeSwitchesController.setClickListenerDelegate(
displayId -> mHandler.post(() -> {
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationModeSwitch.java b/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationModeSwitch.java
index 4723ab958f86..9eb01de239bc 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationModeSwitch.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationModeSwitch.java
@@ -46,7 +46,6 @@ import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
import android.widget.ImageView;
-import com.android.app.viewcapture.ViewCaptureAwareWindowManager;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
import com.android.systemui.res.R;
@@ -77,7 +76,6 @@ class MagnificationModeSwitch implements MagnificationGestureDetector.OnGestureL
private final Context mContext;
private final AccessibilityManager mAccessibilityManager;
private final WindowManager mWindowManager;
- private final ViewCaptureAwareWindowManager mViewCaptureAwareWindowManager;
private final ImageView mImageView;
private final Runnable mWindowInsetChangeRunnable;
private final SfVsyncFrameCallbackProvider mSfVsyncFrameProvider;
@@ -101,21 +99,20 @@ class MagnificationModeSwitch implements MagnificationGestureDetector.OnGestureL
void onClick(int displayId);
}
- MagnificationModeSwitch(@UiContext Context context, ClickListener clickListener,
- ViewCaptureAwareWindowManager viewCaptureAwareWindowManager) {
- this(context, createView(context), new SfVsyncFrameCallbackProvider(), clickListener,
- viewCaptureAwareWindowManager);
+ MagnificationModeSwitch(@UiContext Context context, WindowManager windowManager,
+ ClickListener clickListener) {
+ this(context, windowManager, createView(context), new SfVsyncFrameCallbackProvider(),
+ clickListener);
}
@VisibleForTesting
- MagnificationModeSwitch(Context context, @NonNull ImageView imageView,
- SfVsyncFrameCallbackProvider sfVsyncFrameProvider, ClickListener clickListener,
- ViewCaptureAwareWindowManager viewCaptureAwareWindowManager) {
+ MagnificationModeSwitch(Context context, WindowManager windowManager,
+ @NonNull ImageView imageView, SfVsyncFrameCallbackProvider sfVsyncFrameProvider,
+ ClickListener clickListener) {
mContext = context;
mConfiguration = new Configuration(context.getResources().getConfiguration());
mAccessibilityManager = mContext.getSystemService(AccessibilityManager.class);
- mWindowManager = mContext.getSystemService(WindowManager.class);
- mViewCaptureAwareWindowManager = viewCaptureAwareWindowManager;
+ mWindowManager = windowManager;
mSfVsyncFrameProvider = sfVsyncFrameProvider;
mClickListener = clickListener;
mParams = createLayoutParams(context);
@@ -282,7 +279,7 @@ class MagnificationModeSwitch implements MagnificationGestureDetector.OnGestureL
mImageView.animate().cancel();
mIsFadeOutAnimating = false;
mImageView.setAlpha(0f);
- mViewCaptureAwareWindowManager.removeView(mImageView);
+ mWindowManager.removeView(mImageView);
mContext.unregisterComponentCallbacks(this);
mIsVisible = false;
}
@@ -316,7 +313,7 @@ class MagnificationModeSwitch implements MagnificationGestureDetector.OnGestureL
mParams.y = mDraggableWindowBounds.bottom;
mToLeftScreenEdge = false;
}
- mViewCaptureAwareWindowManager.addView(mImageView, mParams);
+ mWindowManager.addView(mImageView, mParams);
// Exclude magnification switch button from system gesture area.
setSystemGestureExclusion();
mIsVisible = true;
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationSettingsController.java b/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationSettingsController.java
index fc7535a712e3..2d5dc8d23383 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationSettingsController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationSettingsController.java
@@ -26,7 +26,6 @@ import android.content.res.Configuration;
import android.util.Range;
import android.view.WindowManager;
-import com.android.app.viewcapture.ViewCaptureAwareWindowManager;
import com.android.internal.accessibility.common.MagnificationConstants;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
@@ -62,9 +61,9 @@ public class MagnificationSettingsController implements ComponentCallbacks {
SfVsyncFrameCallbackProvider sfVsyncFrameProvider,
@NonNull Callback settingsControllerCallback,
SecureSettings secureSettings,
- ViewCaptureAwareWindowManager viewCaptureAwareWindowManager) {
- this(context, sfVsyncFrameProvider, settingsControllerCallback, secureSettings, null,
- viewCaptureAwareWindowManager);
+ WindowManager windowManager) {
+ this(context, sfVsyncFrameProvider, settingsControllerCallback, secureSettings,
+ windowManager, null);
}
@VisibleForTesting
@@ -73,8 +72,8 @@ public class MagnificationSettingsController implements ComponentCallbacks {
SfVsyncFrameCallbackProvider sfVsyncFrameProvider,
@NonNull Callback settingsControllerCallback,
SecureSettings secureSettings,
- WindowMagnificationSettings windowMagnificationSettings,
- ViewCaptureAwareWindowManager viewCaptureAwareWindowManager) {
+ WindowManager windowManager,
+ WindowMagnificationSettings windowMagnificationSettings) {
mContext = context.createWindowContext(
context.getDisplay(),
WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL,
@@ -88,7 +87,7 @@ public class MagnificationSettingsController implements ComponentCallbacks {
} else {
mWindowMagnificationSettings = new WindowMagnificationSettings(mContext,
mWindowMagnificationSettingsCallback,
- sfVsyncFrameProvider, secureSettings, viewCaptureAwareWindowManager);
+ sfVsyncFrameProvider, secureSettings, windowManager);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/MirrorWindowControl.java b/packages/SystemUI/src/com/android/systemui/accessibility/MirrorWindowControl.java
index eb4de6837d41..7f3a869d8222 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/MirrorWindowControl.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/MirrorWindowControl.java
@@ -18,9 +18,6 @@ package com.android.systemui.accessibility;
import static android.view.WindowManager.LayoutParams;
-import static com.android.app.viewcapture.ViewCaptureFactory.getViewCaptureAwareWindowManagerInstance;
-import static com.android.systemui.Flags.enableViewCaptureTracing;
-
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
@@ -32,8 +29,8 @@ import android.util.MathUtils;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
+import android.view.WindowManager;
-import com.android.app.viewcapture.ViewCaptureAwareWindowManager;
import com.android.systemui.res.R;
/**
@@ -73,12 +70,11 @@ public abstract class MirrorWindowControl {
* @see #setDefaultPosition(LayoutParams)
*/
private final Point mControlPosition = new Point();
- private final ViewCaptureAwareWindowManager mWindowManager;
+ private final WindowManager mWindowManager;
- MirrorWindowControl(Context context) {
+ MirrorWindowControl(Context context, WindowManager windowManager) {
mContext = context;
- mWindowManager = getViewCaptureAwareWindowManagerInstance(mContext,
- enableViewCaptureTracing());
+ mWindowManager = windowManager;
}
public void setWindowDelegate(@Nullable MirrorWindowDelegate windowDelegate) {
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/ModeSwitchesController.java b/packages/SystemUI/src/com/android/systemui/accessibility/ModeSwitchesController.java
index 53827e65344a..7d9f8674457c 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/ModeSwitchesController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/ModeSwitchesController.java
@@ -24,10 +24,11 @@ import android.annotation.MainThread;
import android.content.Context;
import android.hardware.display.DisplayManager;
import android.view.Display;
+import android.view.WindowManager;
-import com.android.app.viewcapture.ViewCaptureAwareWindowManager;
import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.utils.windowmanager.WindowManagerProvider;
import javax.inject.Inject;
@@ -49,9 +50,9 @@ public class ModeSwitchesController implements ClickListener {
@Inject
public ModeSwitchesController(Context context, DisplayManager displayManager,
- ViewCaptureAwareWindowManager viewCaptureAwareWindowManager) {
+ WindowManagerProvider windowManagerProvider) {
mSwitchSupplier = new SwitchSupplier(context, displayManager, this::onClick,
- viewCaptureAwareWindowManager);
+ windowManagerProvider);
}
@VisibleForTesting
@@ -118,7 +119,7 @@ public class ModeSwitchesController implements ClickListener {
private final Context mContext;
private final ClickListener mClickListener;
- private final ViewCaptureAwareWindowManager mViewCaptureAwareWindowManager;
+ private final WindowManagerProvider mWindowManagerProvider;
/**
* Supplies the switch for the given display.
@@ -128,20 +129,20 @@ public class ModeSwitchesController implements ClickListener {
* @param clickListener The callback that will run when the switch is clicked
*/
SwitchSupplier(Context context, DisplayManager displayManager,
- ClickListener clickListener,
- ViewCaptureAwareWindowManager viewCaptureAwareWindowManager) {
+ ClickListener clickListener, WindowManagerProvider windowManagerProvider) {
super(displayManager);
mContext = context;
mClickListener = clickListener;
- mViewCaptureAwareWindowManager = viewCaptureAwareWindowManager;
+ mWindowManagerProvider = windowManagerProvider;
}
@Override
protected MagnificationModeSwitch createInstance(Display display) {
final Context uiContext = mContext.createWindowContext(display,
TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY, /* options */ null);
- return new MagnificationModeSwitch(uiContext, mClickListener,
- mViewCaptureAwareWindowManager);
+ final WindowManager uiWindowManager = mWindowManagerProvider
+ .getWindowManager(uiContext);
+ return new MagnificationModeSwitch(uiContext, uiWindowManager, mClickListener);
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/SimpleMirrorWindowControl.java b/packages/SystemUI/src/com/android/systemui/accessibility/SimpleMirrorWindowControl.java
index bc469eed7359..3cde033bf56a 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/SimpleMirrorWindowControl.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/SimpleMirrorWindowControl.java
@@ -27,6 +27,7 @@ import android.util.Log;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
+import android.view.WindowManager;
import com.android.systemui.res.R;
@@ -48,8 +49,8 @@ class SimpleMirrorWindowControl extends MirrorWindowControl implements View.OnCl
private final PointF mLastDrag = new PointF();
private final Handler mHandler;
- SimpleMirrorWindowControl(Context context, Handler handler) {
- super(context);
+ SimpleMirrorWindowControl(Context context, Handler handler, WindowManager windowManager) {
+ super(context, windowManager);
mHandler = handler;
final Resources resource = context.getResources();
mMoveFrameAmountShort = resource.getDimensionPixelSize(
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
index 8734d05bc894..9cd77e790e8c 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
@@ -249,7 +249,8 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold
@NonNull WindowMagnifierCallback callback,
SysUiState sysUiState,
SecureSettings secureSettings,
- Supplier<SurfaceControlViewHost> scvhSupplier) {
+ Supplier<SurfaceControlViewHost> scvhSupplier,
+ WindowManager windowManager) {
mContext = context;
mHandler = handler;
mAnimationController = animationController;
@@ -265,7 +266,7 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold
mDisplayId = mContext.getDisplayId();
mRotation = display.getRotation();
- mWm = context.getSystemService(WindowManager.class);
+ mWm = windowManager;
mWindowBounds = new Rect(mWm.getCurrentWindowMetrics().getBounds());
mResources = mContext.getResources();
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationSettings.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationSettings.java
index 3b6f8f87a1a8..bd4b6420bf37 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationSettings.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationSettings.java
@@ -56,7 +56,6 @@ import android.widget.LinearLayout;
import android.widget.SeekBar;
import android.widget.Switch;
-import com.android.app.viewcapture.ViewCaptureAwareWindowManager;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
import com.android.systemui.common.ui.view.SeekBarWithIconButtonsView;
@@ -75,7 +74,6 @@ class WindowMagnificationSettings implements MagnificationGestureDetector.OnGest
private final Context mContext;
private final AccessibilityManager mAccessibilityManager;
private final WindowManager mWindowManager;
- private final ViewCaptureAwareWindowManager mViewCaptureAwareWindowManager;
private final SecureSettings mSecureSettings;
private final Runnable mWindowInsetChangeRunnable;
@@ -137,11 +135,10 @@ class WindowMagnificationSettings implements MagnificationGestureDetector.OnGest
@VisibleForTesting
WindowMagnificationSettings(Context context, WindowMagnificationSettingsCallback callback,
SfVsyncFrameCallbackProvider sfVsyncFrameProvider, SecureSettings secureSettings,
- ViewCaptureAwareWindowManager viewCaptureAwareWindowManager) {
+ WindowManager windowManager) {
mContext = context;
mAccessibilityManager = mContext.getSystemService(AccessibilityManager.class);
- mWindowManager = mContext.getSystemService(WindowManager.class);
- mViewCaptureAwareWindowManager = viewCaptureAwareWindowManager;
+ mWindowManager = windowManager;
mSfVsyncFrameProvider = sfVsyncFrameProvider;
mCallback = callback;
mSecureSettings = secureSettings;
@@ -324,7 +321,7 @@ class WindowMagnificationSettings implements MagnificationGestureDetector.OnGest
// Unregister observer before removing view
mSecureSettings.unregisterContentObserverSync(mMagnificationCapabilityObserver);
- mViewCaptureAwareWindowManager.removeView(mSettingView);
+ mWindowManager.removeView(mSettingView);
mIsVisible = false;
if (resetPosition) {
mParams.x = 0;
@@ -382,7 +379,7 @@ class WindowMagnificationSettings implements MagnificationGestureDetector.OnGest
mParams.y = mDraggableWindowBounds.bottom;
}
- mViewCaptureAwareWindowManager.addView(mSettingView, mParams);
+ mWindowManager.addView(mSettingView, mParams);
mSecureSettings.registerContentObserverForUserSync(
Settings.Secure.ACCESSIBILITY_MAGNIFICATION_CAPABILITY,
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuController.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuController.java
index f8e4bda15d01..ef42837ba776 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuController.java
@@ -31,7 +31,6 @@ import android.view.accessibility.IUserInitializationCompleteCallback;
import androidx.annotation.MainThread;
-import com.android.app.viewcapture.ViewCaptureAwareWindowManager;
import com.android.internal.annotations.VisibleForTesting;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.KeyguardUpdateMonitorCallback;
@@ -60,7 +59,6 @@ public class AccessibilityFloatingMenuController implements
private final Context mContext;
private final WindowManager mWindowManager;
- private final ViewCaptureAwareWindowManager mViewCaptureAwareWindowManager;
private final DisplayManager mDisplayManager;
private final AccessibilityManager mAccessibilityManager;
private final HearingAidDeviceManager mHearingAidDeviceManager;
@@ -105,7 +103,6 @@ public class AccessibilityFloatingMenuController implements
@Inject
public AccessibilityFloatingMenuController(Context context,
WindowManager windowManager,
- ViewCaptureAwareWindowManager viewCaptureAwareWindowManager,
DisplayManager displayManager,
AccessibilityManager accessibilityManager,
AccessibilityButtonTargetsObserver accessibilityButtonTargetsObserver,
@@ -118,7 +115,6 @@ public class AccessibilityFloatingMenuController implements
@Main Handler handler) {
mContext = context;
mWindowManager = windowManager;
- mViewCaptureAwareWindowManager = viewCaptureAwareWindowManager;
mDisplayManager = displayManager;
mAccessibilityManager = accessibilityManager;
mAccessibilityButtonTargetsObserver = accessibilityButtonTargetsObserver;
@@ -205,8 +201,8 @@ public class AccessibilityFloatingMenuController implements
final Context windowContext = mContext.createWindowContext(defaultDisplay,
TYPE_NAVIGATION_BAR_PANEL, /* options= */ null);
mFloatingMenu = new MenuViewLayerController(windowContext, mWindowManager,
- mViewCaptureAwareWindowManager, mAccessibilityManager, mSecureSettings,
- mNavigationModeController, mHearingAidDeviceManager);
+ mAccessibilityManager, mSecureSettings, mNavigationModeController,
+ mHearingAidDeviceManager);
}
mFloatingMenu.show();
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/DragToInteractView.kt b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/DragToInteractView.kt
index 13c1a450832f..52e69efdbc19 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/DragToInteractView.kt
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/DragToInteractView.kt
@@ -48,7 +48,7 @@ import com.android.wm.shell.shared.bubbles.DismissView
*
* @note [setup] method should be called after initialisation
*/
-class DragToInteractView(context: Context) : FrameLayout(context) {
+class DragToInteractView(context: Context, windowManager: WindowManager) : FrameLayout(context) {
/**
* The configuration is used to provide module specific resource ids
*
@@ -86,8 +86,7 @@ class DragToInteractView(context: Context) : FrameLayout(context) {
private val spring = PhysicsAnimator.SpringConfig(STIFFNESS_LOW, DAMPING_RATIO_LOW_BOUNCY)
private val INTERACT_SCRIM_FADE_MS = 200L
- private var wm: WindowManager =
- context.getSystemService(Context.WINDOW_SERVICE) as WindowManager
+ private var wm: WindowManager = windowManager
private var gradientDrawable: GradientDrawable? = null
private val GRADIENT_ALPHA: IntProperty<GradientDrawable> =
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewAppearance.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewAppearance.java
index bd3dfe049587..8025d4b0c2ea 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewAppearance.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewAppearance.java
@@ -317,14 +317,14 @@ class MenuViewAppearance {
public static float avoidVerticalDisplayCutout(
float y, float menuHeight, Rect bounds, Rect cutout) {
if (cutout.top > y + menuHeight || cutout.bottom < y) {
- return y;
+ return clampVerticalPosition(y, menuHeight, bounds.top, bounds.bottom);
}
boolean topAvailable = cutout.top - bounds.top >= menuHeight;
boolean bottomAvailable = bounds.bottom - cutout.bottom >= menuHeight;
boolean topOrBottom;
if (!topAvailable && !bottomAvailable) {
- return y;
+ return clampVerticalPosition(y, menuHeight, bounds.top, bounds.bottom);
} else if (topAvailable && !bottomAvailable) {
topOrBottom = true;
} else if (!topAvailable && bottomAvailable) {
@@ -332,7 +332,16 @@ class MenuViewAppearance {
} else {
topOrBottom = y + menuHeight * 0.5f < cutout.centerY();
}
- return (topOrBottom) ? cutout.top - menuHeight : cutout.bottom;
+
+ float finalPosition = (topOrBottom) ? cutout.top - menuHeight : cutout.bottom;
+ return clampVerticalPosition(finalPosition, menuHeight, bounds.top, bounds.bottom);
+ }
+
+ private static float clampVerticalPosition(
+ float position, float height, float min, float max) {
+ position = Float.max(min + height / 2, position);
+ position = Float.min(max - height / 2, position);
+ return position;
}
boolean isMenuOnLeftSide() {
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayer.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayer.java
index 81095220b4a6..8fb260c2df36 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayer.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayer.java
@@ -212,7 +212,7 @@ class MenuViewLayer extends FrameLayout implements
mMenuAnimationController = mMenuView.getMenuAnimationController();
mMenuAnimationController.setSpringAnimationsEndAction(this::onSpringAnimationsEndAction);
mDismissView = new DismissView(context);
- mDragToInteractView = new DragToInteractView(context);
+ mDragToInteractView = new DragToInteractView(context, windowManager);
DismissViewUtils.setup(mDismissView);
mDismissView.getCircle().setId(R.id.action_remove_menu);
mNotificationFactory = new MenuNotificationFactory(context);
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerController.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerController.java
index 102efcf7badd..7bf7e23b5df5 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerController.java
@@ -25,7 +25,6 @@ import android.graphics.PixelFormat;
import android.view.WindowManager;
import android.view.accessibility.AccessibilityManager;
-import com.android.app.viewcapture.ViewCaptureAwareWindowManager;
import com.android.settingslib.bluetooth.HearingAidDeviceManager;
import com.android.systemui.navigationbar.NavigationModeController;
import com.android.systemui.util.settings.SecureSettings;
@@ -35,16 +34,15 @@ import com.android.systemui.util.settings.SecureSettings;
* of {@link IAccessibilityFloatingMenu}.
*/
class MenuViewLayerController implements IAccessibilityFloatingMenu {
- private final ViewCaptureAwareWindowManager mWindowManager;
+ private final WindowManager mWindowManager;
private final MenuViewLayer mMenuViewLayer;
private boolean mIsShowing;
MenuViewLayerController(Context context, WindowManager windowManager,
- ViewCaptureAwareWindowManager viewCaptureAwareWindowManager,
AccessibilityManager accessibilityManager, SecureSettings secureSettings,
NavigationModeController navigationModeController,
HearingAidDeviceManager hearingAidDeviceManager) {
- mWindowManager = viewCaptureAwareWindowManager;
+ mWindowManager = windowManager;
MenuViewModel menuViewModel = new MenuViewModel(
context, accessibilityManager, secureSettings, hearingAidDeviceManager);
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegate.java b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegate.java
index 14b13d105482..7f46613936ac 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegate.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegate.java
@@ -286,7 +286,9 @@ public class HearingDevicesDialogDelegate implements SystemUIDialog.Delegate,
mLaunchSourceId);
final Intent intent = new Intent(Settings.ACTION_ACCESSIBILITY_DETAILS_SETTINGS)
.putExtra(Intent.EXTRA_COMPONENT_NAME,
- ACCESSIBILITY_HEARING_AIDS_COMPONENT_NAME.flattenToString());
+ ACCESSIBILITY_HEARING_AIDS_COMPONENT_NAME.flattenToString())
+ .setPackage(mQSSettingsPackageRepository.getSettingsPackageName());
+
mActivityStarter.postStartActivityDismissingKeyguard(intent, /* delay= */ 0,
mDialogTransitionAnimator.createActivityTransitionController(
dialog));
@@ -325,9 +327,7 @@ public class HearingDevicesDialogDelegate implements SystemUIDialog.Delegate,
setupDeviceListView(dialog, hearingDeviceItemList);
setupPairNewDeviceButton(dialog);
setupPresetSpinner(dialog, activeHearingDevice);
- if (com.android.settingslib.flags.Flags.hearingDevicesInputRoutingControl()
- && com.android.systemui.Flags
- .hearingDevicesInputRoutingUiImprovement()) {
+ if (com.android.settingslib.flags.Flags.hearingDevicesInputRoutingControl()) {
setupInputRoutingSpinner(dialog, activeHearingDevice);
}
if (com.android.settingslib.flags.Flags.hearingDevicesAmbientVolumeControl()) {
@@ -588,9 +588,7 @@ public class HearingDevicesDialogDelegate implements SystemUIDialog.Delegate,
com.android.internal.R.color.materialColorOnPrimaryContainer));
}
text.setText(item.getToolName());
- Intent intent = item.getToolIntent()
- .setPackage(mQSSettingsPackageRepository.getSettingsPackageName());
-
+ Intent intent = item.getToolIntent();
view.setOnClickListener(v -> {
final String name = intent.getComponent() != null
? intent.getComponent().flattenToString()
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/qs/QSAccessibilityModule.kt b/packages/SystemUI/src/com/android/systemui/accessibility/qs/QSAccessibilityModule.kt
index fb47d429e271..e25d54bd38d3 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/qs/QSAccessibilityModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/qs/QSAccessibilityModule.kt
@@ -29,40 +29,40 @@ import com.android.systemui.qs.tiles.HearingDevicesTile
import com.android.systemui.qs.tiles.NightDisplayTile
import com.android.systemui.qs.tiles.OneHandedModeTile
import com.android.systemui.qs.tiles.ReduceBrightColorsTile
-import com.android.systemui.qs.tiles.base.interactor.QSTileAvailabilityInteractor
-import com.android.systemui.qs.tiles.base.viewmodel.QSTileViewModelFactory
-import com.android.systemui.qs.tiles.impl.colorcorrection.domain.ColorCorrectionTileMapper
+import com.android.systemui.qs.tiles.base.domain.interactor.QSTileAvailabilityInteractor
+import com.android.systemui.qs.tiles.base.shared.model.QSTileConfig
+import com.android.systemui.qs.tiles.base.shared.model.QSTileUIConfig
+import com.android.systemui.qs.tiles.base.ui.viewmodel.QSTileViewModel
+import com.android.systemui.qs.tiles.base.ui.viewmodel.QSTileViewModelFactory
+import com.android.systemui.qs.tiles.base.ui.viewmodel.StubQSTileViewModel
import com.android.systemui.qs.tiles.impl.colorcorrection.domain.interactor.ColorCorrectionTileDataInteractor
import com.android.systemui.qs.tiles.impl.colorcorrection.domain.interactor.ColorCorrectionUserActionInteractor
import com.android.systemui.qs.tiles.impl.colorcorrection.domain.model.ColorCorrectionTileModel
-import com.android.systemui.qs.tiles.impl.fontscaling.domain.FontScalingTileMapper
+import com.android.systemui.qs.tiles.impl.colorcorrection.ui.mapper.ColorCorrectionTileMapper
import com.android.systemui.qs.tiles.impl.fontscaling.domain.interactor.FontScalingTileDataInteractor
import com.android.systemui.qs.tiles.impl.fontscaling.domain.interactor.FontScalingTileUserActionInteractor
import com.android.systemui.qs.tiles.impl.fontscaling.domain.model.FontScalingTileModel
-import com.android.systemui.qs.tiles.impl.hearingdevices.domain.HearingDevicesTileMapper
+import com.android.systemui.qs.tiles.impl.fontscaling.ui.mapper.FontScalingTileMapper
import com.android.systemui.qs.tiles.impl.hearingdevices.domain.interactor.HearingDevicesTileDataInteractor
import com.android.systemui.qs.tiles.impl.hearingdevices.domain.interactor.HearingDevicesTileUserActionInteractor
import com.android.systemui.qs.tiles.impl.hearingdevices.domain.model.HearingDevicesTileModel
-import com.android.systemui.qs.tiles.impl.inversion.domain.ColorInversionTileMapper
+import com.android.systemui.qs.tiles.impl.hearingdevices.ui.mapper.HearingDevicesTileMapper
import com.android.systemui.qs.tiles.impl.inversion.domain.interactor.ColorInversionTileDataInteractor
import com.android.systemui.qs.tiles.impl.inversion.domain.interactor.ColorInversionUserActionInteractor
import com.android.systemui.qs.tiles.impl.inversion.domain.model.ColorInversionTileModel
+import com.android.systemui.qs.tiles.impl.inversion.ui.mapper.ColorInversionTileMapper
import com.android.systemui.qs.tiles.impl.night.domain.interactor.NightDisplayTileDataInteractor
import com.android.systemui.qs.tiles.impl.night.domain.interactor.NightDisplayTileUserActionInteractor
import com.android.systemui.qs.tiles.impl.night.domain.model.NightDisplayTileModel
-import com.android.systemui.qs.tiles.impl.night.ui.NightDisplayTileMapper
+import com.android.systemui.qs.tiles.impl.night.ui.mapper.NightDisplayTileMapper
import com.android.systemui.qs.tiles.impl.onehanded.domain.OneHandedModeTileDataInteractor
import com.android.systemui.qs.tiles.impl.onehanded.domain.OneHandedModeTileUserActionInteractor
import com.android.systemui.qs.tiles.impl.onehanded.domain.model.OneHandedModeTileModel
-import com.android.systemui.qs.tiles.impl.onehanded.ui.OneHandedModeTileMapper
+import com.android.systemui.qs.tiles.impl.onehanded.ui.mapper.OneHandedModeTileMapper
import com.android.systemui.qs.tiles.impl.reducebrightness.domain.interactor.ReduceBrightColorsTileDataInteractor
import com.android.systemui.qs.tiles.impl.reducebrightness.domain.interactor.ReduceBrightColorsTileUserActionInteractor
import com.android.systemui.qs.tiles.impl.reducebrightness.domain.model.ReduceBrightColorsTileModel
-import com.android.systemui.qs.tiles.impl.reducebrightness.ui.ReduceBrightColorsTileMapper
-import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
-import com.android.systemui.qs.tiles.viewmodel.QSTileUIConfig
-import com.android.systemui.qs.tiles.viewmodel.QSTileViewModel
-import com.android.systemui.qs.tiles.viewmodel.StubQSTileViewModel
+import com.android.systemui.qs.tiles.impl.reducebrightness.ui.mapper.ReduceBrightColorsTileMapper
import com.android.systemui.res.R
import dagger.Binds
import dagger.Module
diff --git a/packages/SystemUI/src/com/android/systemui/ambient/touch/scrim/BouncerScrimController.java b/packages/SystemUI/src/com/android/systemui/ambient/touch/scrim/BouncerScrimController.java
index 6f2dd799c409..633c13e9e94d 100644
--- a/packages/SystemUI/src/com/android/systemui/ambient/touch/scrim/BouncerScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/ambient/touch/scrim/BouncerScrimController.java
@@ -34,7 +34,7 @@ public class BouncerScrimController implements ScrimController {
@Override
public void show(boolean scrimmed) {
- mStatusBarKeyguardViewManager.showPrimaryBouncer(scrimmed);
+ mStatusBarKeyguardViewManager.showPrimaryBouncer(scrimmed, "BouncerScrimController#show");
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistDisclosure.java b/packages/SystemUI/src/com/android/systemui/assist/AssistDisclosure.java
index f4e2b82f1773..14ce2cf60919 100644
--- a/packages/SystemUI/src/com/android/systemui/assist/AssistDisclosure.java
+++ b/packages/SystemUI/src/com/android/systemui/assist/AssistDisclosure.java
@@ -33,7 +33,6 @@ import android.view.WindowManager;
import android.view.accessibility.AccessibilityEvent;
import com.android.app.animation.Interpolators;
-import com.android.app.viewcapture.ViewCaptureAwareWindowManager;
import com.android.systemui.res.R;
/**
@@ -41,17 +40,16 @@ import com.android.systemui.res.R;
*/
public class AssistDisclosure {
private final Context mContext;
- private final ViewCaptureAwareWindowManager mWm;
+ private final WindowManager mWm;
private final Handler mHandler;
private AssistDisclosureView mView;
private boolean mViewAdded;
- public AssistDisclosure(Context context, Handler handler,
- ViewCaptureAwareWindowManager viewCaptureAwareWindowManager) {
+ public AssistDisclosure(Context context, Handler handler, WindowManager windowManager) {
mContext = context;
mHandler = handler;
- mWm = viewCaptureAwareWindowManager;
+ mWm = windowManager;
}
public void postShow() {
diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java b/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java
index da1c1bc49d23..75ec8dfd881e 100644
--- a/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java
+++ b/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java
@@ -24,8 +24,8 @@ import android.provider.Settings;
import android.service.voice.VisualQueryAttentionResult;
import android.service.voice.VoiceInteractionSession;
import android.util.Log;
+import android.view.WindowManager;
-import com.android.app.viewcapture.ViewCaptureAwareWindowManager;
import com.android.internal.app.AssistUtils;
import com.android.internal.app.IVisualQueryDetectionAttentionListener;
import com.android.internal.app.IVisualQueryRecognitionStatusListener;
@@ -130,6 +130,8 @@ public class AssistManager {
AssistUtils.INVOCATION_TYPE_POWER_BUTTON_LONG_PRESS;
public static final int INVOCATION_TYPE_NAV_HANDLE_LONG_PRESS =
AssistUtils.INVOCATION_TYPE_NAV_HANDLE_LONG_PRESS;
+ public static final int INVOCATION_TYPE_LAUNCHER_SYSTEM_SHORTCUT =
+ AssistUtils.INVOCATION_TYPE_LAUNCHER_SYSTEM_SHORTCUT;
public static final int DISMISS_REASON_INVOCATION_CANCELLED = 1;
public static final int DISMISS_REASON_TAP = 2;
@@ -197,12 +199,12 @@ public class AssistManager {
SelectedUserInteractor selectedUserInteractor,
ActivityManager activityManager,
AssistInteractor interactor,
- ViewCaptureAwareWindowManager viewCaptureAwareWindowManager) {
+ WindowManager windowManager) {
mContext = context;
mDeviceProvisionedController = controller;
mCommandQueue = commandQueue;
mAssistUtils = assistUtils;
- mAssistDisclosure = new AssistDisclosure(context, uiHandler, viewCaptureAwareWindowManager);
+ mAssistDisclosure = new AssistDisclosure(context, uiHandler, windowManager);
mLauncherProxyService = launcherProxyService;
mPhoneStateMonitor = phoneStateMonitor;
mAssistLogger = assistLogger;
diff --git a/packages/SystemUI/src/com/android/systemui/assist/ui/DefaultUiController.java b/packages/SystemUI/src/com/android/systemui/assist/ui/DefaultUiController.java
index 6e257442d139..56273eb9a2cf 100644
--- a/packages/SystemUI/src/com/android/systemui/assist/ui/DefaultUiController.java
+++ b/packages/SystemUI/src/com/android/systemui/assist/ui/DefaultUiController.java
@@ -33,7 +33,6 @@ import android.view.WindowManager;
import android.view.animation.PathInterpolator;
import android.widget.FrameLayout;
-import com.android.app.viewcapture.ViewCaptureAwareWindowManager;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.systemui.assist.AssistLogger;
@@ -67,7 +66,7 @@ public class DefaultUiController implements AssistManager.UiController {
protected InvocationLightsView mInvocationLightsView;
protected final AssistLogger mAssistLogger;
- private final ViewCaptureAwareWindowManager mWindowManager;
+ private final WindowManager mWindowManager;
private final MetricsLogger mMetricsLogger;
private final Lazy<AssistManager> mAssistManagerLazy;
private final WindowManager.LayoutParams mLayoutParams;
@@ -81,12 +80,12 @@ public class DefaultUiController implements AssistManager.UiController {
@Inject
public DefaultUiController(Context context, AssistLogger assistLogger,
- ViewCaptureAwareWindowManager viewCaptureAwareWindowManager,
- MetricsLogger metricsLogger, Lazy<AssistManager> assistManagerLazy,
+ WindowManager windowManager, MetricsLogger metricsLogger,
+ Lazy<AssistManager> assistManagerLazy,
NavigationBarController navigationBarController) {
mAssistLogger = assistLogger;
mRoot = new FrameLayout(context);
- mWindowManager = viewCaptureAwareWindowManager;
+ mWindowManager = windowManager;
mMetricsLogger = metricsLogger;
mAssistManagerLazy = assistManagerLazy;
diff --git a/packages/SystemUI/src/com/android/systemui/battery/BatterySaverModule.kt b/packages/SystemUI/src/com/android/systemui/battery/BatterySaverModule.kt
index 831bc1da5a79..26d5562a3b30 100644
--- a/packages/SystemUI/src/com/android/systemui/battery/BatterySaverModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/battery/BatterySaverModule.kt
@@ -5,15 +5,15 @@ import com.android.systemui.qs.pipeline.shared.TileSpec
import com.android.systemui.qs.shared.model.TileCategory
import com.android.systemui.qs.tileimpl.QSTileImpl
import com.android.systemui.qs.tiles.BatterySaverTile
-import com.android.systemui.qs.tiles.base.interactor.QSTileAvailabilityInteractor
-import com.android.systemui.qs.tiles.base.viewmodel.QSTileViewModelFactory
+import com.android.systemui.qs.tiles.base.domain.interactor.QSTileAvailabilityInteractor
+import com.android.systemui.qs.tiles.base.shared.model.QSTileConfig
+import com.android.systemui.qs.tiles.base.shared.model.QSTileUIConfig
+import com.android.systemui.qs.tiles.base.ui.viewmodel.QSTileViewModel
+import com.android.systemui.qs.tiles.base.ui.viewmodel.QSTileViewModelFactory
import com.android.systemui.qs.tiles.impl.battery.domain.interactor.BatterySaverTileDataInteractor
import com.android.systemui.qs.tiles.impl.battery.domain.interactor.BatterySaverTileUserActionInteractor
import com.android.systemui.qs.tiles.impl.battery.domain.model.BatterySaverTileModel
-import com.android.systemui.qs.tiles.impl.battery.ui.BatterySaverTileMapper
-import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
-import com.android.systemui.qs.tiles.viewmodel.QSTileUIConfig
-import com.android.systemui.qs.tiles.viewmodel.QSTileViewModel
+import com.android.systemui.qs.tiles.impl.battery.ui.mapper.BatterySaverTileMapper
import com.android.systemui.res.R
import dagger.Binds
import dagger.Module
@@ -63,7 +63,7 @@ interface BatterySaverModule {
factory: QSTileViewModelFactory.Static<BatterySaverTileModel>,
mapper: BatterySaverTileMapper,
stateInteractor: BatterySaverTileDataInteractor,
- userActionInteractor: BatterySaverTileUserActionInteractor
+ userActionInteractor: BatterySaverTileUserActionInteractor,
): QSTileViewModel =
factory.create(
TileSpec.create(BATTERY_SAVER_TILE_SPEC),
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
index b8e95ee1dbf0..7bb08edd4773 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
@@ -20,7 +20,6 @@ import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FACE;
import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
import static com.android.internal.jank.InteractionJankMonitor.CUJ_BIOMETRIC_PROMPT_TRANSITION;
-import static com.android.systemui.Flags.enableViewCaptureTracing;
import android.animation.Animator;
import android.annotation.IntDef;
@@ -57,8 +56,6 @@ import android.window.OnBackInvokedDispatcher;
import androidx.constraintlayout.widget.ConstraintLayout;
import com.android.app.animation.Interpolators;
-import com.android.app.viewcapture.ViewCapture;
-import com.android.app.viewcapture.ViewCaptureAwareWindowManager;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.jank.InteractionJankMonitor;
import com.android.internal.widget.LockPatternUtils;
@@ -78,11 +75,10 @@ import com.android.systemui.keyguard.WakefulnessLifecycle;
import com.android.systemui.res.R;
import com.android.systemui.statusbar.VibratorHelper;
import com.android.systemui.util.concurrency.DelayableExecutor;
+import com.android.systemui.utils.windowmanager.WindowManagerUtils;
import com.google.android.msdl.domain.MSDLPlayer;
-import kotlin.Lazy;
-
import kotlinx.coroutines.CoroutineScope;
import java.io.PrintWriter;
@@ -132,7 +128,7 @@ public class AuthContainerView extends LinearLayout
private final Config mConfig;
private final int mEffectiveUserId;
private final IBinder mWindowToken = new Binder();
- private final ViewCaptureAwareWindowManager mWindowManager;
+ private final WindowManager mWindowManager;
@Nullable private final AuthContextPlugins mAuthContextPlugins;
private final Interpolator mLinearOutSlowIn;
private final LockPatternUtils mLockPatternUtils;
@@ -298,16 +294,13 @@ public class AuthContainerView extends LinearLayout
@NonNull Provider<CredentialViewModel> credentialViewModelProvider,
@NonNull @Background DelayableExecutor bgExecutor,
@NonNull VibratorHelper vibratorHelper,
- Lazy<ViewCapture> lazyViewCapture,
@NonNull MSDLPlayer msdlPlayer) {
super(config.mContext);
mConfig = config;
mLockPatternUtils = lockPatternUtils;
mEffectiveUserId = userManager.getCredentialOwnerProfile(mConfig.mUserId);
- WindowManager wm = getContext().getSystemService(WindowManager.class);
- mWindowManager = new ViewCaptureAwareWindowManager(wm, lazyViewCapture,
- enableViewCaptureTracing());
+ mWindowManager = WindowManagerUtils.getWindowManager(getContext());
mAuthContextPlugins = authContextPlugins;
mWakefulnessLifecycle = wakefulnessLifecycle;
mApplicationCoroutineScope = applicationCoroutineScope;
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
index 68a282018ba4..f2c071f14466 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
@@ -22,7 +22,6 @@ import static android.hardware.fingerprint.FingerprintSensorProperties.TYPE_REAR
import static android.view.Display.INVALID_DISPLAY;
import static com.android.systemui.Flags.contAuthPlugin;
-import static com.android.systemui.util.ConvenienceExtensionsKt.toKotlinLazy;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -67,7 +66,6 @@ import android.view.DisplayInfo;
import android.view.MotionEvent;
import android.view.WindowManager;
-import com.android.app.viewcapture.ViewCapture;
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.jank.InteractionJankMonitor;
@@ -93,6 +91,7 @@ import com.android.systemui.statusbar.VibratorHelper;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.util.concurrency.DelayableExecutor;
import com.android.systemui.util.concurrency.Execution;
+import com.android.systemui.utils.windowmanager.WindowManagerProvider;
import com.google.android.msdl.domain.MSDLPlayer;
@@ -194,7 +193,7 @@ public class AuthController implements
@NonNull private final VibratorHelper mVibratorHelper;
@NonNull private final MSDLPlayer mMSDLPlayer;
- private final kotlin.Lazy<ViewCapture> mLazyViewCapture;
+ private final WindowManagerProvider mWindowManagerProvider;
@VisibleForTesting
final TaskStackListener mTaskStackListener = new TaskStackListener() {
@@ -693,8 +692,8 @@ public class AuthController implements
@NonNull UdfpsUtils udfpsUtils,
@NonNull VibratorHelper vibratorHelper,
@NonNull KeyguardManager keyguardManager,
- Lazy<ViewCapture> daggerLazyViewCapture,
- @NonNull MSDLPlayer msdlPlayer) {
+ @NonNull MSDLPlayer msdlPlayer,
+ WindowManagerProvider windowManagerProvider) {
mContext = context;
mExecution = execution;
mUserManager = userManager;
@@ -755,7 +754,7 @@ public class AuthController implements
context.registerReceiver(mBroadcastReceiver, filter, Context.RECEIVER_EXPORTED_UNAUDITED);
mSensorPrivacyManager = context.getSystemService(SensorPrivacyManager.class);
- mLazyViewCapture = toKotlinLazy(daggerLazyViewCapture);
+ mWindowManagerProvider = windowManagerProvider;
}
// TODO(b/229290039): UDFPS controller should manage its dimensions on its own. Remove this.
@@ -1278,7 +1277,7 @@ public class AuthController implements
Log.e(TAG, "unable to get Display for user=" + userId);
return null;
}
- return mContext.createDisplayContext(display).getSystemService(WindowManager.class);
+ return mWindowManagerProvider.getWindowManager(mContext.createDisplayContext(display));
}
private void onDialogDismissed(@BiometricPrompt.DismissedReason int reason) {
@@ -1337,8 +1336,7 @@ public class AuthController implements
return new AuthContainerView(config, mApplicationCoroutineScope, mFpProps, mFaceProps,
wakefulnessLifecycle, userManager, mContextPlugins, lockPatternUtils,
mInteractionJankMonitor, mPromptSelectorInteractor, viewModel,
- mCredentialViewModelProvider, bgExecutor, mVibratorHelper,
- mLazyViewCapture, mMSDLPlayer);
+ mCredentialViewModelProvider, bgExecutor, mVibratorHelper, mMSDLPlayer);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
index 22d2aaf2a8e7..87e9784ddbc9 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
@@ -32,7 +32,6 @@ import com.android.keyguard.KeyguardUpdateMonitorCallback
import com.android.keyguard.logging.KeyguardLogger
import com.android.settingslib.Utils
import com.android.systemui.CoreStartable
-import com.android.systemui.Flags.lightRevealMigration
import com.android.systemui.biometrics.data.repository.FacePropertyRepository
import com.android.systemui.biometrics.shared.model.UdfpsOverlayParams
import com.android.systemui.dagger.SysUISingleton
@@ -43,6 +42,7 @@ import com.android.systemui.keyguard.shared.model.BiometricUnlockSource
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.res.R
+import com.android.systemui.shared.Flags.ambientAod
import com.android.systemui.statusbar.CircleReveal
import com.android.systemui.statusbar.LiftReveal
import com.android.systemui.statusbar.LightRevealEffect
@@ -196,7 +196,7 @@ constructor(
// This code path is not used if the KeyguardTransitionRepository is managing the light
// reveal scrim.
- if (!lightRevealMigration()) {
+ if (!ambientAod()) {
if (statusBarStateController.isDozing || biometricUnlockController.isWakeAndUnlock) {
circleReveal?.let {
lightRevealScrim.revealAmount = 0f
@@ -213,7 +213,7 @@ constructor(
}
override fun onKeyguardFadingAwayChanged() {
- if (lightRevealMigration()) {
+ if (ambientAod()) {
return
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
index dfe8eb28b2a6..dbaa90c10313 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
@@ -52,17 +52,18 @@ import android.os.PowerManager;
import android.os.Trace;
import android.os.VibrationAttributes;
import android.os.VibrationEffect;
+import android.provider.Settings;
import android.util.Log;
import android.view.HapticFeedbackConstants;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
+import android.view.WindowManager;
import android.view.accessibility.AccessibilityManager;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
-import com.android.app.viewcapture.ViewCaptureAwareWindowManager;
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.InstanceId;
@@ -145,7 +146,7 @@ public class UdfpsController implements DozeReceiver, Dumpable {
private final Execution mExecution;
private final FingerprintManager mFingerprintManager;
@NonNull private final LayoutInflater mInflater;
- private final ViewCaptureAwareWindowManager mWindowManager;
+ private final WindowManager mWindowManager;
private final DelayableExecutor mFgExecutor;
@NonNull private final Executor mBiometricExecutor;
@NonNull private final StatusBarStateController mStatusBarStateController;
@@ -659,7 +660,7 @@ public class UdfpsController implements DozeReceiver, Dumpable {
@NonNull Execution execution,
@NonNull @ShadeDisplayAware LayoutInflater inflater,
@Nullable FingerprintManager fingerprintManager,
- @NonNull ViewCaptureAwareWindowManager viewCaptureAwareWindowManager,
+ @NonNull @Main WindowManager windowManager,
@NonNull StatusBarStateController statusBarStateController,
@Main DelayableExecutor fgExecutor,
@NonNull StatusBarKeyguardViewManager statusBarKeyguardViewManager,
@@ -705,7 +706,7 @@ public class UdfpsController implements DozeReceiver, Dumpable {
// The fingerprint manager is queried for UDFPS before this class is constructed, so the
// fingerprint manager should never be null.
mFingerprintManager = checkNotNull(fingerprintManager);
- mWindowManager = viewCaptureAwareWindowManager;
+ mWindowManager = windowManager;
mFgExecutor = fgExecutor;
mStatusBarStateController = statusBarStateController;
mKeyguardStateController = keyguardStateController;
@@ -880,7 +881,7 @@ public class UdfpsController implements DozeReceiver, Dumpable {
Log.v(TAG, "aod lock icon long-press rejected by the falsing manager.");
return;
}
- mKeyguardViewManager.showPrimaryBouncer(true);
+ mKeyguardViewManager.showPrimaryBouncer(true, "UdfpsController#onAodInterrupt");
// play the same haptic as the DeviceEntryIcon longpress
if (mOverlay != null && mOverlay.getTouchOverlay() != null) {
@@ -920,12 +921,21 @@ public class UdfpsController implements DozeReceiver, Dumpable {
true /* isAod */);
};
- if (mScreenOn) {
+ if (isScreenOffUnlockEnabled() || mScreenOn) {
mAodInterruptRunnable.run();
mAodInterruptRunnable = null;
}
}
+ private boolean isScreenOffUnlockEnabled() {
+ return mContext.getResources().getBoolean(R.bool.config_screen_off_udfps_enabled)
+ && Settings.Secure.getIntForUser(
+ mContext.getContentResolver(),
+ Settings.Secure.SCREEN_OFF_UNLOCK_UDFPS_ENABLED,
+ 0,
+ mContext.getUserId()) != 0;
+ }
+
/**
* Add a callback for fingerUp and fingerDown events
*/
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt
index bdf58275effa..61b670715572 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt
@@ -42,7 +42,6 @@ import android.view.accessibility.AccessibilityManager
import android.view.accessibility.AccessibilityManager.TouchExplorationStateChangeListener
import androidx.annotation.VisibleForTesting
import com.android.app.tracing.coroutines.launchTraced as launch
-import com.android.app.viewcapture.ViewCaptureAwareWindowManager
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.systemui.animation.ActivityTransitionAnimator
import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor
@@ -88,7 +87,7 @@ class UdfpsControllerOverlay
constructor(
private val context: Context,
private val inflater: LayoutInflater,
- private val windowManager: ViewCaptureAwareWindowManager,
+ private val windowManager: WindowManager,
private val accessibilityManager: AccessibilityManager,
private val statusBarStateController: StatusBarStateController,
private val statusBarKeyguardViewManager: StatusBarKeyguardViewManager,
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt
index 54c52b533da4..3b22e13f29a2 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt
@@ -397,7 +397,7 @@ object BiometricViewBinder {
// Talkback directional guidance
udfpsGuidanceView.setOnHoverListener { _, event ->
launch {
- viewModel.onAnnounceAccessibilityHint(
+ viewModel.onUpdateAccessibilityHint(
event,
accessibilityManager.isTouchExplorationEnabled,
)
@@ -406,7 +406,9 @@ object BiometricViewBinder {
}
launch {
viewModel.accessibilityHint.collect { message ->
- if (message.isNotBlank()) view.announceForAccessibility(message)
+ if (message.isNotBlank()) {
+ udfpsGuidanceView.contentDescription = message
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewSizeBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewSizeBinder.kt
index 02c378417f3d..fcc0121e8f93 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewSizeBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewSizeBinder.kt
@@ -27,7 +27,6 @@ import android.view.View
import android.view.ViewGroup
import android.view.ViewOutlineProvider
import android.view.WindowInsets
-import android.view.WindowManager
import android.view.accessibility.AccessibilityManager
import android.widget.ImageView
import android.widget.TextView
@@ -48,6 +47,7 @@ import com.android.systemui.biometrics.ui.viewmodel.isSmall
import com.android.systemui.biometrics.ui.viewmodel.isTop
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.res.R
+import com.android.systemui.utils.windowmanager.WindowManagerUtils
import kotlin.math.abs
import kotlinx.coroutines.flow.combine
import com.android.app.tracing.coroutines.launchTraced as launch
@@ -66,7 +66,7 @@ object BiometricViewSizeBinder {
viewsToHideWhenSmall: List<View>,
jankListener: BiometricJankListener,
) {
- val windowManager = requireNotNull(view.context.getSystemService(WindowManager::class.java))
+ val windowManager = WindowManagerUtils.getWindowManager(view.context)
val accessibilityManager =
requireNotNull(view.context.getSystemService(AccessibilityManager::class.java))
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt
index 0902d19b6787..4e17a2658ee7 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt
@@ -886,7 +886,7 @@ constructor(
}
/** Sets the message used for UDFPS directional guidance */
- suspend fun onAnnounceAccessibilityHint(
+ suspend fun onUpdateAccessibilityHint(
event: MotionEvent,
touchExplorationEnabled: Boolean,
): Boolean {
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractor.kt b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractor.kt
index 0c6d7920d7f3..48e08fcd90c5 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractor.kt
@@ -135,7 +135,7 @@ constructor(
// 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): Boolean {
+ fun show(isScrimmed: Boolean, reason: String): Boolean {
// When the scene container framework is enabled, instead of calling this, call
// SceneInteractor#changeScene(Scenes.Bouncer, ...).
SceneContainerFlag.assertInLegacyMode()
@@ -176,6 +176,7 @@ constructor(
return false
}
+ Log.i(TAG, "Show primary bouncer requested, reason: $reason")
repository.setPrimaryShowingSoon(true)
if (usePrimaryBouncerPassiveAuthDelay()) {
Log.d(TAG, "delay bouncer, passive auth may succeed")
diff --git a/packages/SystemUI/src/com/android/systemui/charging/WiredChargingRippleController.kt b/packages/SystemUI/src/com/android/systemui/charging/WiredChargingRippleController.kt
index 7d518f4f7e78..718ef51aa161 100644
--- a/packages/SystemUI/src/com/android/systemui/charging/WiredChargingRippleController.kt
+++ b/packages/SystemUI/src/com/android/systemui/charging/WiredChargingRippleController.kt
@@ -23,20 +23,19 @@ import android.os.SystemProperties
import android.view.Surface
import android.view.View
import android.view.WindowManager
-import com.android.app.viewcapture.ViewCaptureAwareWindowManager
import com.android.internal.annotations.VisibleForTesting
import com.android.internal.logging.UiEvent
import com.android.internal.logging.UiEventLogger
import com.android.settingslib.Utils
+import com.android.systemui.res.R
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
-import com.android.systemui.res.R
+import com.android.systemui.surfaceeffects.ripple.RippleView
import com.android.systemui.statusbar.commandline.Command
import com.android.systemui.statusbar.commandline.CommandRegistry
import com.android.systemui.statusbar.policy.BatteryController
import com.android.systemui.statusbar.policy.ConfigurationController
-import com.android.systemui.surfaceeffects.ripple.RippleView
import com.android.systemui.util.time.SystemClock
import java.io.PrintWriter
import javax.inject.Inject
@@ -58,7 +57,6 @@ class WiredChargingRippleController @Inject constructor(
featureFlags: FeatureFlags,
private val context: Context,
private val windowManager: WindowManager,
- private val viewCaptureAwareWindowManager: ViewCaptureAwareWindowManager,
private val systemClock: SystemClock,
private val uiEventLogger: UiEventLogger
) {
@@ -163,12 +161,12 @@ class WiredChargingRippleController @Inject constructor(
override fun onViewAttachedToWindow(view: View) {
layoutRipple()
rippleView.startRipple(Runnable {
- viewCaptureAwareWindowManager.removeView(rippleView)
+ windowManager.removeView(rippleView)
})
rippleView.removeOnAttachStateChangeListener(this)
}
})
- viewCaptureAwareWindowManager.addView(rippleView, windowLayoutParams)
+ windowManager.addView(rippleView, windowLayoutParams)
uiEventLogger.log(WiredChargingRippleEvent.CHARGING_RIPPLE_PLAYED)
}
diff --git a/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingAnimation.java b/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingAnimation.java
index e5e9c4685264..4ca55400561e 100644
--- a/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingAnimation.java
+++ b/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingAnimation.java
@@ -28,10 +28,10 @@ import android.util.Slog;
import android.view.Gravity;
import android.view.WindowManager;
-import com.android.app.viewcapture.ViewCaptureAwareWindowManager;
import com.android.internal.logging.UiEvent;
import com.android.internal.logging.UiEventLogger;
import com.android.systemui.surfaceeffects.ripple.RippleShader.RippleShape;
+import com.android.systemui.utils.windowmanager.WindowManagerProvider;
/**
* A WirelessChargingAnimation is a view containing view + animation for wireless charging.
@@ -60,11 +60,11 @@ public class WirelessChargingAnimation {
*/
private WirelessChargingAnimation(@NonNull Context context, @Nullable Looper looper,
int transmittingBatteryLevel, int batteryLevel, Callback callback, boolean isDozing,
- RippleShape rippleShape, UiEventLogger uiEventLogger,
- ViewCaptureAwareWindowManager viewCaptureAwareWindowManager) {
+ RippleShape rippleShape, UiEventLogger uiEventLogger, WindowManager windowManager,
+ WindowManagerProvider windowManagerProvider) {
mCurrentWirelessChargingView = new WirelessChargingView(context, looper,
transmittingBatteryLevel, batteryLevel, callback, isDozing,
- rippleShape, uiEventLogger, viewCaptureAwareWindowManager);
+ rippleShape, uiEventLogger, windowManager, windowManagerProvider);
}
/**
@@ -75,11 +75,11 @@ public class WirelessChargingAnimation {
public static WirelessChargingAnimation makeWirelessChargingAnimation(@NonNull Context context,
@Nullable Looper looper, int transmittingBatteryLevel, int batteryLevel,
Callback callback, boolean isDozing, RippleShape rippleShape,
- UiEventLogger uiEventLogger,
- ViewCaptureAwareWindowManager viewCaptureAwareWindowManager) {
+ UiEventLogger uiEventLogger, WindowManager windowManager,
+ WindowManagerProvider windowManagerProvider) {
return new WirelessChargingAnimation(context, looper, transmittingBatteryLevel,
- batteryLevel, callback, isDozing, rippleShape, uiEventLogger,
- viewCaptureAwareWindowManager);
+ batteryLevel, callback, isDozing, rippleShape, uiEventLogger, windowManager,
+ windowManagerProvider);
}
/**
@@ -88,10 +88,10 @@ public class WirelessChargingAnimation {
*/
public static WirelessChargingAnimation makeChargingAnimationWithNoBatteryLevel(
@NonNull Context context, RippleShape rippleShape, UiEventLogger uiEventLogger,
- ViewCaptureAwareWindowManager viewCaptureAwareWindowManager) {
+ WindowManager windowManager, WindowManagerProvider windowManagerProvider) {
return makeWirelessChargingAnimation(context, null,
UNKNOWN_BATTERY_LEVEL, UNKNOWN_BATTERY_LEVEL, null, false,
- rippleShape, uiEventLogger, viewCaptureAwareWindowManager);
+ rippleShape, uiEventLogger, windowManager, windowManagerProvider);
}
/**
@@ -123,19 +123,21 @@ public class WirelessChargingAnimation {
private int mGravity;
private WirelessChargingLayout mView;
private WirelessChargingLayout mNextView;
- private ViewCaptureAwareWindowManager mWM;
+ private WindowManager mWM;
private Callback mCallback;
+ private WindowManagerProvider mWindowManagerProvider;
public WirelessChargingView(Context context, @Nullable Looper looper,
int transmittingBatteryLevel, int batteryLevel, Callback callback,
boolean isDozing, RippleShape rippleShape, UiEventLogger uiEventLogger,
- ViewCaptureAwareWindowManager viewCaptureAwareWindowManager) {
+ WindowManager windowManager, WindowManagerProvider windowManagerProvider) {
mCallback = callback;
mNextView = new WirelessChargingLayout(context, transmittingBatteryLevel, batteryLevel,
isDozing, rippleShape);
mGravity = Gravity.CENTER_HORIZONTAL | Gravity.CENTER;
mUiEventLogger = uiEventLogger;
- mWM = viewCaptureAwareWindowManager;
+ mWM = windowManager;
+ mWindowManagerProvider = windowManagerProvider;
final WindowManager.LayoutParams params = mParams;
params.height = WindowManager.LayoutParams.MATCH_PARENT;
@@ -207,6 +209,7 @@ public class WirelessChargingAnimation {
if (context == null) {
context = mView.getContext();
}
+ mWM = mWindowManagerProvider.getWindowManager(context);
mParams.packageName = packageName;
mParams.hideTimeoutMilliseconds = DURATION;
diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayWindow.java b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayWindow.java
index dc3b50c93298..059ea3271235 100644
--- a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayWindow.java
+++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayWindow.java
@@ -27,7 +27,6 @@ import android.view.Window;
import android.view.WindowInsets;
import android.view.WindowManager;
-import com.android.app.viewcapture.ViewCaptureAwareWindowManager;
import com.android.internal.policy.PhoneWindow;
import com.android.systemui.clipboardoverlay.dagger.ClipboardOverlayModule.OverlayWindowContext;
import com.android.systemui.screenshot.FloatingWindowUtil;
@@ -45,7 +44,6 @@ public class ClipboardOverlayWindow extends PhoneWindow
private final Context mContext;
private final WindowManager mWindowManager;
- private final ViewCaptureAwareWindowManager mViewCaptureAwareWindowManager;
private final WindowManager.LayoutParams mWindowLayoutParams;
private boolean mKeyboardVisible;
@@ -55,7 +53,6 @@ public class ClipboardOverlayWindow extends PhoneWindow
@Inject
ClipboardOverlayWindow(@OverlayWindowContext Context context,
- @OverlayWindowContext ViewCaptureAwareWindowManager viewCaptureAwareWindowManager,
@OverlayWindowContext WindowManager windowManager) {
super(context);
mContext = context;
@@ -66,10 +63,9 @@ public class ClipboardOverlayWindow extends PhoneWindow
requestFeature(Window.FEATURE_ACTIVITY_TRANSITIONS);
setBackgroundDrawableResource(android.R.color.transparent);
mWindowManager = windowManager;
- mViewCaptureAwareWindowManager = viewCaptureAwareWindowManager;
mWindowLayoutParams = FloatingWindowUtil.getFloatingWindowParams();
mWindowLayoutParams.setTitle("ClipboardOverlay");
- setWindowManager(windowManager, null, null);
+ setWindowManager(mWindowManager, null, null);
setWindowFocusable(false);
}
@@ -86,12 +82,10 @@ public class ClipboardOverlayWindow extends PhoneWindow
attach();
withWindowAttached(() -> {
- WindowInsets currentInsets = mWindowManager.getCurrentWindowMetrics()
- .getWindowInsets();
+ WindowInsets currentInsets = mWindowManager.getCurrentWindowMetrics().getWindowInsets();
mKeyboardVisible = currentInsets.isVisible(WindowInsets.Type.ime());
peekDecorView().getViewTreeObserver().addOnGlobalLayoutListener(() -> {
- WindowInsets insets = mWindowManager.getCurrentWindowMetrics()
- .getWindowInsets();
+ WindowInsets insets = mWindowManager.getCurrentWindowMetrics().getWindowInsets();
boolean keyboardVisible = insets.isVisible(WindowInsets.Type.ime());
if (keyboardVisible != mKeyboardVisible) {
mKeyboardVisible = keyboardVisible;
@@ -112,7 +106,7 @@ public class ClipboardOverlayWindow extends PhoneWindow
void remove() {
final View decorView = peekDecorView();
if (decorView != null && decorView.isAttachedToWindow()) {
- mViewCaptureAwareWindowManager.removeViewImmediate(decorView);
+ mWindowManager.removeViewImmediate(decorView);
}
}
@@ -146,7 +140,7 @@ public class ClipboardOverlayWindow extends PhoneWindow
if (decorView.isAttachedToWindow()) {
return;
}
- mViewCaptureAwareWindowManager.addView(decorView, mWindowLayoutParams);
+ mWindowManager.addView(decorView, mWindowLayoutParams);
decorView.requestApplyInsets();
}
@@ -167,7 +161,7 @@ public class ClipboardOverlayWindow extends PhoneWindow
}
final View decorView = peekDecorView();
if (decorView != null && decorView.isAttachedToWindow()) {
- mViewCaptureAwareWindowManager.updateViewLayout(decorView, mWindowLayoutParams);
+ mWindowManager.updateViewLayout(decorView, mWindowLayoutParams);
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/dagger/ClipboardOverlayModule.java b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/dagger/ClipboardOverlayModule.java
index c86a84b17efe..7a60cce63a33 100644
--- a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/dagger/ClipboardOverlayModule.java
+++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/dagger/ClipboardOverlayModule.java
@@ -19,9 +19,7 @@ package com.android.systemui.clipboardoverlay.dagger;
import static android.view.WindowManager.LayoutParams.TYPE_SCREENSHOT;
import static com.android.systemui.Flags.clipboardOverlayMultiuser;
-import static com.android.systemui.Flags.enableViewCaptureTracing;
import static com.android.systemui.shared.Flags.usePreferredImageEditor;
-import static com.android.systemui.util.ConvenienceExtensionsKt.toKotlinLazy;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
@@ -31,8 +29,6 @@ import android.view.Display;
import android.view.LayoutInflater;
import android.view.WindowManager;
-import com.android.app.viewcapture.ViewCapture;
-import com.android.app.viewcapture.ViewCaptureAwareWindowManager;
import com.android.systemui.clipboardoverlay.ActionIntentCreator;
import com.android.systemui.clipboardoverlay.ClipboardOverlayView;
import com.android.systemui.clipboardoverlay.DefaultIntentCreator;
@@ -40,6 +36,7 @@ import com.android.systemui.clipboardoverlay.IntentCreator;
import com.android.systemui.res.R;
import com.android.systemui.settings.DisplayTracker;
import com.android.systemui.settings.UserTracker;
+import com.android.systemui.utils.windowmanager.WindowManagerProvider;
import dagger.Lazy;
import dagger.Module;
@@ -89,21 +86,9 @@ public interface ClipboardOverlayModule {
*/
@Provides
@OverlayWindowContext
- static WindowManager provideWindowManager(@OverlayWindowContext Context context) {
- return context.getSystemService(WindowManager.class);
- }
-
- /**
- *
- */
- @Provides
- @OverlayWindowContext
- static ViewCaptureAwareWindowManager provideViewCaptureAwareWindowManager(
- @OverlayWindowContext WindowManager windowManager,
- Lazy<ViewCapture> daggerLazyViewCapture) {
- return new ViewCaptureAwareWindowManager(windowManager,
- /* lazyViewCapture= */ toKotlinLazy(daggerLazyViewCapture),
- /* isViewCaptureEnabled= */ enableViewCaptureTracing());
+ static WindowManager provideWindowManager(@OverlayWindowContext Context context,
+ WindowManagerProvider windowManagerProvider) {
+ return windowManagerProvider.getWindowManager(context);
}
@Provides
diff --git a/packages/SystemUI/src/com/android/systemui/common/domain/interactor/SysUIStateDisplaysInteractor.kt b/packages/SystemUI/src/com/android/systemui/common/domain/interactor/SysUIStateDisplaysInteractor.kt
index 9db7b50905f8..1301fb87069e 100644
--- a/packages/SystemUI/src/com/android/systemui/common/domain/interactor/SysUIStateDisplaysInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/common/domain/interactor/SysUIStateDisplaysInteractor.kt
@@ -17,9 +17,9 @@
package com.android.systemui.common.domain.interactor
import android.util.Log
+import com.android.app.displaylib.PerDisplayRepository
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.display.data.repository.DisplayRepository
-import com.android.systemui.display.data.repository.PerDisplayRepository
import com.android.systemui.model.StateChange
import com.android.systemui.model.SysUiState
import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/systemui/common/shared/model/Icon.kt b/packages/SystemUI/src/com/android/systemui/common/shared/model/Icon.kt
index 2adaec21867f..6792f3188986 100644
--- a/packages/SystemUI/src/com/android/systemui/common/shared/model/Icon.kt
+++ b/packages/SystemUI/src/com/android/systemui/common/shared/model/Icon.kt
@@ -19,6 +19,7 @@ package com.android.systemui.common.shared.model
import android.annotation.DrawableRes
import android.graphics.drawable.Drawable
import androidx.compose.runtime.Stable
+import com.android.systemui.common.shared.model.Icon.Loaded
/**
* Models an icon, that can either be already [loaded][Icon.Loaded] or be a [reference]
@@ -33,8 +34,37 @@ sealed class Icon {
constructor(
val drawable: Drawable,
override val contentDescription: ContentDescription?,
+ /**
+ * Serves as an id to compare two instances. When provided this is used alongside
+ * [contentDescription] to determine equality. This is useful when comparing icons
+ * representing the same UI, but with different [drawable] instances.
+ */
@DrawableRes val res: Int? = null,
- ) : Icon()
+ ) : Icon() {
+
+ override fun equals(other: Any?): Boolean {
+ val that = other as? Loaded ?: return false
+
+ if (this.res != null && that.res != null) {
+ return this.res == that.res && this.contentDescription == that.contentDescription
+ }
+
+ return this.res == that.res &&
+ this.drawable == that.drawable &&
+ this.contentDescription == that.contentDescription
+ }
+
+ override fun hashCode(): Int {
+ var result = contentDescription?.hashCode() ?: 0
+ result =
+ if (res != null) {
+ 31 * result + res.hashCode()
+ } else {
+ 31 * result + drawable.hashCode()
+ }
+ return result
+ }
+ }
data class Resource(
@DrawableRes val res: Int,
@@ -49,4 +79,4 @@ sealed class Icon {
fun Drawable.asIcon(
contentDescription: ContentDescription? = null,
@DrawableRes res: Int? = null,
-): Icon.Loaded = Icon.Loaded(this, contentDescription, res)
+): Loaded = Loaded(this, contentDescription, res)
diff --git a/packages/SystemUI/src/com/android/systemui/common/ui/compose/gestures/EagerTap.kt b/packages/SystemUI/src/com/android/systemui/common/ui/compose/gestures/EagerTap.kt
new file mode 100644
index 000000000000..078ea569a63c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/common/ui/compose/gestures/EagerTap.kt
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2025 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.common.ui.compose.gestures
+
+import androidx.compose.foundation.gestures.awaitEachGesture
+import androidx.compose.foundation.gestures.awaitFirstDown
+import androidx.compose.foundation.gestures.waitForUpOrCancellation
+import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.input.pointer.PointerInputChange
+import androidx.compose.ui.input.pointer.PointerInputScope
+import kotlinx.coroutines.coroutineScope
+
+/**
+ * Detects taps and double taps without waiting for the double tap minimum delay in between
+ *
+ * Using [detectTapGestures] with both a single tap and a double tap defined will send only one of
+ * these event per user interaction. This variant will send the single tap at all times, with the
+ * optional double tap if the user pressed a second time in a short period of time.
+ *
+ * Warning: Use this only if you know that reporting a single tap followed by a double tap won't be
+ * a problem in your use case.
+ *
+ * @param doubleTapEnabled whether this should listen for double tap events. This value is captured
+ * at the first down movement.
+ * @param onDoubleTap the double tap callback
+ * @param onTap the single tap callback
+ */
+suspend fun PointerInputScope.detectEagerTapGestures(
+ doubleTapEnabled: () -> Boolean,
+ onDoubleTap: (Offset) -> Unit,
+ onTap: () -> Unit,
+) = coroutineScope {
+ awaitEachGesture {
+ val down = awaitFirstDown()
+ down.consume()
+
+ // Capture whether double tap is enabled on first down as this state can change following
+ // the first tap
+ val isDoubleTapEnabled = doubleTapEnabled()
+
+ // wait for first tap up or long press
+ val upOrCancel = waitForUpOrCancellation()
+
+ if (upOrCancel != null) {
+ // tap was successful.
+ upOrCancel.consume()
+ onTap.invoke()
+
+ if (isDoubleTapEnabled) {
+ // check for second tap
+ val secondDown =
+ withTimeoutOrNull(viewConfiguration.doubleTapTimeoutMillis) {
+ val minUptime =
+ upOrCancel.uptimeMillis + viewConfiguration.doubleTapMinTimeMillis
+ var change: PointerInputChange
+ // The second tap doesn't count if it happens before DoubleTapMinTime of the
+ // first tap
+ do {
+ change = awaitFirstDown()
+ } while (change.uptimeMillis < minUptime)
+ change
+ }
+
+ if (secondDown != null) {
+ // Second tap down detected
+
+ // Might have a long second press as the second tap
+ val secondUp = waitForUpOrCancellation()
+ if (secondUp != null) {
+ secondUp.consume()
+ onDoubleTap(secondUp.position)
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/DeviceInactiveCondition.kt b/packages/SystemUI/src/com/android/systemui/communal/DeviceInactiveCondition.kt
index 11c285b3e4e9..70dce2ef0cac 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/DeviceInactiveCondition.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/DeviceInactiveCondition.kt
@@ -18,6 +18,7 @@ package com.android.systemui.communal
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.keyguard.KeyguardUpdateMonitorCallback
import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.keyguard.WakefulnessLifecycle
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.keyguard.shared.model.DozeStateModel.Companion.isDozeOff
@@ -28,6 +29,7 @@ import com.android.systemui.util.kotlin.JavaAdapter
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
+import kotlinx.coroutines.launch
/**
* Condition which estimates device inactivity in order to avoid launching a full-screen activity
@@ -36,13 +38,14 @@ import kotlinx.coroutines.Job
class DeviceInactiveCondition
@Inject
constructor(
- @Application scope: CoroutineScope,
+ @Application private val applicationScope: CoroutineScope,
+ @Background backgroundScope: CoroutineScope,
private val keyguardStateController: KeyguardStateController,
private val wakefulnessLifecycle: WakefulnessLifecycle,
private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
private val keyguardInteractor: KeyguardInteractor,
private val javaAdapter: JavaAdapter,
-) : Condition(scope) {
+) : Condition(backgroundScope) {
private var anyDozeListenerJob: Job? = null
private var anyDoze = false
private val keyguardStateCallback: KeyguardStateController.Callback =
@@ -67,7 +70,9 @@ constructor(
override suspend fun start() {
updateState()
keyguardStateController.addCallback(keyguardStateCallback)
- keyguardUpdateMonitor.registerCallback(keyguardUpdateCallback)
+
+ // Keyguard update monitor callbacks must be registered on the main thread
+ applicationScope.launch { keyguardUpdateMonitor.registerCallback(keyguardUpdateCallback) }
wakefulnessLifecycle.addObserver(wakefulnessObserver)
anyDozeListenerJob =
javaAdapter.alwaysCollectFlow(keyguardInteractor.dozeTransitionModel) {
diff --git a/packages/SystemUI/src/com/android/systemui/communal/DevicePosturingListener.kt b/packages/SystemUI/src/com/android/systemui/communal/DevicePosturingListener.kt
index af8a5fa23ccb..8bfec0a5dac2 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/DevicePosturingListener.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/DevicePosturingListener.kt
@@ -21,8 +21,11 @@ import android.app.DreamManager
import android.service.dreams.Flags.allowDreamWhenPostured
import com.android.app.tracing.coroutines.launchInTraced
import com.android.systemui.CoreStartable
+import com.android.systemui.common.domain.interactor.BatteryInteractor
+import com.android.systemui.communal.domain.interactor.CommunalSettingsInteractor
import com.android.systemui.communal.posturing.domain.interactor.PosturingInteractor
import com.android.systemui.communal.posturing.shared.model.PosturedState
+import com.android.systemui.communal.shared.model.WhenToDream
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.log.dagger.CommunalTableLog
@@ -30,10 +33,14 @@ import com.android.systemui.log.table.TableLogBuffer
import com.android.systemui.log.table.logDiffsForTable
import com.android.systemui.statusbar.commandline.Command
import com.android.systemui.statusbar.commandline.CommandRegistry
+import com.android.systemui.util.kotlin.BooleanFlowOperators.allOf
+import com.android.systemui.utils.coroutines.flow.flatMapLatestConflated
import java.io.PrintWriter
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onEach
@SysUISingleton
@@ -42,19 +49,36 @@ class DevicePosturingListener
constructor(
private val commandRegistry: CommandRegistry,
private val dreamManager: DreamManager,
- private val interactor: PosturingInteractor,
+ private val posturingInteractor: PosturingInteractor,
+ communalSettingsInteractor: CommunalSettingsInteractor,
+ batteryInteractor: BatteryInteractor,
@Background private val bgScope: CoroutineScope,
@CommunalTableLog private val tableLogBuffer: TableLogBuffer,
) : CoreStartable {
private val command = DevicePosturingCommand()
+ // Only subscribe to posturing if applicable to avoid running the posturing CHRE nanoapp
+ // if posturing signal is not needed.
+ private val postured =
+ allOf(
+ batteryInteractor.isDevicePluggedIn,
+ communalSettingsInteractor.whenToDream.map { it == WhenToDream.WHILE_POSTURED },
+ )
+ .flatMapLatestConflated { shouldListen ->
+ if (shouldListen) {
+ posturingInteractor.postured
+ } else {
+ flowOf(false)
+ }
+ }
+
@SuppressLint("MissingPermission")
override fun start() {
if (!allowDreamWhenPostured()) {
return
}
- interactor.postured
+ postured
.distinctUntilChanged()
.logDiffsForTable(
tableLogBuffer = tableLogBuffer,
@@ -78,7 +102,7 @@ constructor(
val state =
when (arg.lowercase()) {
- "true" -> PosturedState.Postured(confidence = 1f)
+ "true" -> PosturedState.Postured
"false" -> PosturedState.NotPostured
"clear" -> PosturedState.Unknown
else -> {
@@ -87,7 +111,7 @@ constructor(
null
}
}
- state?.let { interactor.setValueForDebug(it) }
+ state?.let { posturingInteractor.setValueForDebug(it) }
}
override fun help(pw: PrintWriter) {
diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalSettingsRepository.kt b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalSettingsRepository.kt
index 6f688d172843..42a345b7deb4 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalSettingsRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalSettingsRepository.kt
@@ -32,6 +32,7 @@ import com.android.systemui.communal.data.model.SuppressionReason
import com.android.systemui.communal.data.repository.CommunalSettingsRepositoryModule.Companion.DEFAULT_BACKGROUND_TYPE
import com.android.systemui.communal.shared.model.CommunalBackgroundType
import com.android.systemui.communal.shared.model.WhenToDream
+import com.android.systemui.communal.shared.model.WhenToStartHub
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
@@ -64,6 +65,12 @@ interface CommunalSettingsRepository {
*/
fun getWhenToDreamState(user: UserInfo): Flow<WhenToDream>
+ /**
+ * Returns a[WhenToStartHub] for the specified user, indicating what state the device should be
+ * in to automatically display the hub.
+ */
+ fun getWhenToStartHubState(user: UserInfo): Flow<WhenToStartHub>
+
/** Returns whether glanceable hub is enabled by the current user. */
fun getSettingEnabledByUser(user: UserInfo): Flow<Boolean>
@@ -124,6 +131,10 @@ constructor(
resources.getBoolean(com.android.internal.R.bool.config_dreamsActivatedOnPosturedByDefault)
}
+ private val whenToStartHubByDefault by lazy {
+ resources.getInteger(com.android.internal.R.integer.config_whenToStartHubModeDefault)
+ }
+
private val _suppressionReasons =
MutableStateFlow<List<SuppressionReason>>(
// Suppress hub by default until we get an initial update.
@@ -195,6 +206,31 @@ constructor(
}
.flowOn(bgDispatcher)
+ override fun getWhenToStartHubState(user: UserInfo): Flow<WhenToStartHub> =
+ secureSettings
+ .observerFlow(
+ userId = user.id,
+ names = arrayOf(Settings.Secure.WHEN_TO_START_GLANCEABLE_HUB),
+ )
+ .emitOnStart()
+ .map {
+ when (
+ secureSettings.getIntForUser(
+ Settings.Secure.WHEN_TO_START_GLANCEABLE_HUB,
+ whenToStartHubByDefault,
+ user.id,
+ )
+ ) {
+ Settings.Secure.GLANCEABLE_HUB_START_NEVER -> WhenToStartHub.NEVER
+ Settings.Secure.GLANCEABLE_HUB_START_CHARGING -> WhenToStartHub.WHILE_CHARGING
+ Settings.Secure.GLANCEABLE_HUB_START_CHARGING_UPRIGHT ->
+ WhenToStartHub.WHILE_CHARGING_AND_POSTURED
+ Settings.Secure.GLANCEABLE_HUB_START_DOCKED -> WhenToStartHub.WHILE_DOCKED
+ else -> WhenToStartHub.NEVER
+ }
+ }
+ .flowOn(bgDispatcher)
+
override fun getAllowedByDevicePolicy(user: UserInfo): Flow<Boolean> =
broadcastDispatcher
.broadcastFlow(
diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalAutoOpenInteractor.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalAutoOpenInteractor.kt
index 51df3338a18e..678a5e2f6a1c 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalAutoOpenInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalAutoOpenInteractor.kt
@@ -22,7 +22,7 @@ import com.android.systemui.communal.data.model.FEATURE_AUTO_OPEN
import com.android.systemui.communal.data.model.FEATURE_MANUAL_OPEN
import com.android.systemui.communal.data.model.SuppressionReason
import com.android.systemui.communal.posturing.domain.interactor.PosturingInteractor
-import com.android.systemui.communal.shared.model.WhenToDream
+import com.android.systemui.communal.shared.model.WhenToStartHub
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dock.DockManager
@@ -49,17 +49,25 @@ constructor(
@Named(SWIPE_TO_HUB) private val allowSwipeAlways: Boolean,
) {
val shouldAutoOpen: Flow<Boolean> =
- communalSettingsInteractor.whenToDream
- .flatMapLatestConflated { whenToDream ->
- when (whenToDream) {
- WhenToDream.WHILE_CHARGING -> batteryInteractor.isDevicePluggedIn
- WhenToDream.WHILE_DOCKED -> {
+ communalSettingsInteractor.whenToStartHub
+ .flatMapLatestConflated { whenToStartHub ->
+ when (whenToStartHub) {
+ WhenToStartHub.WHILE_CHARGING -> batteryInteractor.isDevicePluggedIn
+ WhenToStartHub.WHILE_DOCKED -> {
allOf(batteryInteractor.isDevicePluggedIn, dockManager.retrieveIsDocked())
}
- WhenToDream.WHILE_POSTURED -> {
- allOf(batteryInteractor.isDevicePluggedIn, posturingInteractor.postured)
+ WhenToStartHub.WHILE_CHARGING_AND_POSTURED -> {
+ // Only listen to posturing if applicable to avoid running the posturing
+ // CHRE nanoapp when not needed.
+ batteryInteractor.isDevicePluggedIn.flatMapLatestConflated { isCharging ->
+ if (isCharging) {
+ posturingInteractor.postured
+ } else {
+ flowOf(false)
+ }
+ }
}
- WhenToDream.NEVER -> flowOf(false)
+ WhenToStartHub.NEVER -> flowOf(false)
}
}
.flowOn(backgroundContext)
diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSettingsInteractor.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSettingsInteractor.kt
index 0d7a2d9707d7..cf51fa1b61fb 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSettingsInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSettingsInteractor.kt
@@ -17,7 +17,6 @@
package com.android.systemui.communal.domain.interactor
import android.content.pm.UserInfo
-import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
import com.android.systemui.communal.data.model.FEATURE_AUTO_OPEN
import com.android.systemui.communal.data.model.FEATURE_ENABLED
import com.android.systemui.communal.data.model.FEATURE_MANUAL_OPEN
@@ -25,10 +24,12 @@ import com.android.systemui.communal.data.model.SuppressionReason
import com.android.systemui.communal.data.repository.CommunalSettingsRepository
import com.android.systemui.communal.shared.model.CommunalBackgroundType
import com.android.systemui.communal.shared.model.WhenToDream
+import com.android.systemui.communal.shared.model.WhenToStartHub
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.settings.UserTracker
import com.android.systemui.user.domain.interactor.SelectedUserInteractor
+import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
import com.android.systemui.utils.coroutines.flow.flatMapLatestConflated
import java.util.concurrent.Executor
import javax.inject.Inject
@@ -79,6 +80,12 @@ constructor(
repository.getWhenToDreamState(user)
}
+ /** When to automatically start hub for the currently selected user. */
+ val whenToStartHub: Flow<WhenToStartHub> =
+ userInteractor.selectedUserInfo.flatMapLatest { user ->
+ repository.getWhenToStartHubState(user)
+ }
+
/** Whether communal hub is allowed by device policy for the current user */
val allowedForCurrentUserByDevicePolicy: Flow<Boolean> =
userInteractor.selectedUserInfo.flatMapLatestConflated { user ->
diff --git a/packages/SystemUI/src/com/android/systemui/communal/posturing/data/model/PositionState.kt b/packages/SystemUI/src/com/android/systemui/communal/posturing/data/model/PositionState.kt
new file mode 100644
index 000000000000..21b8dd785f53
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/posturing/data/model/PositionState.kt
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2025 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.communal.posturing.data.model
+
+import androidx.annotation.FloatRange
+
+data class PositionState(
+ val stationary: StationaryState = StationaryState.Unknown,
+ val orientation: OrientationState = OrientationState.Unknown,
+) {
+ sealed interface StationaryState {
+ @get:FloatRange(from = 0.0, to = 1.0) val confidence: Float
+
+ data object Unknown : StationaryState {
+ override val confidence: Float = 0f
+ }
+
+ data class Stationary(override val confidence: Float) : StationaryState
+
+ data class NotStationary(override val confidence: Float) : StationaryState
+ }
+
+ sealed interface OrientationState {
+ @get:FloatRange(from = 0.0, to = 1.0) val confidence: Float
+
+ data object Unknown : OrientationState {
+ override val confidence: Float = 0f
+ }
+
+ data class Postured(override val confidence: Float) : OrientationState
+
+ data class NotPostured(override val confidence: Float) : OrientationState
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/posturing/data/repository/NoOpPosturingRepository.kt b/packages/SystemUI/src/com/android/systemui/communal/posturing/data/repository/NoOpPosturingRepository.kt
index c5f357f556ca..d826685886d9 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/posturing/data/repository/NoOpPosturingRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/posturing/data/repository/NoOpPosturingRepository.kt
@@ -16,7 +16,7 @@
package com.android.systemui.communal.posturing.data.repository
-import com.android.systemui.communal.posturing.shared.model.PosturedState
+import com.android.systemui.communal.posturing.data.model.PositionState
import com.android.systemui.dagger.SysUISingleton
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
@@ -25,6 +25,6 @@ import kotlinx.coroutines.flow.asStateFlow
@SysUISingleton
class NoOpPosturingRepository @Inject constructor() : PosturingRepository {
- override val posturedState: Flow<PosturedState> =
- MutableStateFlow(PosturedState.Unknown).asStateFlow()
+ override val positionState: Flow<PositionState> =
+ MutableStateFlow(PositionState()).asStateFlow()
}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/posturing/data/repository/PosturingRepository.kt b/packages/SystemUI/src/com/android/systemui/communal/posturing/data/repository/PosturingRepository.kt
index dae1a47f5be0..4de0a1e21d35 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/posturing/data/repository/PosturingRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/posturing/data/repository/PosturingRepository.kt
@@ -16,7 +16,7 @@
package com.android.systemui.communal.posturing.data.repository
-import com.android.systemui.communal.posturing.shared.model.PosturedState
+import com.android.systemui.communal.posturing.data.model.PositionState
import kotlinx.coroutines.flow.Flow
/**
@@ -25,5 +25,5 @@ import kotlinx.coroutines.flow.Flow
*/
interface PosturingRepository {
/** Whether the device is currently stationary and upright. */
- val posturedState: Flow<PosturedState>
+ val positionState: Flow<PositionState>
}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/posturing/domain/interactor/PosturingInteractor.kt b/packages/SystemUI/src/com/android/systemui/communal/posturing/domain/interactor/PosturingInteractor.kt
index cd81dea9cad1..e487590d87d7 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/posturing/domain/interactor/PosturingInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/posturing/domain/interactor/PosturingInteractor.kt
@@ -16,26 +16,211 @@
package com.android.systemui.communal.posturing.domain.interactor
+import android.annotation.SuppressLint
+import android.hardware.Sensor
+import android.hardware.TriggerEvent
+import android.hardware.TriggerEventListener
+import android.service.dreams.Flags.allowDreamWhenPostured
+import com.android.systemui.communal.posturing.data.model.PositionState
import com.android.systemui.communal.posturing.data.repository.PosturingRepository
import com.android.systemui.communal.posturing.shared.model.PosturedState
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.log.LogBuffer
+import com.android.systemui.log.core.Logger
+import com.android.systemui.log.dagger.CommunalLog
+import com.android.systemui.util.kotlin.BooleanFlowOperators.allOf
+import com.android.systemui.util.kotlin.slidingWindow
+import com.android.systemui.util.sensors.AsyncSensorManager
+import com.android.systemui.util.time.SystemClock
+import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
+import java.util.concurrent.atomic.AtomicBoolean
import javax.inject.Inject
+import kotlin.time.Duration.Companion.seconds
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.filterNot
+import kotlinx.coroutines.flow.flowOn
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.merge
+import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.flow.stateIn
@SysUISingleton
-class PosturingInteractor @Inject constructor(repository: PosturingRepository) {
- private val debugPostured = MutableStateFlow<PosturedState>(PosturedState.Unknown)
+class PosturingInteractor
+@Inject
+constructor(
+ repository: PosturingRepository,
+ private val asyncSensorManager: AsyncSensorManager,
+ @Application private val applicationScope: CoroutineScope,
+ @Background private val bgDispatcher: CoroutineDispatcher,
+ @CommunalLog private val logBuffer: LogBuffer,
+ clock: SystemClock,
+) {
+ private val logger = Logger(logBuffer, TAG)
- val postured: Flow<Boolean> =
- combine(repository.posturedState, debugPostured) { postured, debugValue ->
- debugValue.asBoolean() ?: postured.asBoolean() ?: false
- }
+ private val debugPostured = MutableStateFlow<PosturedState>(PosturedState.Unknown)
fun setValueForDebug(value: PosturedState) {
debugPostured.value = value
}
+
+ /**
+ * Detects whether or not the device is stationary, applying a sliding window smoothing
+ * algorithm.
+ */
+ private val stationarySmoothed: Flow<Boolean> =
+ merge(
+ observeTriggerSensor(Sensor.TYPE_PICK_UP_GESTURE)
+ // If pickup detected, avoid triggering posturing at all within the sliding
+ // window by emitting a negative infinity value.
+ .map { Float.NEGATIVE_INFINITY }
+ .onEach { logger.i("pickup gesture detected") },
+ observeTriggerSensor(Sensor.TYPE_SIGNIFICANT_MOTION)
+ // If motion detected, avoid triggering posturing at all within the sliding
+ // window by emitting a negative infinity value.
+ .map { Float.NEGATIVE_INFINITY }
+ .onEach { logger.i("significant motion detected") },
+ repository.positionState
+ .map { it.stationary }
+ .filterNot { it is PositionState.StationaryState.Unknown }
+ .map { stationaryState ->
+ if (stationaryState is PositionState.StationaryState.Stationary) {
+ stationaryState.confidence
+ } else {
+ // If not stationary, then we should effectively disable posturing by
+ // emitting the lowest possible confidence.
+ Float.NEGATIVE_INFINITY
+ }
+ },
+ )
+ .slidingWindow(SLIDING_WINDOW_DURATION, clock)
+ .filterNot { it.isEmpty() }
+ .map { window ->
+ val avgStationaryConfidence = window.average()
+ logger.i({ "stationary confidence: $double1 | window: $str1" }) {
+ str1 = window.formatWindowForDebugging()
+ double1 = avgStationaryConfidence
+ }
+ avgStationaryConfidence > CONFIDENCE_THRESHOLD
+ }
+
+ /**
+ * Detects whether or not the device is in an upright orientation, applying a sliding window
+ * smoothing algorithm.
+ */
+ private val orientationSmoothed: Flow<Boolean> =
+ repository.positionState
+ .map { it.orientation }
+ .filterNot { it is PositionState.OrientationState.Unknown }
+ .map { orientationState ->
+ if (orientationState is PositionState.OrientationState.Postured) {
+ orientationState.confidence
+ } else {
+ // If not postured, then we should effectively disable posturing by
+ // emitting the lowest possible confidence.
+ Float.NEGATIVE_INFINITY
+ }
+ }
+ .slidingWindow(SLIDING_WINDOW_DURATION, clock)
+ .filterNot { it.isEmpty() }
+ .map { window ->
+ val avgOrientationConfidence = window.average()
+ logger.i({ "orientation confidence: $double1 | window: $str1" }) {
+ str1 = window.formatWindowForDebugging()
+ double1 = avgOrientationConfidence
+ }
+ avgOrientationConfidence > CONFIDENCE_THRESHOLD
+ }
+
+ /**
+ * Posturing is composed of the device being stationary and in the correct orientation. If both
+ * conditions are met, then consider it postured.
+ */
+ private val posturedSmoothed: Flow<PosturedState> =
+ allOf(stationarySmoothed, orientationSmoothed)
+ .map { postured ->
+ if (postured) {
+ PosturedState.Postured
+ } else {
+ PosturedState.NotPostured
+ }
+ }
+ .flowOn(bgDispatcher)
+ .stateIn(
+ scope = applicationScope,
+ // Avoid losing the smoothing history if the user plug/unplugs rapidly.
+ started =
+ SharingStarted.WhileSubscribed(
+ stopTimeoutMillis = STOP_TIMEOUT_AFTER_UNSUBSCRIBE.inWholeMilliseconds,
+ replayExpirationMillis = 0,
+ ),
+ initialValue = PosturedState.Unknown,
+ )
+
+ /**
+ * Whether the device is postured.
+ *
+ * NOTE: Due to smoothing, this signal may be delayed to ensure we have a stable reading before
+ * being considered postured.
+ */
+ val postured: Flow<Boolean> by lazy {
+ if (allowDreamWhenPostured()) {
+ combine(posturedSmoothed, debugPostured) { postured, debugValue ->
+ debugValue.asBoolean() ?: postured.asBoolean() ?: false
+ }
+ } else {
+ MutableStateFlow(false)
+ }
+ }
+
+ /**
+ * Helper for observing a trigger sensor, which automatically unregisters itself after it
+ * executes once.
+ */
+ private fun observeTriggerSensor(type: Int): Flow<Unit> = conflatedCallbackFlow {
+ val sensor = asyncSensorManager.getDefaultSensor(type)
+ val isRegistered = AtomicBoolean(false)
+
+ fun registerCallbackInternal(callback: TriggerEventListener) {
+ if (isRegistered.compareAndSet(false, true)) {
+ asyncSensorManager.requestTriggerSensor(callback, sensor)
+ }
+ }
+
+ val callback =
+ object : TriggerEventListener() {
+ override fun onTrigger(event: TriggerEvent) {
+ trySend(Unit)
+ if (isRegistered.getAndSet(false)) {
+ registerCallbackInternal(this)
+ }
+ }
+ }
+
+ if (sensor != null) {
+ registerCallbackInternal(callback)
+ }
+
+ awaitClose {
+ if (isRegistered.getAndSet(false)) {
+ asyncSensorManager.cancelTriggerSensor(callback, sensor)
+ }
+ }
+ }
+
+ companion object {
+ const val TAG = "PosturingInteractor"
+ val SLIDING_WINDOW_DURATION = 10.seconds
+ const val CONFIDENCE_THRESHOLD = 0.8f
+ val STOP_TIMEOUT_AFTER_UNSUBSCRIBE = 5.seconds
+ }
}
fun PosturedState.asBoolean(): Boolean? {
@@ -45,3 +230,8 @@ fun PosturedState.asBoolean(): Boolean? {
PosturedState.Unknown -> null
}
}
+
+@SuppressLint("DefaultLocale")
+fun List<Float>.formatWindowForDebugging(): String {
+ return joinToString(prefix = "[", postfix = "]") { String.format("%.2f", it) }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/posturing/shared/model/PosturedState.kt b/packages/SystemUI/src/com/android/systemui/communal/posturing/shared/model/PosturedState.kt
index 431ca67315eb..c71cf14c4b52 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/posturing/shared/model/PosturedState.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/posturing/shared/model/PosturedState.kt
@@ -18,7 +18,7 @@ package com.android.systemui.communal.posturing.shared.model
sealed interface PosturedState {
/** Represents postured state */
- data class Postured(val confidence: Float) : PosturedState
+ data object Postured : PosturedState
/** Represents unknown/uninitialized state */
data object Unknown : PosturedState
diff --git a/packages/SystemUI/src/com/android/systemui/communal/shared/model/WhenToStartHub.kt b/packages/SystemUI/src/com/android/systemui/communal/shared/model/WhenToStartHub.kt
new file mode 100644
index 000000000000..be89fda01009
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/shared/model/WhenToStartHub.kt
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2025 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.communal.shared.model
+
+enum class WhenToStartHub {
+ NEVER,
+ WHILE_CHARGING,
+ WHILE_CHARGING_AND_POSTURED,
+ WHILE_DOCKED,
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalLockIconViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalLockIconViewModel.kt
index 19eeabd98c88..931639c8b247 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalLockIconViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalLockIconViewModel.kt
@@ -130,7 +130,9 @@ constructor(
if (SceneContainerFlag.isEnabled) {
deviceEntryInteractor.attemptDeviceEntry()
} else {
- keyguardViewController.get().showPrimaryBouncer(/* scrim */ true)
+ keyguardViewController
+ .get()
+ .showPrimaryBouncer(/* scrim */ true, "CommunalLockIconViewModel#onUserInteraction")
}
deviceEntrySourceInteractor.attemptEnterDeviceFromDeviceEntryIcon()
}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt
index 756edb3d048d..5a4b0b0e2d24 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt
@@ -229,7 +229,7 @@ constructor(
MutableStateFlow(false)
}
- val blurRadiusPx: Float = blurConfig.maxBlurRadiusPx / 2.0f
+ val blurRadiusPx: Float = blurConfig.maxBlurRadiusPx
init {
// Initialize our media host for the UMO. This only needs to happen once and must be done
diff --git a/packages/SystemUI/src/com/android/systemui/controls/dagger/ControlsModule.kt b/packages/SystemUI/src/com/android/systemui/controls/dagger/ControlsModule.kt
index 037b6facc50d..ba9f52b63d41 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/dagger/ControlsModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/dagger/ControlsModule.kt
@@ -51,8 +51,9 @@ import com.android.systemui.qs.pipeline.shared.TileSpec
import com.android.systemui.qs.shared.model.TileCategory
import com.android.systemui.qs.tileimpl.QSTileImpl
import com.android.systemui.qs.tiles.DeviceControlsTile
-import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
-import com.android.systemui.qs.tiles.viewmodel.QSTileUIConfig
+import com.android.systemui.qs.tiles.base.shared.model.QSTileConfig
+import com.android.systemui.qs.tiles.base.shared.model.QSTileUIConfig
+import com.android.systemui.res.R
import dagger.Binds
import dagger.BindsOptionalOf
import dagger.Module
@@ -91,8 +92,8 @@ abstract class ControlsModule {
tileSpec = TileSpec.create(DEVICE_CONTROLS_SPEC),
uiConfig =
QSTileUIConfig.Resource(
- iconRes = com.android.systemui.res.R.drawable.controls_icon,
- labelRes = com.android.systemui.res.R.string.quick_controls_title
+ iconRes = R.drawable.controls_icon,
+ labelRes = R.string.quick_controls_title,
),
instanceId = uiEventLogger.getNewInstanceId(),
category = TileCategory.UTILITIES,
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java b/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java
index 7354f4096801..f45bafdfb17e 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java
@@ -16,9 +16,6 @@
package com.android.systemui.dagger;
-import static com.android.systemui.Flags.enableViewCaptureTracing;
-import static com.android.systemui.util.ConvenienceExtensionsKt.toKotlinLazy;
-
import android.annotation.Nullable;
import android.annotation.SuppressLint;
import android.app.ActivityManager;
@@ -103,7 +100,6 @@ import android.telephony.CarrierConfigManager;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.telephony.satellite.SatelliteManager;
-import android.view.Choreographer;
import android.view.CrossWindowBlurListeners;
import android.view.IWindowManager;
import android.view.LayoutInflater;
@@ -115,13 +111,9 @@ import android.view.accessibility.CaptioningManager;
import android.view.inputmethod.InputMethodManager;
import android.view.textclassifier.TextClassificationManager;
-import androidx.annotation.NonNull;
import androidx.asynclayoutinflater.view.AsyncLayoutInflater;
import androidx.core.app.NotificationManagerCompat;
-import com.android.app.viewcapture.ViewCapture;
-import com.android.app.viewcapture.ViewCaptureAwareWindowManager;
-import com.android.app.viewcapture.ViewCaptureFactory;
import com.android.internal.app.IBatteryStats;
import com.android.internal.appwidget.IAppWidgetService;
import com.android.internal.jank.InteractionJankMonitor;
@@ -135,8 +127,9 @@ import com.android.systemui.dagger.qualifiers.TestHarness;
import com.android.systemui.shared.system.PackageManagerWrapper;
import com.android.systemui.user.utils.UserScopedService;
import com.android.systemui.user.utils.UserScopedServiceImpl;
+import com.android.systemui.utils.windowmanager.WindowManagerProvider;
+import com.android.systemui.utils.windowmanager.WindowManagerProviderImpl;
-import dagger.Lazy;
import dagger.Module;
import dagger.Provides;
@@ -713,38 +706,23 @@ public class FrameworkServicesModule {
@Provides
@Singleton
- static WindowManager provideWindowManager(Context context) {
- return context.getSystemService(WindowManager.class);
- }
-
- /** A window manager working for the default display only. */
- @Provides
- @Singleton
- @Main
- static WindowManager provideMainWindowManager(WindowManager windowManager) {
- return windowManager;
+ static WindowManagerProvider provideWindowManagerProvider() {
+ return new WindowManagerProviderImpl();
}
@Provides
@Singleton
- static ViewCaptureAwareWindowManager provideViewCaptureAwareWindowManager(
- WindowManager windowManager, Lazy<ViewCapture> daggerLazyViewCapture) {
- return new ViewCaptureAwareWindowManager(windowManager,
- /* lazyViewCapture= */ toKotlinLazy(daggerLazyViewCapture),
- /* isViewCaptureEnabled= */ enableViewCaptureTracing());
+ static WindowManager provideWindowManager(Context context,
+ WindowManagerProvider windowManagerProvider) {
+ return windowManagerProvider.getWindowManager(context);
}
+ /** A window manager working for the default display only. */
@Provides
@Singleton
- static ViewCaptureAwareWindowManager.Factory viewCaptureAwareWindowManagerFactory(
- Lazy<ViewCapture> daggerLazyViewCapture) {
- return new ViewCaptureAwareWindowManager.Factory() {
- @NonNull
- @Override
- public ViewCaptureAwareWindowManager create(@NonNull WindowManager windowManager) {
- return provideViewCaptureAwareWindowManager(windowManager, daggerLazyViewCapture);
- }
- };
+ @Main
+ static WindowManager provideMainWindowManager(WindowManager windowManager) {
+ return windowManager;
}
@Provides
@@ -835,12 +813,6 @@ public class FrameworkServicesModule {
@Provides
@Singleton
- static ViewCapture provideViewCapture(Context context) {
- return ViewCaptureFactory.getInstance(context);
- }
-
- @Provides
- @Singleton
@Nullable
static SupervisionManager provideSupervisionManager(Context context) {
return (SupervisionManager) context.getSystemService(Context.SUPERVISION_SERVICE);
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/PerDisplayRepositoriesModule.kt b/packages/SystemUI/src/com/android/systemui/dagger/PerDisplayRepositoriesModule.kt
index 39708a743c23..a8f21885907b 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/PerDisplayRepositoriesModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/dagger/PerDisplayRepositoriesModule.kt
@@ -16,9 +16,10 @@
package com.android.systemui.dagger
-import com.android.systemui.display.data.repository.DefaultDisplayOnlyInstanceRepositoryImpl
-import com.android.systemui.display.data.repository.PerDisplayInstanceRepositoryImpl
-import com.android.systemui.display.data.repository.PerDisplayRepository
+import com.android.app.displaylib.DefaultDisplayOnlyInstanceRepositoryImpl
+import com.android.app.displaylib.PerDisplayInstanceRepositoryImpl
+import com.android.app.displaylib.PerDisplayRepository
+import com.android.systemui.display.data.repository.PerDisplayCoroutineScopeRepositoryModule
import com.android.systemui.model.SysUIStateInstanceProvider
import com.android.systemui.model.SysUiState
import com.android.systemui.shade.shared.flag.ShadeWindowGoesAround
@@ -26,7 +27,7 @@ import dagger.Module
import dagger.Provides
/** This module is meant to contain all the code to create the various [PerDisplayRepository<>]. */
-@Module
+@Module(includes = [PerDisplayCoroutineScopeRepositoryModule::class])
class PerDisplayRepositoriesModule {
@SysUISingleton
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
index f08126af0a7a..edee64e50b53 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
@@ -65,7 +65,7 @@ import com.android.systemui.dagger.qualifiers.UiBackground;
import com.android.systemui.demomode.dagger.DemoModeModule;
import com.android.systemui.deviceentry.DeviceEntryModule;
import com.android.systemui.display.DisplayModule;
-import com.android.systemui.display.data.repository.PerDisplayRepository;
+import com.android.app.displaylib.PerDisplayRepository;
import com.android.systemui.doze.dagger.DozeComponent;
import com.android.systemui.dreams.dagger.DreamModule;
import com.android.systemui.flags.FeatureFlags;
@@ -97,11 +97,11 @@ import com.android.systemui.plugins.BcSmartspaceConfigPlugin;
import com.android.systemui.plugins.BcSmartspaceDataPlugin;
import com.android.systemui.privacy.PrivacyModule;
import com.android.systemui.process.condition.SystemProcessCondition;
-import com.android.systemui.qrcodescanner.dagger.QRCodeScannerModule;
import com.android.systemui.qs.FgsManagerController;
import com.android.systemui.qs.FgsManagerControllerImpl;
import com.android.systemui.qs.QSFragmentStartableModule;
import com.android.systemui.qs.footer.dagger.FooterActionsModule;
+import com.android.systemui.qs.tiles.impl.qr.ui.model.QRCodeScannerModule;
import com.android.systemui.recents.Recents;
import com.android.systemui.recordissue.RecordIssueModule;
import com.android.systemui.retail.RetailModeModule;
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/ui/view/UdfpsAccessibilityOverlay.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/ui/view/UdfpsAccessibilityOverlay.kt
index 7be323073692..9c3b9b273ab5 100644
--- a/packages/SystemUI/src/com/android/systemui/deviceentry/ui/view/UdfpsAccessibilityOverlay.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/ui/view/UdfpsAccessibilityOverlay.kt
@@ -20,4 +20,8 @@ import android.content.Context
import android.view.View
/** Overlay to handle under-fingerprint sensor accessibility events. */
-class UdfpsAccessibilityOverlay(context: Context?) : View(context)
+class UdfpsAccessibilityOverlay(context: Context?) : View(context) {
+ init {
+ accessibilityLiveRegion = ACCESSIBILITY_LIVE_REGION_ASSERTIVE
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/ui/viewmodel/UdfpsAccessibilityOverlayViewModel.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/ui/viewmodel/UdfpsAccessibilityOverlayViewModel.kt
index fa849bf5e413..1849bf20abdb 100644
--- a/packages/SystemUI/src/com/android/systemui/deviceentry/ui/viewmodel/UdfpsAccessibilityOverlayViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/ui/viewmodel/UdfpsAccessibilityOverlayViewModel.kt
@@ -56,7 +56,7 @@ abstract class UdfpsAccessibilityOverlayViewModel(
event.getPointerId(0),
event,
overlayParams, /* rotateToPortrait */
- false
+ false,
)
if (
@@ -64,7 +64,7 @@ abstract class UdfpsAccessibilityOverlayViewModel(
event.getPointerId(0),
event,
overlayParams,
- /* rotateTouchToPortrait */ false
+ /* rotateTouchToPortrait */ false,
)
) {
// view only receives motionEvents when [visible] which requires touchExplorationEnabled
@@ -75,10 +75,10 @@ abstract class UdfpsAccessibilityOverlayViewModel(
scaledTouch.x,
scaledTouch.y,
overlayParams,
- /* touchRotatedToPortrait */ false
+ /* touchRotatedToPortrait */ false,
)
if (announceStr != null) {
- v.announceForAccessibility(announceStr)
+ v.contentDescription = announceStr
}
}
// always let the motion events go through to underlying views
diff --git a/packages/SystemUI/src/com/android/systemui/display/DisplayModule.kt b/packages/SystemUI/src/com/android/systemui/display/DisplayModule.kt
index f3316958f01d..32b69e5423ba 100644
--- a/packages/SystemUI/src/com/android/systemui/display/DisplayModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/display/DisplayModule.kt
@@ -18,7 +18,9 @@ package com.android.systemui.display
import android.hardware.display.DisplayManager
import android.os.Handler
+import com.android.app.displaylib.DisplayLibBackground
import com.android.app.displaylib.DisplayLibComponent
+import com.android.app.displaylib.PerDisplayRepository
import com.android.app.displaylib.createDisplayLibComponent
import com.android.systemui.CoreStartable
import com.android.systemui.dagger.SysUISingleton
@@ -27,14 +29,13 @@ import com.android.systemui.display.data.repository.DeviceStateRepository
import com.android.systemui.display.data.repository.DeviceStateRepositoryImpl
import com.android.systemui.display.data.repository.DisplayRepository
import com.android.systemui.display.data.repository.DisplayRepositoryImpl
-import com.android.systemui.display.data.repository.DisplayScopeRepository
-import com.android.systemui.display.data.repository.DisplayScopeRepositoryImpl
import com.android.systemui.display.data.repository.DisplayWindowPropertiesRepository
import com.android.systemui.display.data.repository.DisplayWindowPropertiesRepositoryImpl
+import com.android.systemui.display.data.repository.DisplaysWithDecorationsRepository
+import com.android.systemui.display.data.repository.DisplaysWithDecorationsRepositoryImpl
import com.android.systemui.display.data.repository.FocusedDisplayRepository
import com.android.systemui.display.data.repository.FocusedDisplayRepositoryImpl
import com.android.systemui.display.data.repository.PerDisplayRepoDumpHelper
-import com.android.systemui.display.data.repository.PerDisplayRepository
import com.android.systemui.display.domain.interactor.ConnectedDisplayInteractor
import com.android.systemui.display.domain.interactor.ConnectedDisplayInteractorImpl
import com.android.systemui.display.domain.interactor.DisplayWindowPropertiesInteractorModule
@@ -75,31 +76,24 @@ interface DisplayModule {
focusedDisplayRepository: FocusedDisplayRepositoryImpl
): FocusedDisplayRepository
- @Binds fun displayScopeRepository(impl: DisplayScopeRepositoryImpl): DisplayScopeRepository
-
@Binds
fun displayWindowPropertiesRepository(
impl: DisplayWindowPropertiesRepositoryImpl
): DisplayWindowPropertiesRepository
@Binds
+ fun displaysWithDecorationsRepository(
+ impl: DisplaysWithDecorationsRepositoryImpl
+ ): DisplaysWithDecorationsRepository
+
+ @Binds
fun dumpRegistrationLambda(helper: PerDisplayRepoDumpHelper): PerDisplayRepository.InitCallback
- companion object {
- @Provides
- @SysUISingleton
- @IntoMap
- @ClassKey(DisplayScopeRepositoryImpl::class)
- fun displayScopeRepoCoreStartable(
- repoImplLazy: Lazy<DisplayScopeRepositoryImpl>
- ): CoreStartable {
- return if (StatusBarConnectedDisplays.isEnabled) {
- repoImplLazy.get()
- } else {
- CoreStartable.NOP
- }
- }
+ @Binds
+ @DisplayLibBackground
+ fun bindDisplayLibBackground(@Background bgScope: CoroutineScope): CoroutineScope
+ companion object {
@Provides
@SysUISingleton
@IntoMap
diff --git a/packages/SystemUI/src/com/android/systemui/display/dagger/PerDisplayCommonModule.kt b/packages/SystemUI/src/com/android/systemui/display/dagger/PerDisplayCommonModule.kt
new file mode 100644
index 000000000000..6fb3727db1f4
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/display/dagger/PerDisplayCommonModule.kt
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2025 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.display.dagger
+
+import android.content.Context
+import android.view.Display
+import com.android.app.displaylib.DisplayRepository
+import com.android.systemui.coroutines.newTracingContext
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.display.dagger.SystemUIDisplaySubcomponent.DisplayAware
+import com.android.systemui.display.dagger.SystemUIDisplaySubcomponent.DisplayId
+import com.android.systemui.display.dagger.SystemUIDisplaySubcomponent.PerDisplaySingleton
+import dagger.Module
+import dagger.Provides
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.CoroutineScope
+
+/** Module providing common dependencies for per-display singletons. */
+@Module
+class PerDisplayCommonModule {
+
+ @Provides
+ @PerDisplaySingleton
+ fun provideDisplay(@DisplayId displayId: Int, displayRepository: DisplayRepository): Display {
+ return displayRepository.getDisplay(displayId)
+ ?: error("Couldn't get the display with id=$displayId")
+ }
+
+ @Provides
+ @PerDisplaySingleton
+ @DisplayAware
+ fun provideDisplayContext(
+ display: Display,
+ @Application context: Context,
+ ): Context {
+ return context.createDisplayContext(display)
+ }
+
+ @Provides
+ @PerDisplaySingleton
+ @DisplayAware
+ fun provideDisplayCoroutineScope(
+ @Background backgroundDispatcher: CoroutineDispatcher,
+ @DisplayId displayId: Int,
+ ): CoroutineScope {
+ return CoroutineScope(
+ backgroundDispatcher + newTracingContext("DisplayScope(id=$displayId)")
+ )
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/display/dagger/SystemUIDisplaySubcomponent.kt b/packages/SystemUI/src/com/android/systemui/display/dagger/SystemUIDisplaySubcomponent.kt
new file mode 100644
index 000000000000..f878aa1ea87b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/display/dagger/SystemUIDisplaySubcomponent.kt
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2025 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.display.dagger
+
+import dagger.BindsInstance
+import dagger.Subcomponent
+import javax.inject.Qualifier
+import javax.inject.Scope
+import kotlinx.coroutines.CoroutineScope
+
+/**
+ * Subcomponent for SysUI classes that should be instantiated once per display.
+ *
+ * All display specific classes should be provided with the @DisplayAware annotation. Once the
+ * display is removed, [displayCoroutineScope] gets cancelled. This means that if classes have some
+ * teardown step it should be executed when the scope is cancelled. Also note that the scope is
+ * cancelled in the background, so any teardown logic should be threadsafe. Cancelling on the main
+ * thread is not feasible as it would cause jank.
+ */
+@Subcomponent(modules = [PerDisplayCommonModule::class])
+interface SystemUIDisplaySubcomponent {
+
+ @DisplayAware val displayCoroutineScope: CoroutineScope
+
+ @Subcomponent.Factory
+ interface Factory {
+ fun create(@BindsInstance @DisplayId displayId: Int): SystemUIDisplaySubcomponent
+ }
+
+ /** Scope annotation for singletons associated to a display. */
+ @MustBeDocumented
+ @Retention(AnnotationRetention.RUNTIME)
+ @Scope
+ annotation class PerDisplaySingleton
+
+ /** Qualifier used to represent that the object is provided/bound with the proper display. */
+ @Qualifier @Retention(AnnotationRetention.RUNTIME) annotation class DisplayAware
+
+ /** Annotates the display id inside the subcomponent. */
+ @Qualifier @Retention(AnnotationRetention.RUNTIME) annotation class DisplayId
+}
diff --git a/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayComponentRepository.kt b/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayComponentRepository.kt
new file mode 100644
index 000000000000..a2d3b35740ea
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayComponentRepository.kt
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2025 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.display.data.repository
+
+import com.android.app.displaylib.PerDisplayInstanceProviderWithTeardown
+import com.android.app.displaylib.PerDisplayInstanceRepositoryImpl
+import com.android.app.displaylib.PerDisplayRepository
+import com.android.app.tracing.traceSection
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.display.dagger.SystemUIDisplaySubcomponent
+import dagger.Module
+import dagger.Provides
+import javax.inject.Inject
+import kotlinx.coroutines.cancel
+
+@SysUISingleton
+class DisplayComponentInstanceProvider
+@Inject
+constructor(private val componentFactory: SystemUIDisplaySubcomponent.Factory) :
+ PerDisplayInstanceProviderWithTeardown<SystemUIDisplaySubcomponent> {
+ override fun createInstance(displayId: Int): SystemUIDisplaySubcomponent? =
+ runCatching { componentFactory.create(displayId) }.getOrNull()
+
+ override fun destroyInstance(instance: SystemUIDisplaySubcomponent) {
+ traceSection("Destroying a display component instance") {
+ instance.displayCoroutineScope.cancel("Cancelling scope associated to the display.")
+ }
+ }
+}
+
+@Module
+object DisplayComponentRepository {
+ @SysUISingleton
+ @Provides
+ fun provideDisplayComponentRepository(
+ repositoryFactory: PerDisplayInstanceRepositoryImpl.Factory<SystemUIDisplaySubcomponent>,
+ instanceProvider: DisplayComponentInstanceProvider,
+ ): PerDisplayRepository<SystemUIDisplaySubcomponent> {
+ return repositoryFactory.create(
+ debugName = "DisplayComponentInstanceProvider",
+ instanceProvider,
+ )
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayRepository.kt b/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayRepository.kt
index 051fe7e5517c..01bbf2d57dd6 100644
--- a/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayRepository.kt
@@ -16,95 +16,25 @@
package com.android.systemui.display.data.repository
-import android.annotation.SuppressLint
-import android.view.IWindowManager
import com.android.app.displaylib.DisplayRepository as DisplayRepositoryFromLib
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.dagger.qualifiers.Background
-import com.android.systemui.statusbar.CommandQueue
import javax.inject.Inject
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.channels.awaitClose
-import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.SharingStarted
-import kotlinx.coroutines.flow.StateFlow
-import kotlinx.coroutines.flow.callbackFlow
-import kotlinx.coroutines.flow.distinctUntilChanged
-import kotlinx.coroutines.flow.filter
-import kotlinx.coroutines.flow.map
-import kotlinx.coroutines.flow.merge
-import kotlinx.coroutines.flow.scan
-import kotlinx.coroutines.flow.stateIn
-/** Repository for providing access to display related information and events. */
-interface DisplayRepository : DisplayRepositoryFromLib {
-
- /** A [StateFlow] that maintains a set of display IDs that should have system decorations. */
- val displayIdsWithSystemDecorations: StateFlow<Set<Int>>
-}
+/**
+ * Repository for providing access to display related information and events.
+ *
+ * This is now just an interface that extends [DisplayRepositoryFromLib] to avoid changing all the
+ * imports in sysui using this interface.
+ */
+interface DisplayRepository : DisplayRepositoryFromLib, DisplaysWithDecorationsRepository
@SysUISingleton
-@SuppressLint("SharedFlowCreation")
class DisplayRepositoryImpl
@Inject
constructor(
- private val commandQueue: CommandQueue,
- private val windowManager: IWindowManager,
- @Background bgApplicationScope: CoroutineScope,
private val displayRepositoryFromLib: com.android.app.displaylib.DisplayRepository,
-) : DisplayRepositoryFromLib by displayRepositoryFromLib, DisplayRepository {
-
- private val decorationEvents: Flow<Event> = callbackFlow {
- val callback =
- object : CommandQueue.Callbacks {
- override fun onDisplayAddSystemDecorations(displayId: Int) {
- trySend(Event.Add(displayId))
- }
-
- override fun onDisplayRemoveSystemDecorations(displayId: Int) {
- trySend(Event.Remove(displayId))
- }
- }
- commandQueue.addCallback(callback)
- awaitClose { commandQueue.removeCallback(callback) }
- }
-
- private val initialDisplayIdsWithDecorations: Set<Int> =
- displayIds.value.filter { windowManager.shouldShowSystemDecors(it) }.toSet()
-
- /**
- * A [StateFlow] that maintains a set of display IDs that should have system decorations.
- *
- * Updates to the set are triggered by:
- * - Adding displays via [CommandQueue.Callbacks.onDisplayAddSystemDecorations].
- * - Removing displays via [CommandQueue.Callbacks.onDisplayRemoveSystemDecorations].
- * - Removing displays via [displayRemovalEvent] emissions.
- *
- * The set is initialized with displays that qualify for system decorations based on
- * [WindowManager.shouldShowSystemDecors].
- */
- override val displayIdsWithSystemDecorations: StateFlow<Set<Int>> =
- merge(decorationEvents, displayRemovalEvent.map { Event.Remove(it) })
- .scan(initialDisplayIdsWithDecorations) { displayIds: Set<Int>, event: Event ->
- when (event) {
- is Event.Add -> displayIds + event.displayId
- is Event.Remove -> displayIds - event.displayId
- }
- }
- .distinctUntilChanged()
- .stateIn(
- scope = bgApplicationScope,
- started = SharingStarted.WhileSubscribed(),
- initialValue = initialDisplayIdsWithDecorations,
- )
-
- private sealed class Event(val displayId: Int) {
- class Add(displayId: Int) : Event(displayId)
-
- class Remove(displayId: Int) : Event(displayId)
- }
-
- private companion object {
- const val TAG = "DisplayRepository"
- }
-}
+ private val displaysWithDecorationsRepositoryImpl: DisplaysWithDecorationsRepository,
+) :
+ DisplayRepositoryFromLib by displayRepositoryFromLib,
+ DisplaysWithDecorationsRepository by displaysWithDecorationsRepositoryImpl,
+ DisplayRepository
diff --git a/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayScopeRepository.kt b/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayScopeRepository.kt
index d912b6a13d0f..ac59b16bfe3c 100644
--- a/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayScopeRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayScopeRepository.kt
@@ -17,60 +17,58 @@
package com.android.systemui.display.data.repository
import android.view.Display
-import com.android.systemui.CoreStartable
+import com.android.app.displaylib.PerDisplayInstanceProviderWithTeardown
+import com.android.app.displaylib.PerDisplayInstanceRepositoryImpl
+import com.android.app.displaylib.PerDisplayRepository
import com.android.systemui.coroutines.newTracingContext
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
-import com.android.systemui.statusbar.core.StatusBarConnectedDisplays
-import java.util.concurrent.ConcurrentHashMap
+import dagger.Module
+import dagger.Provides
import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.cancel
-import com.android.app.tracing.coroutines.launchTraced as launch
/**
- * Provides per display instances of [CoroutineScope]. These will remain active as long as the
- * display is connected, and automatically cancelled when the display is removed.
+ * Provides per display instances of [CoroutineScope].
+ *
+ * This is used to create a [PerDisplayRepository] of [CoroutineScope]
*/
-interface DisplayScopeRepository {
- fun scopeForDisplay(displayId: Int): CoroutineScope
-}
-
@SysUISingleton
-class DisplayScopeRepositoryImpl
+class DisplayScopeRepositoryInstanceProvider
@Inject
constructor(
@Background private val backgroundApplicationScope: CoroutineScope,
@Background private val backgroundDispatcher: CoroutineDispatcher,
- private val displayRepository: DisplayRepository,
-) : DisplayScopeRepository, CoreStartable {
-
- private val perDisplayScopes = ConcurrentHashMap<Int, CoroutineScope>()
-
- override fun scopeForDisplay(displayId: Int): CoroutineScope {
- return perDisplayScopes.computeIfAbsent(displayId) { createScopeForDisplay(displayId) }
- }
-
- override fun start() {
- StatusBarConnectedDisplays.unsafeAssertInNewMode()
- backgroundApplicationScope.launch {
- displayRepository.displayRemovalEvent.collect { displayId ->
- val scope = perDisplayScopes.remove(displayId)
- scope?.cancel("Display $displayId has been removed.")
- }
- }
- }
+) : PerDisplayInstanceProviderWithTeardown<CoroutineScope> {
- private fun createScopeForDisplay(displayId: Int): CoroutineScope {
+ override fun createInstance(displayId: Int): CoroutineScope {
return if (displayId == Display.DEFAULT_DISPLAY) {
// The default display is connected all the time, therefore we can optimise by reusing
// the application scope, and don't need to create a new scope.
backgroundApplicationScope
} else {
- CoroutineScope(
- backgroundDispatcher + newTracingContext("DisplayScope$displayId")
- )
+ CoroutineScope(backgroundDispatcher + newTracingContext("DisplayScope$displayId"))
}
}
+
+ override fun destroyInstance(instance: CoroutineScope) {
+ instance.cancel("DisplayContext has been cancelled.")
+ }
+}
+
+@Module
+object PerDisplayCoroutineScopeRepositoryModule {
+ @SysUISingleton
+ @Provides
+ fun provideDisplayCoroutineScopeRepository(
+ repositoryFactory: PerDisplayInstanceRepositoryImpl.Factory<CoroutineScope>,
+ instanceProvider: DisplayScopeRepositoryInstanceProvider,
+ ): PerDisplayRepository<CoroutineScope> {
+ return repositoryFactory.create(
+ debugName = "CoroutineScopePerDisplayRepo",
+ instanceProvider,
+ )
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayWindowPropertiesRepository.kt b/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayWindowPropertiesRepository.kt
index 3390640fa6c6..aaaaacef001a 100644
--- a/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayWindowPropertiesRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayWindowPropertiesRepository.kt
@@ -31,6 +31,7 @@ import com.android.systemui.display.shared.model.DisplayWindowProperties
import com.android.systemui.res.R
import com.android.systemui.shade.shared.flag.ShadeWindowGoesAround
import com.android.systemui.statusbar.core.StatusBarConnectedDisplays
+import com.android.systemui.utils.windowmanager.WindowManagerUtils
import com.google.common.collect.HashBasedTable
import com.google.common.collect.Table
import java.io.PrintWriter
@@ -110,7 +111,7 @@ constructor(
return null
}
@SuppressLint("NonInjectedService") // Need to manually get the service
- val windowManager = context.getSystemService(WindowManager::class.java) as WindowManager
+ val windowManager = WindowManagerUtils.getWindowManager(context)
val layoutInflater = LayoutInflater.from(context)
DisplayWindowProperties(displayId, windowType, context, windowManager, layoutInflater)
}
diff --git a/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplaysWithDecorationsRepository.kt b/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplaysWithDecorationsRepository.kt
new file mode 100644
index 000000000000..f4a2ed48f295
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplaysWithDecorationsRepository.kt
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2025 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.display.data.repository
+
+import android.view.IWindowManager
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.statusbar.CommandQueue
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.callbackFlow
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.merge
+import kotlinx.coroutines.flow.scan
+import kotlinx.coroutines.flow.stateIn
+
+/** Provides the displays with decorations. */
+interface DisplaysWithDecorationsRepository {
+ /** A [StateFlow] that maintains a set of display IDs that should have system decorations. */
+ val displayIdsWithSystemDecorations: StateFlow<Set<Int>>
+}
+
+@SysUISingleton
+class DisplaysWithDecorationsRepositoryImpl
+@Inject
+constructor(
+ private val commandQueue: CommandQueue,
+ private val windowManager: IWindowManager,
+ @Background bgApplicationScope: CoroutineScope,
+ displayRepository: com.android.app.displaylib.DisplayRepository,
+) : DisplaysWithDecorationsRepository {
+
+ private val decorationEvents: Flow<Event> = callbackFlow {
+ val callback =
+ object : CommandQueue.Callbacks {
+ override fun onDisplayAddSystemDecorations(displayId: Int) {
+ trySend(Event.Add(displayId))
+ }
+
+ override fun onDisplayRemoveSystemDecorations(displayId: Int) {
+ trySend(Event.Remove(displayId))
+ }
+ }
+ commandQueue.addCallback(callback)
+ awaitClose { commandQueue.removeCallback(callback) }
+ }
+
+ private val initialDisplayIdsWithDecorations: Set<Int> =
+ displayRepository.displayIds.value
+ .filter { windowManager.shouldShowSystemDecors(it) }
+ .toSet()
+
+ /**
+ * A [StateFlow] that maintains a set of display IDs that should have system decorations.
+ *
+ * Updates to the set are triggered by:
+ * - Adding displays via [CommandQueue.Callbacks.onDisplayAddSystemDecorations].
+ * - Removing displays via [CommandQueue.Callbacks.onDisplayRemoveSystemDecorations].
+ * - Removing displays via [displayRemovalEvent] emissions.
+ *
+ * The set is initialized with displays that qualify for system decorations based on
+ * [WindowManager.shouldShowSystemDecors].
+ */
+ override val displayIdsWithSystemDecorations: StateFlow<Set<Int>> =
+ merge(decorationEvents, displayRepository.displayRemovalEvent.map { Event.Remove(it) })
+ .scan(initialDisplayIdsWithDecorations) { displayIds: Set<Int>, event: Event ->
+ when (event) {
+ is Event.Add -> displayIds + event.displayId
+ is Event.Remove -> displayIds - event.displayId
+ }
+ }
+ .distinctUntilChanged()
+ .stateIn(
+ scope = bgApplicationScope,
+ started = SharingStarted.WhileSubscribed(),
+ initialValue = initialDisplayIdsWithDecorations,
+ )
+
+ private sealed class Event(val displayId: Int) {
+ class Add(displayId: Int) : Event(displayId)
+
+ class Remove(displayId: Int) : Event(displayId)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/display/data/repository/FakePerDisplayRepository.kt b/packages/SystemUI/src/com/android/systemui/display/data/repository/FakePerDisplayRepository.kt
index a56710ee3772..86c9d84c27b1 100644
--- a/packages/SystemUI/src/com/android/systemui/display/data/repository/FakePerDisplayRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/display/data/repository/FakePerDisplayRepository.kt
@@ -16,6 +16,9 @@
package com.android.systemui.display.data.repository
+import com.android.app.displaylib.PerDisplayRepository
+
+// TODO b/401305290 - move to displaylib
class FakePerDisplayRepository<T> : PerDisplayRepository<T> {
private val instances = mutableMapOf<Int, T>()
diff --git a/packages/SystemUI/src/com/android/systemui/display/data/repository/PerDisplayRepoDumpHelper.kt b/packages/SystemUI/src/com/android/systemui/display/data/repository/PerDisplayRepoDumpHelper.kt
index 212d55612935..efbae5d04caf 100644
--- a/packages/SystemUI/src/com/android/systemui/display/data/repository/PerDisplayRepoDumpHelper.kt
+++ b/packages/SystemUI/src/com/android/systemui/display/data/repository/PerDisplayRepoDumpHelper.kt
@@ -16,6 +16,7 @@
package com.android.systemui.display.data.repository
+import com.android.app.displaylib.PerDisplayRepository
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dump.DumpManager
import com.android.systemui.dump.DumpableFromToString
diff --git a/packages/SystemUI/src/com/android/systemui/display/data/repository/PerDisplayRepository.kt b/packages/SystemUI/src/com/android/systemui/display/data/repository/PerDisplayRepository.kt
deleted file mode 100644
index 7e00c60dc43a..000000000000
--- a/packages/SystemUI/src/com/android/systemui/display/data/repository/PerDisplayRepository.kt
+++ /dev/null
@@ -1,223 +0,0 @@
-/*
- * Copyright (C) 2025 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.display.data.repository
-
-import android.util.Log
-import android.view.Display
-import com.android.app.tracing.coroutines.launchTraced as launch
-import com.android.app.tracing.traceSection
-import com.android.systemui.dagger.qualifiers.Background
-import dagger.assisted.Assisted
-import dagger.assisted.AssistedFactory
-import dagger.assisted.AssistedInject
-import java.util.concurrent.ConcurrentHashMap
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.flow.collectLatest
-
-/**
- * Used to create instances of type `T` for a specific display.
- *
- * This is useful for resources or objects that need to be managed independently for each connected
- * display (e.g., UI state, rendering contexts, or display-specific configurations).
- *
- * Note that in most cases this can be implemented by a simple `@AssistedFactory` with `displayId`
- * parameter
- *
- * ```kotlin
- * class SomeType @AssistedInject constructor(@Assisted displayId: Int,..)
- * @AssistedFactory
- * interface Factory {
- * fun create(displayId: Int): SomeType
- * }
- * }
- * ```
- *
- * Then it can be used to create a [PerDisplayRepository] as follows:
- * ```kotlin
- * // Injected:
- * val repositoryFactory: PerDisplayRepositoryImpl.Factory
- * val instanceFactory: PerDisplayRepositoryImpl.Factory
- * // repository creation:
- * repositoryFactory.create(instanceFactory::create)
- * ```
- *
- * @see PerDisplayRepository For how to retrieve and manage instances created by this factory.
- */
-fun interface PerDisplayInstanceProvider<T> {
- /** Creates an instance for a display. */
- fun createInstance(displayId: Int): T?
-}
-
-/**
- * Extends [PerDisplayInstanceProvider], adding support for destroying the instance.
- *
- * This is useful for releasing resources associated with a display when it is disconnected or when
- * the per-display instance is no longer needed.
- */
-interface PerDisplayInstanceProviderWithTeardown<T> : PerDisplayInstanceProvider<T> {
- /** Destroys a previously created instance of `T` forever. */
- fun destroyInstance(instance: T)
-}
-
-/**
- * Provides access to per-display instances of type `T`.
- *
- * Acts as a repository, managing the caching and retrieval of instances created by a
- * [PerDisplayInstanceProvider]. It ensures that only one instance of `T` exists per display ID.
- */
-interface PerDisplayRepository<T> {
- /** Gets the cached instance or create a new one for a given display. */
- operator fun get(displayId: Int): T?
-
- /** Debug name for this repository, mainly for tracing and logging. */
- val debugName: String
-
- /**
- * Callback to run when a given repository is initialized.
- *
- * This allows the caller to perform custom logic when the repository is ready to be used, e.g.
- * register to dumpManager.
- *
- * Note that the instance is *leaked* outside of this class, so it should only be done when
- * repository is meant to live as long as the caller. In systemUI this is ok because the
- * repository lives as long as the process itself.
- */
- interface InitCallback {
- fun onInit(debugName: String, instance: Any)
- }
-}
-
-/**
- * Default implementation of [PerDisplayRepository].
- *
- * This class manages a cache of per-display instances of type `T`, creating them using a provided
- * [PerDisplayInstanceProvider] and optionally tearing them down using a
- * [PerDisplayInstanceProviderWithTeardown] when displays are disconnected.
- *
- * It listens to the [DisplayRepository] to detect when displays are added or removed, and
- * automatically manages the lifecycle of the per-display instances.
- *
- * Note that this is a [PerDisplayStoreImpl] 2.0 that doesn't require [CoreStartable] bindings,
- * providing all args in the constructor.
- */
-class PerDisplayInstanceRepositoryImpl<T>
-@AssistedInject
-constructor(
- @Assisted override val debugName: String,
- @Assisted private val instanceProvider: PerDisplayInstanceProvider<T>,
- @Background private val backgroundApplicationScope: CoroutineScope,
- private val displayRepository: DisplayRepository,
- private val initCallback: PerDisplayRepository.InitCallback,
-) : PerDisplayRepository<T> {
-
- private val perDisplayInstances = ConcurrentHashMap<Int, T?>()
-
- init {
- backgroundApplicationScope.launch("$debugName#start") { start() }
- }
-
- private suspend fun start() {
- initCallback.onInit(debugName, this)
- displayRepository.displayIds.collectLatest { displayIds ->
- val toRemove = perDisplayInstances.keys - displayIds
- toRemove.forEach { displayId ->
- Log.d(TAG, "<$debugName> destroying instance for displayId=$displayId.")
- perDisplayInstances.remove(displayId)?.let { instance ->
- (instanceProvider as? PerDisplayInstanceProviderWithTeardown)?.destroyInstance(
- instance
- )
- }
- }
- }
- }
-
- override fun get(displayId: Int): T? {
- if (displayRepository.getDisplay(displayId) == null) {
- Log.e(TAG, "<$debugName: Display with id $displayId doesn't exist.")
- return null
- }
-
- // If it doesn't exist, create it and put it in the map.
- return perDisplayInstances.computeIfAbsent(displayId) { key ->
- Log.d(TAG, "<$debugName> creating instance for displayId=$key, as it wasn't available.")
- val instance =
- traceSection({ "creating instance of $debugName for displayId=$key" }) {
- instanceProvider.createInstance(key)
- }
- if (instance == null) {
- Log.e(
- TAG,
- "<$debugName> returning null because createInstance($key) returned null.",
- )
- }
- instance
- }
- }
-
- @AssistedFactory
- interface Factory<T> {
- fun create(
- debugName: String,
- instanceProvider: PerDisplayInstanceProvider<T>,
- ): PerDisplayInstanceRepositoryImpl<T>
- }
-
- companion object {
- private const val TAG = "PerDisplayInstanceRepo"
- }
-
- override fun toString(): String {
- return "PerDisplayInstanceRepositoryImpl(" +
- "debugName='$debugName', instances=$perDisplayInstances)"
- }
-}
-
-/**
- * Provides an instance of a given class **only** for the default display, even if asked for another
- * display.
- *
- * This is useful in case of **flag refactors**: it can be provided instead of an instance of
- * [PerDisplayInstanceRepositoryImpl] when a flag related to multi display refactoring is off.
- *
- * Note that this still requires all instances to be provided by a [PerDisplayInstanceProvider]. If
- * you want to provide an existing instance instead for the default display, either implement it in
- * a custom [PerDisplayInstanceProvider] (e.g. inject it in the constructor and return it if the
- * displayId is zero), or use [SingleInstanceRepositoryImpl].
- */
-class DefaultDisplayOnlyInstanceRepositoryImpl<T>(
- override val debugName: String,
- private val instanceProvider: PerDisplayInstanceProvider<T>,
-) : PerDisplayRepository<T> {
- private val lazyDefaultDisplayInstance by lazy {
- instanceProvider.createInstance(Display.DEFAULT_DISPLAY)
- }
-
- override fun get(displayId: Int): T? = lazyDefaultDisplayInstance
-}
-
-/**
- * Always returns [instance] for any display.
- *
- * This can be used to provide a single instance based on a flag value during a refactor. Similar to
- * [DefaultDisplayOnlyInstanceRepositoryImpl], but also avoids creating the
- * [PerDisplayInstanceProvider]. This is useful when you want to provide an existing instance only,
- * without even instantiating a [PerDisplayInstanceProvider].
- */
-class SingleInstanceRepositoryImpl<T>(override val debugName: String, private val instance: T) :
- PerDisplayRepository<T> {
- override fun get(displayId: Int): T? = instance
-}
diff --git a/packages/SystemUI/src/com/android/systemui/display/data/repository/PerDisplayStore.kt b/packages/SystemUI/src/com/android/systemui/display/data/repository/PerDisplayStore.kt
index 46048868f503..8756c8813df6 100644
--- a/packages/SystemUI/src/com/android/systemui/display/data/repository/PerDisplayStore.kt
+++ b/packages/SystemUI/src/com/android/systemui/display/data/repository/PerDisplayStore.kt
@@ -68,7 +68,7 @@ abstract class PerDisplayStoreImpl<T>(
* displays.
*/
override fun forDisplay(displayId: Int): T? {
- if (displayRepository.getDisplay(displayId) == null) {
+ if (displayRepository.getDisplay(displayId) == null) {
Log.e(TAG, "<${instanceClass.simpleName}>: Display with id $displayId doesn't exist.")
return null
}
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java b/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java
index 9def81a89e3a..2b16e198b1b1 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java
@@ -18,7 +18,6 @@ package com.android.systemui.doze;
import static com.android.systemui.doze.DozeMachine.State.DOZE;
import static com.android.systemui.doze.DozeMachine.State.DOZE_AOD_PAUSED;
-import static com.android.systemui.Flags.dozeuiSchedulingAlarmsBackgroundExecution;
import android.app.AlarmManager;
import android.content.Context;
@@ -84,13 +83,7 @@ public class DozeUi implements DozeMachine.Part {
mBgExecutor = bgExecutor;
mCanAnimateTransition = !params.getDisplayNeedsBlanking();
mDozeParameters = params;
- if (dozeuiSchedulingAlarmsBackgroundExecution()) {
- mTimeTicker = new AlarmTimeout(alarmManager, this::onTimeTick, "doze_time_tick",
- bgHandler);
- } else {
- mTimeTicker = new AlarmTimeout(alarmManager, this::onTimeTick, "doze_time_tick",
- handler);
- }
+ mTimeTicker = new AlarmTimeout(alarmManager, this::onTimeTick, "doze_time_tick", bgHandler);
mDozeLog = dozeLog;
}
@@ -184,7 +177,7 @@ public class DozeUi implements DozeMachine.Part {
mTimeTickScheduled = true;
long time = System.currentTimeMillis();
- long delta = roundToNextMinute(time) - System.currentTimeMillis();
+ long delta = roundToNextMinute(time) - time;
boolean scheduled = mTimeTicker.schedule(delta, AlarmTimeout.MODE_RESCHEDULE_IF_SCHEDULED);
if (scheduled) {
mDozeLog.traceTimeTickScheduled(time, time + delta);
@@ -224,14 +217,8 @@ public class DozeUi implements DozeMachine.Part {
private void onTimeTick() {
verifyLastTimeTick();
- if (dozeuiSchedulingAlarmsBackgroundExecution()) {
- mHandler.post(mHost::dozeTimeTick);
- } else {
- mHost.dozeTimeTick();
- }
-
// Keep wakelock until a frame has been pushed.
- mHandler.post(mWakeLock.wrap(() -> {}));
+ mHandler.post(mWakeLock.wrap(mHost::dozeTimeTick));
mTimeTickScheduled = false;
scheduleTimeTick();
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java
index a2bcb98e36fe..fd716eea799a 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java
@@ -46,7 +46,6 @@ import androidx.lifecycle.LifecycleService;
import androidx.lifecycle.ServiceLifecycleDispatcher;
import androidx.lifecycle.ViewModelStore;
-import com.android.app.viewcapture.ViewCaptureAwareWindowManager;
import com.android.compose.animation.scene.OverlayKey;
import com.android.internal.logging.UiEvent;
import com.android.internal.logging.UiEventLogger;
@@ -117,7 +116,7 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ
@Nullable
private final ComponentName mHomeControlPanelDreamComponent;
private final UiEventLogger mUiEventLogger;
- private final ViewCaptureAwareWindowManager mWindowManager;
+ private final WindowManager mWindowManager;
private final String mWindowTitle;
// A reference to the {@link Window} used to hold the dream overlay.
@@ -378,7 +377,7 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ
Context context,
DreamOverlayLifecycleOwner lifecycleOwner,
@Main DelayableExecutor executor,
- ViewCaptureAwareWindowManager viewCaptureAwareWindowManager,
+ WindowManager windowManager,
ComplicationComponent.Factory complicationComponentFactory,
DreamComplicationComponent.Factory dreamComplicationComponentFactory,
DreamOverlayComponent.Factory dreamOverlayComponentFactory,
@@ -403,7 +402,7 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ
super(executor);
mContext = context;
mExecutor = executor;
- mWindowManager = viewCaptureAwareWindowManager;
+ mWindowManager = windowManager;
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mScrimManager = scrimManager;
mLowLightDreamComponent = lowLightDreamComponent;
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java
index 15f73ee0eda6..a56f07e04d21 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java
@@ -42,9 +42,9 @@ import com.android.systemui.dreams.homecontrols.system.HomeControlsRemoteService
import com.android.systemui.qs.QsEventLogger;
import com.android.systemui.qs.pipeline.shared.TileSpec;
import com.android.systemui.qs.shared.model.TileCategory;
-import com.android.systemui.qs.tiles.viewmodel.QSTileConfig;
-import com.android.systemui.qs.tiles.viewmodel.QSTilePolicy;
-import com.android.systemui.qs.tiles.viewmodel.QSTileUIConfig;
+import com.android.systemui.qs.tiles.base.shared.model.QSTileConfig;
+import com.android.systemui.qs.tiles.base.shared.model.QSTilePolicy;
+import com.android.systemui.qs.tiles.base.shared.model.QSTileUIConfig;
import com.android.systemui.res.R;
import com.android.systemui.touch.TouchInsetManager;
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/docking/binder/KeyboardDockingIndicationViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyboard/docking/binder/KeyboardDockingIndicationViewBinder.kt
index c2974a8d5429..c08a8e297174 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/docking/binder/KeyboardDockingIndicationViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/docking/binder/KeyboardDockingIndicationViewBinder.kt
@@ -20,7 +20,6 @@ import android.content.Context
import android.graphics.Paint
import android.graphics.PixelFormat
import android.view.WindowManager
-import com.android.app.viewcapture.ViewCaptureAwareWindowManager
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.keyboard.docking.ui.KeyboardDockingIndicationView
@@ -38,7 +37,7 @@ constructor(
context: Context,
@Application private val applicationScope: CoroutineScope,
private val viewModel: KeyboardDockingIndicationViewModel,
- private val windowManager: ViewCaptureAwareWindowManager,
+ private val windowManager: WindowManager
) {
private val windowLayoutParams =
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutHelperCustomizationModeRepository.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutHelperCustomizationModeRepository.kt
new file mode 100644
index 000000000000..2f16b9f6ed72
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutHelperCustomizationModeRepository.kt
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2025 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.keyboard.shortcut.data.repository
+
+import com.android.systemui.keyboard.shortcut.shared.model.ShortcutHelperState
+import javax.inject.Inject
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.combine
+
+class ShortcutHelperCustomizationModeRepository
+@Inject
+constructor(shortcutHelperStateRepository: ShortcutHelperStateRepository) {
+ private val _isCustomizationModeEnabled = MutableStateFlow(false)
+ val isCustomizationModeEnabled =
+ combine(_isCustomizationModeEnabled, shortcutHelperStateRepository.state) {
+ isCustomizationModeEnabled,
+ shortcutHelperState ->
+ isCustomizationModeEnabled && shortcutHelperState is ShortcutHelperState.Active
+ }
+
+ fun toggleCustomizationMode(isCustomizing: Boolean) {
+ _isCustomizationModeEnabled.value = isCustomizing
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutHelperCustomizationModeInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutHelperCustomizationModeInteractor.kt
new file mode 100644
index 000000000000..2cec991080b8
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutHelperCustomizationModeInteractor.kt
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2025 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.keyboard.shortcut.domain.interactor
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyboard.shortcut.data.repository.ShortcutHelperCustomizationModeRepository
+import javax.inject.Inject
+
+@SysUISingleton
+class ShortcutHelperCustomizationModeInteractor
+@Inject
+constructor(private val customizationModeRepository: ShortcutHelperCustomizationModeRepository) {
+ val customizationMode = customizationModeRepository.isCustomizationModeEnabled
+
+ fun toggleCustomizationMode(isCustomizing: Boolean) {
+ customizationModeRepository.toggleCustomizationMode(isCustomizing)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/ShortcutHelperDialogStarter.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/ShortcutHelperDialogStarter.kt
index ea36a10fb01a..81efaac06d26 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/ShortcutHelperDialogStarter.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/ShortcutHelperDialogStarter.kt
@@ -85,9 +85,12 @@ constructor(
shortcutsUiState = shortcutsUiState,
onKeyboardSettingsClicked = { onKeyboardSettingsClicked(dialog) },
onSearchQueryChanged = { shortcutHelperViewModel.onSearchQueryChanged(it) },
- onCustomizationRequested = {
+ onShortcutCustomizationRequested = {
shortcutCustomizationDialogStarter.onShortcutCustomizationRequested(it)
},
+ onCustomizationModeToggled = { isCustomizing ->
+ shortcutHelperViewModel.toggleCustomizationMode(isCustomizing)
+ },
)
dialog.setOnDismissListener { shortcutHelperViewModel.onViewClosed() }
dialog.setTitle(stringResource(R.string.shortcut_helper_title))
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/ShortcutHelper.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/ShortcutHelper.kt
index b0e554540371..72b984e226e2 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/ShortcutHelper.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/ShortcutHelper.kt
@@ -138,7 +138,8 @@ fun ShortcutHelper(
modifier: Modifier = Modifier,
shortcutsUiState: ShortcutsUiState,
useSinglePane: @Composable () -> Boolean = { shouldUseSinglePane() },
- onCustomizationRequested: (ShortcutCustomizationRequestInfo) -> Unit = {},
+ onShortcutCustomizationRequested: (ShortcutCustomizationRequestInfo) -> Unit = {},
+ onCustomizationModeToggled: (Boolean) -> Unit = {},
) {
when (shortcutsUiState) {
is ShortcutsUiState.Active -> {
@@ -146,9 +147,10 @@ fun ShortcutHelper(
shortcutsUiState,
useSinglePane,
onSearchQueryChanged,
+ onCustomizationModeToggled,
modifier,
onKeyboardSettingsClicked,
- onCustomizationRequested,
+ onShortcutCustomizationRequested,
)
}
@@ -163,9 +165,10 @@ private fun ActiveShortcutHelper(
shortcutsUiState: ShortcutsUiState.Active,
useSinglePane: @Composable () -> Boolean,
onSearchQueryChanged: (String) -> Unit,
+ onCustomizationModeToggled: (Boolean) -> Unit,
modifier: Modifier,
onKeyboardSettingsClicked: () -> Unit,
- onCustomizationRequested: (ShortcutCustomizationRequestInfo) -> Unit = {},
+ onShortcutCustomizationRequested: (ShortcutCustomizationRequestInfo) -> Unit = {},
) {
var selectedCategoryType by
remember(shortcutsUiState.defaultSelectedCategory) {
@@ -185,14 +188,16 @@ private fun ActiveShortcutHelper(
ShortcutHelperTwoPane(
shortcutsUiState.searchQuery,
onSearchQueryChanged,
- modifier,
shortcutsUiState.shortcutCategories,
selectedCategoryType,
onCategorySelected = { selectedCategoryType = it },
onKeyboardSettingsClicked,
shortcutsUiState.isShortcutCustomizerFlagEnabled,
- onCustomizationRequested,
shortcutsUiState.shouldShowResetButton,
+ shortcutsUiState.isCustomizationModeEnabled,
+ onCustomizationModeToggled,
+ modifier,
+ onShortcutCustomizationRequested,
)
}
}
@@ -376,17 +381,18 @@ private fun ShortcutSubCategorySinglePane(searchQuery: String, subCategory: Shor
private fun ShortcutHelperTwoPane(
searchQuery: String,
onSearchQueryChanged: (String) -> Unit,
- modifier: Modifier = Modifier,
categories: List<ShortcutCategoryUi>,
selectedCategoryType: ShortcutCategoryType?,
onCategorySelected: (ShortcutCategoryType?) -> Unit,
onKeyboardSettingsClicked: () -> Unit,
isShortcutCustomizerFlagEnabled: Boolean,
- onCustomizationRequested: (ShortcutCustomizationRequestInfo) -> Unit = {},
shouldShowResetButton: Boolean,
+ isCustomizationModeEnabled: Boolean,
+ onCustomizationModeToggled: (isCustomizing: Boolean) -> Unit,
+ modifier: Modifier = Modifier,
+ onShortcutCustomizationRequested: (ShortcutCustomizationRequestInfo) -> Unit = {},
) {
val selectedCategory = categories.fastFirstOrNull { it.type == selectedCategoryType }
- var isCustomizing by remember { mutableStateOf(false) }
Column(modifier = modifier.fillMaxSize().padding(horizontal = 24.dp)) {
Row(
@@ -397,14 +403,18 @@ private fun ShortcutHelperTwoPane(
// Keep title centered whether customize button is visible or not.
Spacer(modifier = Modifier.weight(1f))
Box(modifier = Modifier.width(412.dp), contentAlignment = Alignment.Center) {
- TitleBar(isCustomizing)
+ TitleBar(isCustomizationModeEnabled)
}
if (isShortcutCustomizerFlagEnabled) {
CustomizationButtonsContainer(
modifier = Modifier.weight(1f),
- isCustomizing = isCustomizing,
- onToggleCustomizationMode = { isCustomizing = !isCustomizing },
- onReset = { onCustomizationRequested(ShortcutCustomizationRequestInfo.Reset) },
+ isCustomizing = isCustomizationModeEnabled,
+ onToggleCustomizationMode = {
+ onCustomizationModeToggled(!isCustomizationModeEnabled)
+ },
+ onReset = {
+ onShortcutCustomizationRequested(ShortcutCustomizationRequestInfo.Reset)
+ },
shouldShowResetButton = shouldShowResetButton,
)
} else {
@@ -426,8 +436,8 @@ private fun ShortcutHelperTwoPane(
searchQuery,
Modifier.fillMaxSize().padding(top = 8.dp).semantics { isTraversalGroup = true },
selectedCategory,
- isCustomizing = isCustomizing,
- onCustomizationRequested = onCustomizationRequested,
+ isCustomizing = isCustomizationModeEnabled,
+ onShortcutCustomizationRequested = onShortcutCustomizationRequested,
)
}
}
@@ -496,7 +506,7 @@ private fun EndSidePanel(
modifier: Modifier,
category: ShortcutCategoryUi?,
isCustomizing: Boolean,
- onCustomizationRequested: (ShortcutCustomizationRequestInfo) -> Unit = {},
+ onShortcutCustomizationRequested: (ShortcutCustomizationRequestInfo) -> Unit = {},
) {
val listState = rememberLazyListState()
LaunchedEffect(key1 = category) { if (category != null) listState.animateScrollToItem(0) }
@@ -510,16 +520,20 @@ private fun EndSidePanel(
searchQuery = searchQuery,
subCategory = subcategory,
isCustomizing = isCustomizing and category.type.includeInCustomization,
- onCustomizationRequested = { requestInfo ->
+ onShortcutCustomizationRequested = { requestInfo ->
when (requestInfo) {
is ShortcutCustomizationRequestInfo.SingleShortcutCustomization.Add ->
- onCustomizationRequested(requestInfo.copy(categoryType = category.type))
+ onShortcutCustomizationRequested(
+ requestInfo.copy(categoryType = category.type)
+ )
is ShortcutCustomizationRequestInfo.SingleShortcutCustomization.Delete ->
- onCustomizationRequested(requestInfo.copy(categoryType = category.type))
+ onShortcutCustomizationRequested(
+ requestInfo.copy(categoryType = category.type)
+ )
ShortcutCustomizationRequestInfo.Reset ->
- onCustomizationRequested(requestInfo)
+ onShortcutCustomizationRequested(requestInfo)
}
},
)
@@ -551,7 +565,7 @@ private fun SubCategoryContainerDualPane(
searchQuery: String,
subCategory: ShortcutSubCategory,
isCustomizing: Boolean,
- onCustomizationRequested: (ShortcutCustomizationRequestInfo) -> Unit,
+ onShortcutCustomizationRequested: (ShortcutCustomizationRequestInfo) -> Unit,
) {
Surface(
modifier = Modifier.fillMaxWidth(),
@@ -573,20 +587,20 @@ private fun SubCategoryContainerDualPane(
searchQuery = searchQuery,
shortcut = shortcut,
isCustomizing = isCustomizing && shortcut.isCustomizable,
- onCustomizationRequested = { requestInfo ->
+ onShortcutCustomizationRequested = { requestInfo ->
when (requestInfo) {
is ShortcutCustomizationRequestInfo.SingleShortcutCustomization.Add ->
- onCustomizationRequested(
+ onShortcutCustomizationRequested(
requestInfo.copy(subCategoryLabel = subCategory.label)
)
is ShortcutCustomizationRequestInfo.SingleShortcutCustomization.Delete ->
- onCustomizationRequested(
+ onShortcutCustomizationRequested(
requestInfo.copy(subCategoryLabel = subCategory.label)
)
ShortcutCustomizationRequestInfo.Reset ->
- onCustomizationRequested(requestInfo)
+ onShortcutCustomizationRequested(requestInfo)
}
},
)
@@ -610,7 +624,7 @@ private fun Shortcut(
searchQuery: String,
shortcut: ShortcutModel,
isCustomizing: Boolean = false,
- onCustomizationRequested: (ShortcutCustomizationRequestInfo) -> Unit = {},
+ onShortcutCustomizationRequested: (ShortcutCustomizationRequestInfo) -> Unit = {},
) {
val interactionSource = remember { MutableInteractionSource() }
val isFocused by interactionSource.collectIsFocusedAsState()
@@ -650,7 +664,7 @@ private fun Shortcut(
shortcut = shortcut,
isCustomizing = isCustomizing,
onAddShortcutRequested = {
- onCustomizationRequested(
+ onShortcutCustomizationRequested(
ShortcutCustomizationRequestInfo.SingleShortcutCustomization.Add(
label = shortcut.label,
shortcutCommand = shortcut.commands.first(),
@@ -658,7 +672,7 @@ private fun Shortcut(
)
},
onDeleteShortcutRequested = {
- onCustomizationRequested(
+ onShortcutCustomizationRequested(
ShortcutCustomizationRequestInfo.SingleShortcutCustomization.Delete(
label = shortcut.label,
shortcutCommand = shortcut.commands.first(),
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/model/ShortcutsUiState.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/model/ShortcutsUiState.kt
index 52ab157a0e70..b474ae6ad3f0 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/model/ShortcutsUiState.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/model/ShortcutsUiState.kt
@@ -26,6 +26,7 @@ sealed interface ShortcutsUiState {
val defaultSelectedCategory: ShortcutCategoryType?,
val isShortcutCustomizerFlagEnabled: Boolean = false,
val shouldShowResetButton: Boolean = false,
+ val isCustomizationModeEnabled: Boolean = false,
) : ShortcutsUiState
data object Inactive : ShortcutsUiState
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutHelperViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutHelperViewModel.kt
index f11205fd7246..5937308898a3 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutHelperViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutHelperViewModel.kt
@@ -21,7 +21,6 @@ import android.content.Context
import android.content.pm.PackageManager.NameNotFoundException
import android.util.Log
import androidx.compose.material.icons.Icons
-import androidx.compose.material.icons.filled.Accessibility
import androidx.compose.material.icons.filled.AccessibilityNew
import androidx.compose.material.icons.filled.Android
import androidx.compose.material.icons.filled.Apps
@@ -32,6 +31,7 @@ import com.android.compose.ui.graphics.painter.DrawablePainter
import com.android.systemui.Flags.keyboardShortcutHelperShortcutCustomizer
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.keyboard.shortcut.domain.interactor.ShortcutHelperCategoriesInteractor
+import com.android.systemui.keyboard.shortcut.domain.interactor.ShortcutHelperCustomizationModeInteractor
import com.android.systemui.keyboard.shortcut.domain.interactor.ShortcutHelperStateInteractor
import com.android.systemui.keyboard.shortcut.shared.model.Shortcut
import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategory
@@ -65,6 +65,7 @@ constructor(
@Background private val backgroundDispatcher: CoroutineDispatcher,
private val stateInteractor: ShortcutHelperStateInteractor,
categoriesInteractor: ShortcutHelperCategoriesInteractor,
+ private val customizationModeInteractor: ShortcutHelperCustomizationModeInteractor,
) {
private val searchQuery = MutableStateFlow("")
@@ -77,7 +78,11 @@ constructor(
.flowOn(backgroundDispatcher)
val shortcutsUiState =
- combine(searchQuery, categoriesInteractor.shortcutCategories) { query, categories ->
+ combine(
+ searchQuery,
+ categoriesInteractor.shortcutCategories,
+ customizationModeInteractor.customizationMode,
+ ) { query, categories, isCustomizationModeEnabled ->
if (categories.isEmpty()) {
ShortcutsUiState.Inactive
} else {
@@ -94,6 +99,7 @@ constructor(
isShortcutCustomizerFlagEnabled =
keyboardShortcutHelperShortcutCustomizer(),
shouldShowResetButton = shouldShowResetButton(shortcutCategoriesUi),
+ isCustomizationModeEnabled = isCustomizationModeEnabled,
)
}
}
@@ -243,6 +249,7 @@ constructor(
fun onViewClosed() {
stateInteractor.onViewClosed()
resetSearchQuery()
+ resetCustomizationMode()
}
fun onViewOpened() {
@@ -253,7 +260,15 @@ constructor(
searchQuery.value = query
}
+ fun toggleCustomizationMode(isCustomizing: Boolean) {
+ customizationModeInteractor.toggleCustomizationMode(isCustomizing)
+ }
+
private fun resetSearchQuery() {
searchQuery.value = ""
}
+
+ private fun resetCustomizationMode() {
+ customizationModeInteractor.toggleCustomizationMode(false)
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
index c61530c3dbcc..74cf7e4f7359 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
@@ -20,7 +20,6 @@ package com.android.systemui.keyguard
import android.content.Context
import com.android.internal.jank.InteractionJankMonitor
import com.android.systemui.CoreStartable
-import com.android.systemui.Flags.lightRevealMigration
import com.android.systemui.biometrics.ui.binder.DeviceEntryUnlockTrackerViewBinder
import com.android.systemui.common.ui.ConfigurationState
import com.android.systemui.dagger.SysUISingleton
@@ -46,6 +45,7 @@ import com.android.systemui.plugins.FalsingManager
import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.shade.ShadeDisplayAware
import com.android.systemui.shade.domain.interactor.ShadeInteractor
+import com.android.systemui.shared.Flags.ambientAod
import com.android.systemui.statusbar.KeyguardIndicationController
import com.android.systemui.statusbar.LightRevealScrim
import com.android.systemui.statusbar.VibratorHelper
@@ -105,7 +105,7 @@ constructor(
bindJankViewModel()
initializeViews()
- if (lightRevealMigration()) {
+ if (ambientAod()) {
LightRevealScrimViewBinder.bind(
lightRevealScrim,
lightRevealScrimViewModel,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 4755e2845587..099a7f067482 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -140,6 +140,7 @@ import com.android.systemui.animation.TransitionAnimator;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.classifier.FalsingCollector;
import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor;
+import com.android.systemui.communal.domain.interactor.CommunalSettingsInteractor;
import com.android.systemui.communal.ui.viewmodel.CommunalTransitionViewModel;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dagger.qualifiers.UiBackground;
@@ -364,6 +365,7 @@ public class KeyguardViewMediator implements CoreStartable,
private final Lazy<NotificationShadeDepthController> mNotificationShadeDepthController;
private final Lazy<ShadeController> mShadeController;
private final Lazy<CommunalSceneInteractor> mCommunalSceneInteractor;
+ private final Lazy<CommunalSettingsInteractor> mCommunalSettingsInteractor;
/*
* Records the user id on request to go away, for validation when WM calls back to start the
* exit animation.
@@ -1567,6 +1569,7 @@ public class KeyguardViewMediator implements CoreStartable,
KeyguardInteractor keyguardInteractor,
KeyguardTransitionBootInteractor transitionBootInteractor,
Lazy<CommunalSceneInteractor> communalSceneInteractor,
+ Lazy<CommunalSettingsInteractor> communalSettingsInteractor,
WindowManagerOcclusionManager wmOcclusionManager) {
mContext = context;
mUserTracker = userTracker;
@@ -1609,6 +1612,7 @@ public class KeyguardViewMediator implements CoreStartable,
mKeyguardInteractor = keyguardInteractor;
mTransitionBootInteractor = transitionBootInteractor;
mCommunalSceneInteractor = communalSceneInteractor;
+ mCommunalSettingsInteractor = communalSettingsInteractor;
mStatusBarStateController = statusBarStateController;
statusBarStateController.addCallback(this);
@@ -1744,6 +1748,9 @@ public class KeyguardViewMediator implements CoreStartable,
mJavaAdapter.alwaysCollectFlow(
mWallpaperRepository.getWallpaperSupportsAmbientMode(),
this::setWallpaperSupportsAmbientMode);
+ mJavaAdapter.alwaysCollectFlow(
+ mKeyguardInteractor.getDozeTimeTick(),
+ this::triggerTimeUpdate);
}
@Override
@@ -2426,9 +2433,18 @@ public class KeyguardViewMediator implements CoreStartable,
private void doKeyguardLocked(Bundle options) {
// If the power button behavior requests to open the glanceable hub.
if (options != null && options.getBoolean(EXTRA_TRIGGER_HUB)) {
- // Set the hub to show immediately when the SysUI window shows, then continue to lock
- // the device.
- mCommunalSceneInteractor.get().showHubFromPowerButton();
+ if (mCommunalSettingsInteractor.get().getAutoOpenEnabled().getValue()) {
+ // Set the hub to show immediately when the SysUI window shows, then continue to
+ // lock the device.
+ mCommunalSceneInteractor.get().showHubFromPowerButton();
+ } else {
+ // If the hub is not available, go to sleep instead of locking. This can happen
+ // because the power button behavior does not check all possible reasons the hub
+ // might be disabled.
+ mPM.goToSleep(android.os.SystemClock.uptimeMillis(),
+ PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON, 0);
+ return;
+ }
}
int currentUserId = mSelectedUserInteractor.getSelectedUserId();
@@ -3762,13 +3778,7 @@ public class KeyguardViewMediator implements CoreStartable,
Log.d(TAG, "Status bar manager is disabled for visible background users");
}
} else {
- try {
- mStatusBarService.disableForUser(flags, mStatusBarDisableToken,
- mContext.getPackageName(),
- mSelectedUserInteractor.getSelectedUserId());
- } catch (RemoteException e) {
- Log.d(TAG, "Failed to force clear flags", e);
- }
+ statusBarServiceDisableForUser(flags, "Failed to force clear flags");
}
}
@@ -3804,18 +3814,29 @@ public class KeyguardViewMediator implements CoreStartable,
// Handled in StatusBarDisableFlagsInteractor.
if (!KeyguardWmStateRefactor.isEnabled()) {
- try {
- mStatusBarService.disableForUser(flags, mStatusBarDisableToken,
- mContext.getPackageName(),
- mSelectedUserInteractor.getSelectedUserId());
- } catch (RemoteException e) {
- Log.d(TAG, "Failed to set disable flags: " + flags, e);
- }
+ statusBarServiceDisableForUser(flags, "Failed to set disable flags: ");
}
}
}
}
+ private void statusBarServiceDisableForUser(int flags, String loggingContext) {
+ Runnable runnable = () -> {
+ try {
+ mStatusBarService.disableForUser(flags, mStatusBarDisableToken,
+ mContext.getPackageName(),
+ mSelectedUserInteractor.getSelectedUserId());
+ } catch (RemoteException e) {
+ Log.d(TAG, loggingContext + " " + flags, e);
+ }
+ };
+ if (com.android.systemui.Flags.bouncerUiRevamp()) {
+ mUiBgExecutor.execute(runnable);
+ } else {
+ runnable.run();
+ }
+ }
+
/**
* Handle message sent by {@link #resetStateLocked}
* @see #RESET
@@ -4056,6 +4077,10 @@ public class KeyguardViewMediator implements CoreStartable,
mWallpaperSupportsAmbientMode = supportsAmbientMode;
}
+ private void triggerTimeUpdate(long timeInMillis) {
+ mUpdateMonitor.triggerTimeUpdate();
+ }
+
private static class StartKeyguardExitAnimParams {
@WindowManager.TransitionOldType int mTransit;
@@ -4092,12 +4117,23 @@ public class KeyguardViewMediator implements CoreStartable,
|| aodShowing != mAodShowing || forceCallbacks;
mShowing = showing;
mAodShowing = aodShowing;
- if (notifyDefaultDisplayCallbacks) {
- notifyDefaultDisplayCallbacks(showing);
- }
- if (updateActivityLockScreenState) {
- updateActivityLockScreenState(showing, aodShowing, reason);
+
+ if (KeyguardWmReorderAtmsCalls.isEnabled()) {
+ if (updateActivityLockScreenState) {
+ updateActivityLockScreenState(showing, aodShowing, reason);
+ }
+ if (notifyDefaultDisplayCallbacks) {
+ notifyDefaultDisplayCallbacks(showing);
+ }
+ } else {
+ if (notifyDefaultDisplayCallbacks) {
+ notifyDefaultDisplayCallbacks(showing);
+ }
+ if (updateActivityLockScreenState) {
+ updateActivityLockScreenState(showing, aodShowing, reason);
+ }
}
+
}
private void notifyDefaultDisplayCallbacks(boolean showing) {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardWmReorderAtmsCalls.kt b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardWmReorderAtmsCalls.kt
new file mode 100644
index 000000000000..7ac52813ff71
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardWmReorderAtmsCalls.kt
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard
+
+import com.android.systemui.Flags
+import com.android.systemui.flags.FlagToken
+import com.android.systemui.flags.RefactorFlagUtils
+
+/** Helper for reading or using the keyguard wm state refactor flag state. */
+@Suppress("NOTHING_TO_INLINE")
+object KeyguardWmReorderAtmsCalls {
+ /** The aconfig flag name */
+ const val FLAG_NAME = Flags.FLAG_KEYGUARD_WM_REORDER_ATMS_CALLS
+
+ /** A token used for dependency declaration */
+ val token: FlagToken
+ get() = FlagToken(FLAG_NAME, isEnabled)
+
+ /** Is the refactor enabled */
+ @JvmStatic
+ inline val isEnabled
+ get() = Flags.keyguardWmReorderAtmsCalls()
+
+ /**
+ * Called to ensure code is only run when the flag is enabled. This protects users from the
+ * unintended behaviors caused by accidentally running new logic, while also crashing on an eng
+ * build to ensure that the refactor author catches issues in testing.
+ */
+ @JvmStatic
+ inline fun isUnexpectedlyInLegacyMode() =
+ RefactorFlagUtils.isUnexpectedlyInLegacyMode(isEnabled, FLAG_NAME)
+
+ /**
+ * Called to ensure code is only run when the flag is disabled. This will throw an exception if
+ * the flag is enabled to ensure that the refactor author catches issues in testing.
+ */
+ @JvmStatic
+ inline fun assertInLegacyMode() = RefactorFlagUtils.assertInLegacyMode(isEnabled, FLAG_NAME)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardWmStateRefactor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardWmStateRefactor.kt
index ddccc5d9e96d..41d14b9e727f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardWmStateRefactor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardWmStateRefactor.kt
@@ -20,7 +20,16 @@ import com.android.systemui.Flags
import com.android.systemui.flags.FlagToken
import com.android.systemui.flags.RefactorFlagUtils
-/** Helper for reading or using the keyguard wm state refactor flag state. */
+/**
+ * Helper for reading or using the keyguard_wm_state_refactor flag state.
+ *
+ * keyguard_wm_state_refactor works both with and without flexiglass (scene_container), but
+ * flexiglass requires keyguard_wm_state_refactor. For this reason, this class will return isEnabled
+ * if either keyguard_wm_state_refactor OR scene_container are enabled. This enables us to roll out
+ * keyguard_wm_state_refactor independently of scene_container, while also ensuring that
+ * scene_container rolling out ahead of keyguard_wm_state_refactor causes code gated by
+ * KeyguardWmStateRefactor to be enabled as well.
+ */
@Suppress("NOTHING_TO_INLINE")
object KeyguardWmStateRefactor {
/** The aconfig flag name */
@@ -30,10 +39,9 @@ object KeyguardWmStateRefactor {
val token: FlagToken
get() = FlagToken(FLAG_NAME, isEnabled)
- /** Is the refactor enabled */
@JvmStatic
inline val isEnabled
- get() = Flags.keyguardWmStateRefactor()
+ get() = Flags.keyguardWmStateRefactor() || Flags.sceneContainer()
/**
* Called to ensure code is only run when the flag is enabled. This protects users from the
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
index 6b1248b6983e..1fe6eb9ce7c8 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
@@ -42,6 +42,7 @@ import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.classifier.FalsingCollector;
import com.android.systemui.classifier.FalsingModule;
import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor;
+import com.android.systemui.communal.domain.interactor.CommunalSettingsInteractor;
import com.android.systemui.communal.ui.viewmodel.CommunalTransitionViewModel;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
@@ -182,6 +183,7 @@ public interface KeyguardModule {
KeyguardInteractor keyguardInteractor,
KeyguardTransitionBootInteractor transitionBootInteractor,
Lazy<CommunalSceneInteractor> communalSceneInteractor,
+ Lazy<CommunalSettingsInteractor> communalSettingsInteractor,
WindowManagerOcclusionManager windowManagerOcclusionManager) {
return new KeyguardViewMediator(
context,
@@ -234,6 +236,7 @@ public interface KeyguardModule {
keyguardInteractor,
transitionBootInteractor,
communalSceneInteractor,
+ communalSettingsInteractor,
windowManagerOcclusionManager);
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt
index a3796ab5ee27..cc03e49b462b 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt
@@ -145,7 +145,7 @@ constructor(
KeyguardState.GLANCEABLE_HUB
} else if (isOccluded && !isDreaming) {
KeyguardState.OCCLUDED
- } else if (hubV2 && isDreaming) {
+ } else if (isDreaming) {
KeyguardState.DREAMING
} else if (hubV2 && isIdleOnCommunal) {
if (SceneContainerFlag.isEnabled) return@collect
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
index f8c7a86687dd..f4e804ac5abf 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
@@ -24,6 +24,7 @@ import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor
import com.android.systemui.communal.domain.interactor.CommunalSettingsInteractor
import com.android.systemui.communal.shared.model.CommunalScenes
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.keyguard.KeyguardWmStateRefactor
@@ -62,6 +63,7 @@ constructor(
override val internalTransitionInteractor: InternalKeyguardTransitionInteractor,
transitionInteractor: KeyguardTransitionInteractor,
@Background private val scope: CoroutineScope,
+ @Application private val applicationScope: CoroutineScope,
@Background bgDispatcher: CoroutineDispatcher,
@Main mainDispatcher: CoroutineDispatcher,
keyguardInteractor: KeyguardInteractor,
@@ -175,7 +177,7 @@ constructor(
private fun listenForLockscreenToPrimaryBouncerDragging() {
if (SceneContainerFlag.isEnabled) return
var transitionId: UUID? = null
- scope.launch("$TAG#listenForLockscreenToPrimaryBouncerDragging") {
+ applicationScope.launch("$TAG#listenForLockscreenToPrimaryBouncerDragging") {
shadeRepository.legacyShadeExpansion.collect { shadeExpansion ->
val statusBarState = keyguardInteractor.statusBarState.value
val isKeyguardUnlocked = keyguardInteractor.isKeyguardDismissible.value
@@ -204,7 +206,7 @@ constructor(
id,
// This maps the shadeExpansion to a much faster curve, to match
// the existing logic
- 1f - MathUtils.constrainedMap(0f, 1f, 0.95f, 1f, shadeExpansion),
+ 1f - MathUtils.constrainedMap(0f, 1f, 0.88f, 1f, shadeExpansion),
nextState,
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractor.kt
index 0a4022ad4de8..e6f8406726f0 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractor.kt
@@ -159,7 +159,10 @@ constructor(
if (alternateBouncerInteractor.canShowAlternateBouncer.value) {
alternateBouncerInteractor.forceShow()
} else {
- primaryBouncerInteractor.show(true)
+ primaryBouncerInteractor.show(
+ true,
+ "KeyguardDismissInteractor#dismissKeyguardWithCallback",
+ )
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
index 7977000ed5c8..2d5ff61a5015 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
@@ -19,7 +19,6 @@ import android.app.StatusBarManager
import android.graphics.Point
import android.util.Log
import android.util.MathUtils
-import com.android.app.animation.Interpolators
import com.android.systemui.bouncer.data.repository.KeyguardBouncerRepository
import com.android.systemui.common.shared.model.NotificationContainerBounds
import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor
@@ -371,9 +370,11 @@ constructor(
currentKeyguardState == LOCKSCREEN &&
legacyShadeExpansion != 1f
) {
- emit(MathUtils.constrainedMap(0f, 1f, 0.95f, 1f, legacyShadeExpansion))
+ emit(MathUtils.constrainedMap(0f, 1f, 0.82f, 1f, legacyShadeExpansion))
} else if (
- (legacyShadeExpansion == 0f || legacyShadeExpansion == 1f) && !onGlanceableHub
+ !onGlanceableHub &&
+ isKeyguardDismissible &&
+ (legacyShadeExpansion == 0f || legacyShadeExpansion == 1f)
) {
// Resets alpha state
emit(1f)
@@ -401,15 +402,7 @@ constructor(
// 0f and 1f need to be ignored in the legacy shade expansion. These can
// flip arbitrarily as the legacy shade is reset, and would cause the
// translation value to jump around unexpectedly.
- emit(
- MathUtils.lerp(
- translationDistance,
- 0,
- Interpolators.FAST_OUT_LINEAR_IN.getInterpolation(
- legacyShadeExpansion
- ),
- )
- )
+ emit(MathUtils.lerp(translationDistance, 0, legacyShadeExpansion))
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardKeyEventInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardKeyEventInteractor.kt
index 6d9b276031e9..ced96e93d87d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardKeyEventInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardKeyEventInteractor.kt
@@ -136,7 +136,10 @@ constructor(
return true
}
StatusBarState.KEYGUARD -> {
- statusBarKeyguardViewManager.showPrimaryBouncer(true)
+ statusBarKeyguardViewManager.showPrimaryBouncer(
+ true,
+ "KeyguardKeyEventInteractor#collapseShadeLockedOrShowPrimaryBouncer",
+ )
return true
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt
index af58d10f3066..df58b215167a 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt
@@ -20,6 +20,8 @@ package com.android.systemui.keyguard.domain.interactor
import android.annotation.SuppressLint
import android.util.Log
import com.android.app.tracing.coroutines.flow.filterTraced
+import com.android.app.tracing.coroutines.flow.shareInTraced
+import com.android.app.tracing.coroutines.flow.stateInTraced
import com.android.app.tracing.coroutines.flow.traceAs
import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.app.tracing.coroutines.traceCoroutine
@@ -64,8 +66,6 @@ import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.mapLatest
import kotlinx.coroutines.flow.onStart
-import kotlinx.coroutines.flow.shareIn
-import kotlinx.coroutines.flow.stateIn
/** Encapsulates business-logic related to the keyguard transitions. */
@SysUISingleton
@@ -102,12 +102,18 @@ constructor(
val transitions = repository.transitions
val transitionState: StateFlow<TransitionStep> =
- transitions.stateIn(scope, SharingStarted.Eagerly, TransitionStep())
+ transitions.stateInTraced(
+ "KTF-transitionState",
+ scope,
+ SharingStarted.Eagerly,
+ TransitionStep(),
+ )
private val sceneTransitionPair =
sceneInteractor.transitionState
.pairwise()
- .stateIn(
+ .stateInTraced(
+ "KTF-sceneTransitionPair",
scope,
SharingStarted.Eagerly,
WithPrev(
@@ -130,11 +136,16 @@ constructor(
repository.transitions
.pairwise()
.filter { it.newValue.transitionState == TransitionState.STARTED }
- .shareIn(scope, SharingStarted.Eagerly, replay = 1)
+ .shareInTraced(
+ "KTF-startedStepWithPrecedingStep",
+ scope,
+ SharingStarted.Eagerly,
+ replay = 1,
+ )
init {
// Collect non-canceled steps and emit transition values.
- scope.launch {
+ scope.launch("KTF-update-non-canceled") {
repository.transitions
.filter { it.transitionState != TransitionState.CANCELED }
.collect { step ->
@@ -145,7 +156,7 @@ constructor(
}
}
- scope.launch {
+ scope.launch("KTF-update-transitionMap") {
repository.transitions.collect {
// FROM->TO
transitionMap[Edge.create(it.from, it.to)]?.emit(it)
@@ -160,7 +171,7 @@ constructor(
// need to ensure we emit transitionValue(A) = 0f, since no further steps will be emitted
// where the from or to states are A. This would leave transitionValue(A) stuck at an
// arbitrary non-zero value.
- scope.launch {
+ scope.launch("KTF-update-canceled") {
startedStepWithPrecedingStep.collect { (prevStep, startedStep) ->
if (
prevStep.transitionState == TransitionState.CANCELED &&
@@ -180,7 +191,7 @@ constructor(
// Safety: When any transition is FINISHED, ensure all other transitionValue flows other
// than the FINISHED state are reset to a value of 0f. There have been rare but severe
// bugs that get the device stuck in a bad state when these are not properly reset.
- scope.launch {
+ scope.launch("KTF-update-finished") {
repository.transitions
.filter { it.transitionState == TransitionState.FINISHED }
.collect {
@@ -201,7 +212,7 @@ constructor(
* If the screen is turning off, finish the current transition immediately. Further
* frames won't be visible anyway.
*/
- scope.launch {
+ scope.launch("KTF-force-finish") {
powerInteractor.screenPowerState
.filter { it == ScreenPowerState.SCREEN_TURNING_OFF }
.collect { repository.forceFinishCurrentTransition() }
@@ -347,6 +358,7 @@ constructor(
}
}
}
+ .traceAs("KTF-transition-simulator")
/**
* This function is similar to flatMapLatest but it will additionally emit a FINISHED
@@ -372,7 +384,7 @@ constructor(
traceCoroutine("cancelAndJoin") { job?.cancelAndJoin() }
job =
- launch("inner") {
+ launch("KTF-flatMapLatestWithFinished") {
val innerFlow = transform(value)
try {
innerFlow.collect { step ->
@@ -398,7 +410,6 @@ constructor(
}
}
}
- .traceAs("flatMapLatestWithFinished")
/**
* Converts old KTF states to UNDEFINED when [SceneContainerFlag] is enabled.
@@ -451,7 +462,12 @@ constructor(
val startedKeyguardTransitionStep: StateFlow<TransitionStep> =
repository.transitions
.filter { step -> step.transitionState == TransitionState.STARTED }
- .stateIn(scope, SharingStarted.Eagerly, TransitionStep())
+ .stateInTraced(
+ "KTF-startedKeyguardTransitionStep",
+ scope,
+ SharingStarted.Eagerly,
+ TransitionStep(),
+ )
/**
* The [KeyguardState] we're currently in.
@@ -517,7 +533,7 @@ constructor(
it.from
}
}
- .stateIn(scope, SharingStarted.Eagerly, OFF)
+ .stateInTraced("KTF-currentKeyguardState", scope, SharingStarted.Eagerly, OFF)
val isInTransition =
combine(isInTransitionWhere({ true }, { true }), sceneInteractor.transitionState) {
@@ -629,7 +645,7 @@ constructor(
repository.transitions
.filter { it.transitionState == TransitionState.FINISHED }
.map { it.to }
- .stateIn(scope, SharingStarted.Eagerly, OFF)
+ .stateInTraced("KTF-finishedKeyguardState", scope, SharingStarted.Eagerly, OFF)
companion object {
private val TAG = KeyguardTransitionInteractor::class.simpleName
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractor.kt
index 0b116ded42da..438dff9cb476 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractor.kt
@@ -139,7 +139,7 @@ constructor(
fun setWallpaperSupportsAmbientMode(supportsAmbientMode: Boolean) {
repository.maxAlpha.value =
if (supportsAmbientMode) {
- 0.7f
+ 0.54f
} else {
1f
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt
index 68d595ebf0b6..b4e9d8296a74 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt
@@ -196,39 +196,50 @@ constructor(
.distinctUntilChanged()
}
- private val lockscreenVisibilityWithScenes =
- combine(
- sceneInteractor.get().transitionState.flatMapLatestConflated {
- when (it) {
- is Idle -> {
- when (it.currentScene) {
- in keyguardContent -> flowOf(true)
- in nonKeyguardContent -> flowOf(false)
- in keyguardAgnosticContent -> isDeviceNotEnteredDirectly
- else ->
- throw IllegalStateException("Unknown scene: ${it.currentScene}")
- }
- }
- is Transition -> {
- when {
- it.isTransitioningSets(from = keyguardContent) -> flowOf(true)
- it.isTransitioningSets(from = nonKeyguardContent) -> flowOf(false)
- it.isTransitioningSets(from = keyguardAgnosticContent) ->
- isDeviceNotEnteredDirectly
- else ->
- throw IllegalStateException(
- "Unknown content: ${it.fromContent}"
- )
+ private val lockscreenVisibilityWithScenes: Flow<Boolean> =
+ // The scene container visibility into account as that will be forced to false when the
+ // device isn't yet provisioned (e.g. still in the setup wizard).
+ sceneInteractor.get().isVisible.flatMapLatestConflated { isVisible ->
+ if (isVisible) {
+ combine(
+ sceneInteractor.get().transitionState.flatMapLatestConflated {
+ when (it) {
+ is Idle ->
+ when (it.currentScene) {
+ in keyguardContent -> flowOf(true)
+ in nonKeyguardContent -> flowOf(false)
+ in keyguardAgnosticContent -> isDeviceNotEnteredDirectly
+ else ->
+ throw IllegalStateException(
+ "Unknown scene: ${it.currentScene}"
+ )
+ }
+ is Transition ->
+ when {
+ it.isTransitioningSets(from = keyguardContent) ->
+ flowOf(true)
+ it.isTransitioningSets(from = nonKeyguardContent) ->
+ flowOf(false)
+ it.isTransitioningSets(from = keyguardAgnosticContent) ->
+ isDeviceNotEnteredDirectly
+ else ->
+ throw IllegalStateException(
+ "Unknown content: ${it.fromContent}"
+ )
+ }
}
- }
+ },
+ wakeToGoneInteractor.canWakeDirectlyToGone,
+ ::Pair,
+ )
+ .map { (lockscreenVisibilityByTransitionState, canWakeDirectlyToGone) ->
+ lockscreenVisibilityByTransitionState && !canWakeDirectlyToGone
}
- },
- wakeToGoneInteractor.canWakeDirectlyToGone,
- ::Pair,
- )
- .map { (lockscreenVisibilityByTransitionState, canWakeDirectlyToGone) ->
- lockscreenVisibilityByTransitionState && !canWakeDirectlyToGone
+ } else {
+ // Lockscreen is never visible when the scene container is invisible.
+ flowOf(false)
}
+ }
private val lockscreenVisibilityLegacy =
combine(
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/DeviceEntryIconViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/DeviceEntryIconViewBinder.kt
index 70a827d5e45b..1ea47ec670af 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/DeviceEntryIconViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/DeviceEntryIconViewBinder.kt
@@ -39,6 +39,7 @@ import com.android.systemui.plugins.FalsingManager
import com.android.systemui.res.R
import com.android.systemui.statusbar.VibratorHelper
import com.android.systemui.util.kotlin.DisposableHandles
+import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.DisposableHandle
@@ -56,6 +57,7 @@ object DeviceEntryIconViewBinder {
@JvmStatic
fun bind(
applicationScope: CoroutineScope,
+ mainImmediateDispatcher: CoroutineDispatcher,
view: DeviceEntryIconView,
viewModel: DeviceEntryIconViewModel,
fgViewModel: DeviceEntryForegroundViewModel,
@@ -96,6 +98,32 @@ object DeviceEntryIconViewBinder {
}
disposables +=
+ view.repeatWhenAttached(mainImmediateDispatcher) {
+ repeatOnLifecycle(Lifecycle.State.CREATED) {
+ launch("$TAG#viewModel.useBackgroundProtection") {
+ viewModel.useBackgroundProtection.collect { useBackgroundProtection ->
+ if (useBackgroundProtection) {
+ bgView.visibility = View.VISIBLE
+ } else {
+ bgView.visibility = View.GONE
+ }
+ }
+ }
+ launch("$TAG#viewModel.burnInOffsets") {
+ viewModel.burnInOffsets.collect { burnInOffsets ->
+ view.translationX = burnInOffsets.x.toFloat()
+ view.translationY = burnInOffsets.y.toFloat()
+ view.aodFpDrawable.progress = burnInOffsets.progress
+ }
+ }
+
+ launch("$TAG#viewModel.deviceEntryViewAlpha") {
+ viewModel.deviceEntryViewAlpha.collect { alpha -> view.alpha = alpha }
+ }
+ }
+ }
+
+ disposables +=
view.repeatWhenAttached {
// Repeat on CREATED so that the view will always observe the entire
// GONE => AOD transition (even though the view may not be visible until the middle
@@ -152,26 +180,6 @@ object DeviceEntryIconViewBinder {
}
}
}
- launch("$TAG#viewModel.useBackgroundProtection") {
- viewModel.useBackgroundProtection.collect { useBackgroundProtection ->
- if (useBackgroundProtection) {
- bgView.visibility = View.VISIBLE
- } else {
- bgView.visibility = View.GONE
- }
- }
- }
- launch("$TAG#viewModel.burnInOffsets") {
- viewModel.burnInOffsets.collect { burnInOffsets ->
- view.translationX = burnInOffsets.x.toFloat()
- view.translationY = burnInOffsets.y.toFloat()
- view.aodFpDrawable.progress = burnInOffsets.progress
- }
- }
-
- launch("$TAG#viewModel.deviceEntryViewAlpha") {
- viewModel.deviceEntryViewAlpha.collect { alpha -> view.alpha = alpha }
- }
}
}
@@ -212,7 +220,7 @@ object DeviceEntryIconViewBinder {
}
disposables +=
- bgView.repeatWhenAttached {
+ bgView.repeatWhenAttached(mainImmediateDispatcher) {
repeatOnLifecycle(Lifecycle.State.CREATED) {
launch("$TAG#bgViewModel.alpha") {
bgViewModel.alpha.collect { alpha -> bgView.alpha = alpha }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt
index aeb327035c79..60460bf68c12 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt
@@ -135,7 +135,10 @@ object KeyguardRootViewBinder {
} else if (
event.action == MotionEvent.ACTION_UP && !event.isTouchscreenSource()
) {
- statusBarKeyguardViewManager?.showBouncer(true)
+ statusBarKeyguardViewManager?.showBouncer(
+ true,
+ "KeyguardRootViewBinder: click on lockscreen",
+ )
consumed = true
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/SideFpsProgressBar.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/SideFpsProgressBar.kt
index 1fd609dba637..853f1769994e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/SideFpsProgressBar.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/SideFpsProgressBar.kt
@@ -26,7 +26,6 @@ import android.view.ViewGroup.LayoutParams.MATCH_PARENT
import android.view.WindowManager
import android.widget.ProgressBar
import androidx.core.view.isGone
-import com.android.app.viewcapture.ViewCaptureAwareWindowManager
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.res.R
import javax.inject.Inject
@@ -38,7 +37,7 @@ class SideFpsProgressBar
@Inject
constructor(
private val layoutInflater: LayoutInflater,
- private val windowManager: ViewCaptureAwareWindowManager,
+ private val windowManager: WindowManager,
) {
private var overlayView: View? = null
@@ -91,7 +90,7 @@ constructor(
) {
if (overlayView == null) {
overlayView = layoutInflater.inflate(R.layout.sidefps_progress_bar, null, false)
- windowManager.addView(requireNotNull(overlayView), overlayViewParams)
+ windowManager.addView(overlayView, overlayViewParams)
progressBar?.pivotX = 0.0f
progressBar?.pivotY = 0.0f
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySection.kt
index 58d482b8a66f..9c8f04b419fb 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySection.kt
@@ -28,6 +28,7 @@ import androidx.constraintlayout.widget.ConstraintSet
import com.android.systemui.biometrics.AuthController
import com.android.systemui.customization.R as customR
import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.shared.model.KeyguardSection
@@ -48,6 +49,7 @@ import com.android.systemui.shade.ShadeDisplayAware
import com.android.systemui.statusbar.VibratorHelper
import dagger.Lazy
import javax.inject.Inject
+import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.DisposableHandle
@@ -56,6 +58,7 @@ class DefaultDeviceEntrySection
@Inject
constructor(
@Application private val applicationScope: CoroutineScope,
+ @Main private val mainDispatcher: CoroutineDispatcher,
private val authController: AuthController,
private val windowManager: WindowManager,
@ShadeDisplayAware private val context: Context,
@@ -91,6 +94,7 @@ constructor(
disposableHandle =
DeviceEntryIconViewBinder.bind(
applicationScope,
+ mainDispatcher,
it,
deviceEntryIconViewModel.get(),
deviceEntryForegroundViewModel.get(),
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerUdfpsIconViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerUdfpsIconViewModel.kt
index 9038922466df..803e2c0b0f96 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerUdfpsIconViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerUdfpsIconViewModel.kt
@@ -106,7 +106,10 @@ constructor(
}
fun onTapped() {
- statusBarKeyguardViewManager.showPrimaryBouncer(/* scrimmed */ true)
+ statusBarKeyguardViewManager.showPrimaryBouncer(
+ /* scrimmed */ true,
+ "AlternateBouncerUdfpsIconViewModel#onTapped",
+ )
}
val bgColor: Flow<Int> = deviceEntryBackgroundViewModel.color
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModel.kt
index cff651114c93..45f43bb484c8 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModel.kt
@@ -47,7 +47,9 @@ constructor(
/** Reports the alternate bouncer visible state if the scene container flag is enabled. */
val isVisible: Flow<Boolean> =
- alternateBouncerInteractor.get().isVisible.onEach { SceneContainerFlag.unsafeAssertInNewMode() }
+ alternateBouncerInteractor.get().isVisible.onEach {
+ SceneContainerFlag.unsafeAssertInNewMode()
+ }
/** Progress to a fully transitioned alternate bouncer. 1f represents fully transitioned. */
val transitionToAlternateBouncerProgress: Flow<Float> =
@@ -63,7 +65,10 @@ constructor(
transitionToAlternateBouncerProgress.map { it == 1f }.distinctUntilChanged()
fun onTapped() {
- statusBarKeyguardViewManager.showPrimaryBouncer(/* scrimmed */ true)
+ statusBarKeyguardViewManager.showPrimaryBouncer(
+ /* scrimmed */ true,
+ "AlternateBouncerViewModel#onTapped",
+ )
}
fun onRemovedFromWindow() {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToGlanceableHubTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToGlanceableHubTransitionViewModel.kt
index 45f8f10595e4..3cf05062325d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToGlanceableHubTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToGlanceableHubTransitionViewModel.kt
@@ -17,7 +17,6 @@
package com.android.systemui.keyguard.ui.viewmodel
import android.util.MathUtils
-import com.android.systemui.Flags.lightRevealMigration
import com.android.systemui.communal.ui.compose.TransitionDuration
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.dagger.GlanceableHubBlurComponent
@@ -28,6 +27,7 @@ import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
import com.android.systemui.keyguard.ui.transitions.GlanceableHubTransition
import com.android.systemui.scene.shared.model.Scenes
+import com.android.systemui.shared.Flags.ambientAod
import javax.inject.Inject
import kotlin.time.Duration.Companion.milliseconds
import kotlinx.coroutines.flow.Flow
@@ -56,14 +56,14 @@ constructor(
return transitionAnimation.sharedFlow(
duration = 250.milliseconds,
startTime =
- if (lightRevealMigration()) {
+ if (ambientAod()) {
100.milliseconds // Wait for the light reveal to "hit" the LS elements.
} else {
0.milliseconds
},
onStart = {
currentAlpha =
- if (lightRevealMigration()) {
+ if (ambientAod()) {
viewState.alpha()
} else {
0f
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToOccludedTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToOccludedTransitionViewModel.kt
index d981eeb0989b..ba6bda8a2a04 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToOccludedTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToOccludedTransitionViewModel.kt
@@ -17,7 +17,6 @@
package com.android.systemui.keyguard.ui.viewmodel
import android.util.MathUtils
-import com.android.systemui.Flags.lightRevealMigration
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.domain.interactor.FromAodTransitionInteractor
import com.android.systemui.keyguard.shared.model.Edge
@@ -25,6 +24,7 @@ import com.android.systemui.keyguard.shared.model.KeyguardState.AOD
import com.android.systemui.keyguard.shared.model.KeyguardState.OCCLUDED
import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
+import com.android.systemui.shared.Flags.ambientAod
import javax.inject.Inject
import kotlin.time.Duration.Companion.milliseconds
import kotlinx.coroutines.flow.Flow
@@ -52,13 +52,13 @@ constructor(animationFlow: KeyguardTransitionAnimationFlow) : DeviceEntryIconTra
return transitionAnimation.sharedFlow(
duration = 250.milliseconds,
startTime =
- if (lightRevealMigration()) {
+ if (ambientAod()) {
100.milliseconds // Wait for the light reveal to "hit" the LS elements.
} else {
0.milliseconds
},
onStart = {
- if (lightRevealMigration()) {
+ if (ambientAod()) {
currentAlpha = viewState.alpha()
} else {
currentAlpha = 0f
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModel.kt
index 13cd5839e1c8..9b4bd67f227e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModel.kt
@@ -257,7 +257,9 @@ constructor(
if (SceneContainerFlag.isEnabled) {
deviceEntryInteractor.attemptDeviceEntry()
} else {
- keyguardViewController.get().showPrimaryBouncer(/* scrim */ true)
+ keyguardViewController
+ .get()
+ .showPrimaryBouncer(/* scrim */ true, "DeviceEntryIconViewModel#onUserInteraction")
}
deviceEntrySourceInteractor.attemptEnterDeviceFromDeviceEntryIcon()
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToOccludedTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToOccludedTransitionViewModel.kt
index b15cacf077a4..2eb5bf9328e6 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToOccludedTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToOccludedTransitionViewModel.kt
@@ -17,7 +17,6 @@
package com.android.systemui.keyguard.ui.viewmodel
import android.util.MathUtils
-import com.android.systemui.Flags.lightRevealMigration
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.domain.interactor.FromAodTransitionInteractor
import com.android.systemui.keyguard.shared.model.Edge
@@ -25,6 +24,7 @@ import com.android.systemui.keyguard.shared.model.KeyguardState.DOZING
import com.android.systemui.keyguard.shared.model.KeyguardState.OCCLUDED
import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
+import com.android.systemui.shared.Flags.ambientAod
import javax.inject.Inject
import kotlin.time.Duration.Companion.milliseconds
import kotlinx.coroutines.flow.Flow
@@ -56,7 +56,7 @@ constructor(animationFlow: KeyguardTransitionAnimationFlow) : DeviceEntryIconTra
duration = 250.milliseconds,
startTime = 0.milliseconds,
onStart = {
- if (lightRevealMigration()) {
+ if (ambientAod()) {
currentAlpha = viewState.alpha()
} else {
currentAlpha = 0f
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModel.kt
index 9312bca04994..a0458f0172f5 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModel.kt
@@ -64,7 +64,7 @@ constructor(
val shortcutsAlpha: Flow<Float> =
transitionAnimation.sharedFlow(
- duration = FromLockscreenTransitionInteractor.TO_PRIMARY_BOUNCER_DURATION,
+ duration = 200.milliseconds,
onStep = alphaForAnimationStep,
// Rapid swipes to bouncer, and may end up skipping intermediate values that would've
// caused a complete fade out of lockscreen elements. Ensure it goes to 0f.
diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/QSTilesVerboseLog.java b/packages/SystemUI/src/com/android/systemui/log/dagger/QSTilesVerboseLog.java
index b0c2f8c59deb..b27a4217d454 100644
--- a/packages/SystemUI/src/com/android/systemui/log/dagger/QSTilesVerboseLog.java
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/QSTilesVerboseLog.java
@@ -26,8 +26,7 @@ import java.lang.annotation.Retention;
import javax.inject.Qualifier;
/**
- * A {@link LogBuffer} for QS tiles messages. It's used exclusively in
- * {@link com.android.systemui.qs.tiles.base.logging.QSTileLogger}
+ * A {@link LogBuffer} for QS tiles messages. It's used exclusively in @link QSTileLogger}.
*/
@Qualifier
@Documented
diff --git a/packages/SystemUI/src/com/android/systemui/lowlightclock/DirectBootCondition.kt b/packages/SystemUI/src/com/android/systemui/lowlightclock/DirectBootCondition.kt
index cdac61cea10b..ce5fd195d151 100644
--- a/packages/SystemUI/src/com/android/systemui/lowlightclock/DirectBootCondition.kt
+++ b/packages/SystemUI/src/com/android/systemui/lowlightclock/DirectBootCondition.kt
@@ -20,7 +20,7 @@ import android.content.Intent
import android.content.IntentFilter
import android.os.UserManager
import com.android.systemui.broadcast.BroadcastDispatcher
-import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.shared.condition.Condition
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
@@ -35,7 +35,7 @@ class DirectBootCondition
constructor(
broadcastDispatcher: BroadcastDispatcher,
private val userManager: UserManager,
- @Application private val coroutineScope: CoroutineScope,
+ @Background private val coroutineScope: CoroutineScope,
) : Condition(coroutineScope) {
private var job: Job? = null
private val directBootFlow =
diff --git a/packages/SystemUI/src/com/android/systemui/lowlightclock/ForceLowLightCondition.kt b/packages/SystemUI/src/com/android/systemui/lowlightclock/ForceLowLightCondition.kt
index f75894da2c85..85a94306d0ea 100644
--- a/packages/SystemUI/src/com/android/systemui/lowlightclock/ForceLowLightCondition.kt
+++ b/packages/SystemUI/src/com/android/systemui/lowlightclock/ForceLowLightCondition.kt
@@ -17,7 +17,7 @@ package com.android.systemui.lowlightclock
import android.text.TextUtils
import android.util.Log
-import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.shared.condition.Condition
import com.android.systemui.statusbar.commandline.Command
import com.android.systemui.statusbar.commandline.CommandRegistry
@@ -31,7 +31,7 @@ import kotlinx.coroutines.CoroutineScope
*/
class ForceLowLightCondition
@Inject
-constructor(@Application scope: CoroutineScope, commandRegistry: CommandRegistry) :
+constructor(@Background scope: CoroutineScope, commandRegistry: CommandRegistry) :
Condition(scope, null, true) {
/**
* Default Constructor.
diff --git a/packages/SystemUI/src/com/android/systemui/lowlightclock/LowLightCondition.kt b/packages/SystemUI/src/com/android/systemui/lowlightclock/LowLightCondition.kt
index a29c666ff792..34e9a6351180 100644
--- a/packages/SystemUI/src/com/android/systemui/lowlightclock/LowLightCondition.kt
+++ b/packages/SystemUI/src/com/android/systemui/lowlightclock/LowLightCondition.kt
@@ -16,7 +16,7 @@
package com.android.systemui.lowlightclock
import com.android.internal.logging.UiEventLogger
-import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.shared.condition.Condition
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
@@ -25,7 +25,7 @@ import kotlinx.coroutines.CoroutineScope
class LowLightCondition
@Inject
constructor(
- @Application scope: CoroutineScope,
+ @Background scope: CoroutineScope,
private val ambientLightModeMonitor: AmbientLightModeMonitor,
private val uiEventLogger: UiEventLogger,
) : Condition(scope) {
diff --git a/packages/SystemUI/src/com/android/systemui/lowlightclock/ScreenSaverEnabledCondition.kt b/packages/SystemUI/src/com/android/systemui/lowlightclock/ScreenSaverEnabledCondition.kt
index f6b0f6276440..e761cc0692f7 100644
--- a/packages/SystemUI/src/com/android/systemui/lowlightclock/ScreenSaverEnabledCondition.kt
+++ b/packages/SystemUI/src/com/android/systemui/lowlightclock/ScreenSaverEnabledCondition.kt
@@ -21,7 +21,7 @@ import android.os.UserHandle
import android.provider.Settings
import android.util.Log
import com.android.internal.R
-import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.shared.condition.Condition
import com.android.systemui.util.settings.SecureSettings
@@ -32,7 +32,7 @@ import kotlinx.coroutines.CoroutineScope
class ScreenSaverEnabledCondition
@Inject
constructor(
- @Application scope: CoroutineScope,
+ @Background scope: CoroutineScope,
@Main resources: Resources,
private val secureSettings: SecureSettings,
) : Condition(scope) {
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataFilterImpl.kt b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataFilterImpl.kt
index 2cd5016cb206..d053dd2b54d1 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataFilterImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataFilterImpl.kt
@@ -248,7 +248,7 @@ constructor(
// Update loading state with actual active value
mediaFilterRepository.selectedUserEntries.value[lastActiveId]?.let {
mediaFilterRepository.addMediaDataLoadingState(
- MediaDataLoadingModel.Loaded(lastActiveId, immediately)
+ MediaDataLoadingModel.Loaded(lastActiveId)
)
mediaLogger.logMediaLoaded(lastActiveId, it.active, "expiring reactivated id")
listeners.forEach { listener ->
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/shared/model/MediaDataLoadingModel.kt b/packages/SystemUI/src/com/android/systemui/media/controls/shared/model/MediaDataLoadingModel.kt
index c8a02faea58a..323e5cb76aba 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/shared/model/MediaDataLoadingModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/shared/model/MediaDataLoadingModel.kt
@@ -26,13 +26,10 @@ sealed class MediaDataLoadingModel {
/** Media data has been loaded. */
data class Loaded(
override val instanceId: InstanceId,
- val immediatelyUpdateUi: Boolean = true,
val receivedSmartspaceCardLatency: Int = 0,
val isSsReactivated: Boolean = false,
) : MediaDataLoadingModel()
/** Media data has been removed. */
- data class Removed(
- override val instanceId: InstanceId,
- ) : MediaDataLoadingModel()
+ data class Removed(override val instanceId: InstanceId) : MediaDataLoadingModel()
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselController.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselController.kt
index 71b3223b77be..800220ee962a 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselController.kt
@@ -68,7 +68,7 @@ import com.android.systemui.media.controls.ui.view.MediaHostState
import com.android.systemui.media.controls.ui.view.MediaScrollView
import com.android.systemui.media.controls.ui.view.MediaViewHolder
import com.android.systemui.media.controls.ui.viewmodel.MediaCarouselViewModel
-import com.android.systemui.media.controls.ui.viewmodel.MediaCommonViewModel
+import com.android.systemui.media.controls.ui.viewmodel.MediaControlViewModel
import com.android.systemui.media.controls.util.MediaUiEventLogger
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.plugins.FalsingManager
@@ -324,8 +324,8 @@ constructor(
private var widthInSceneContainerPx = 0
private var heightInSceneContainerPx = 0
- private val controllerById = mutableMapOf<String, MediaViewController>()
- private val commonViewModels = mutableListOf<MediaCommonViewModel>()
+ private val controllerById = mutableMapOf<InstanceId, MediaViewController>()
+ private val controlViewModels = mutableListOf<MediaControlViewModel>()
private val isOnGone =
keyguardTransitionInteractor
@@ -566,10 +566,10 @@ constructor(
private fun listenForMediaItemsChanges(scope: CoroutineScope): Job {
return scope.launch {
mediaCarouselViewModel.mediaItems.collectLatest {
- val diffUtilCallback = MediaViewModelCallback(commonViewModels, it)
+ val diffUtilCallback = MediaViewModelCallback(controlViewModels, it)
val listUpdateCallback =
MediaViewModelListUpdateCallback(
- old = commonViewModels,
+ old = controlViewModels,
new = it,
onAdded = this@MediaCarouselController::onAdded,
onUpdated = this@MediaCarouselController::onUpdated,
@@ -590,7 +590,7 @@ constructor(
}
private fun onAdded(
- commonViewModel: MediaCommonViewModel,
+ controlViewModel: MediaControlViewModel,
position: Int,
configChanged: Boolean = false,
) {
@@ -601,64 +601,52 @@ constructor(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT,
)
- when (commonViewModel) {
- is MediaCommonViewModel.MediaControl -> {
- val viewHolder = MediaViewHolder.create(LayoutInflater.from(context), mediaContent)
- viewController.widthInSceneContainerPx = widthInSceneContainerPx
- viewController.heightInSceneContainerPx = heightInSceneContainerPx
- viewController.attachPlayer(viewHolder)
- viewController.mediaViewHolder?.player?.layoutParams = lp
- if (configChanged) {
- commonViewModel.controlViewModel.onMediaConfigChanged()
- }
- MediaControlViewBinder.bind(
- viewHolder,
- commonViewModel.controlViewModel,
- viewController,
- falsingManager,
- backgroundDispatcher,
- mainDispatcher,
- )
- mediaContent.addView(viewHolder.player, position)
- controllerById[commonViewModel.instanceId.toString()] = viewController
- }
+ val viewHolder = MediaViewHolder.create(LayoutInflater.from(context), mediaContent)
+ viewController.widthInSceneContainerPx = widthInSceneContainerPx
+ viewController.heightInSceneContainerPx = heightInSceneContainerPx
+ viewController.attachPlayer(viewHolder)
+ viewController.mediaViewHolder?.player?.layoutParams = lp
+ if (configChanged) {
+ controlViewModel.onMediaConfigChanged()
}
+ MediaControlViewBinder.bind(
+ viewHolder,
+ controlViewModel,
+ viewController,
+ falsingManager,
+ backgroundDispatcher,
+ mainDispatcher,
+ )
+ mediaContent.addView(viewHolder.player, position)
+ controllerById[controlViewModel.instanceId] = viewController
viewController.setListening(mediaCarouselScrollHandler.visibleToUser && currentlyExpanded)
updateViewControllerToState(viewController, noAnimation = true)
updatePageIndicator()
- if (
- commonViewModel is MediaCommonViewModel.MediaControl && commonViewModel.isMediaFromRec
- ) {
- mediaCarouselScrollHandler.scrollToPlayer(
- mediaCarouselScrollHandler.visibleMediaIndex,
- destIndex = 0,
- )
- }
mediaCarouselScrollHandler.onPlayersChanged()
mediaFrame.requiresRemeasuring = true
- commonViewModel.onAdded(commonViewModel)
+ controlViewModel.onAdded(controlViewModel)
}
- private fun onUpdated(commonViewModel: MediaCommonViewModel, position: Int) {
- commonViewModel.onUpdated(commonViewModel)
+ private fun onUpdated(controlViewModel: MediaControlViewModel, position: Int) {
+ controlViewModel.onUpdated(controlViewModel)
updatePageIndicator()
mediaCarouselScrollHandler.onPlayersChanged()
}
- private fun onRemoved(commonViewModel: MediaCommonViewModel) {
- val id = (commonViewModel as MediaCommonViewModel.MediaControl).instanceId.toString()
+ private fun onRemoved(controlViewModel: MediaControlViewModel) {
+ val id = controlViewModel.instanceId
controllerById.remove(id)?.let {
mediaCarouselScrollHandler.onPrePlayerRemoved(it.mediaViewHolder!!.player)
mediaContent.removeView(it.mediaViewHolder!!.player)
it.onDestroy()
mediaCarouselScrollHandler.onPlayersChanged()
updatePageIndicator()
- commonViewModel.onRemoved(true)
+ controlViewModel.onRemoved(true)
}
}
- private fun onMoved(commonViewModel: MediaCommonViewModel, from: Int, to: Int) {
- val id = (commonViewModel as MediaCommonViewModel.MediaControl).instanceId.toString()
+ private fun onMoved(controlViewModel: MediaControlViewModel, from: Int, to: Int) {
+ val id = controlViewModel.instanceId
controllerById[id]?.let {
mediaContent.removeViewAt(from)
mediaContent.addView(it.mediaViewHolder!!.player, to)
@@ -667,19 +655,12 @@ constructor(
mediaCarouselScrollHandler.onPlayersChanged()
}
- private fun setNewViewModelsList(viewModels: List<MediaCommonViewModel>) {
- commonViewModels.clear()
- commonViewModels.addAll(viewModels)
+ private fun setNewViewModelsList(viewModels: List<MediaControlViewModel>) {
+ controlViewModels.clear()
+ controlViewModels.addAll(viewModels)
// Ensure we only show the needed UMOs in media carousel.
- val viewIds =
- viewModels
- .map { mediaCommonViewModel ->
- (mediaCommonViewModel as MediaCommonViewModel.MediaControl)
- .instanceId
- .toString()
- }
- .toHashSet()
+ val viewIds = viewModels.map { controlViewModel -> controlViewModel.instanceId }.toHashSet()
controllerById
.filter { !viewIds.contains(it.key) }
.forEach {
@@ -976,9 +957,8 @@ constructor(
ColorStateList.valueOf(context.getColor(R.color.media_paging_indicator))
if (recreateMedia) {
mediaContent.removeAllViews()
- commonViewModels.forEachIndexed { index, viewModel ->
- val mediaControlViewModel = (viewModel as MediaCommonViewModel.MediaControl)
- controllerById[mediaControlViewModel.instanceId.toString()]?.onDestroy()
+ controlViewModels.forEachIndexed { index, viewModel ->
+ controllerById[viewModel.instanceId]?.onDestroy()
onAdded(viewModel, index, configChanged = true)
}
}
@@ -1309,7 +1289,7 @@ constructor(
println("dataKeys: ${MediaPlayerData.dataKeys()}")
println("orderedPlayerSortKeys: ${MediaPlayerData.playerKeys()}")
println("visiblePlayerSortKeys: ${MediaPlayerData.visiblePlayerKeys()}")
- println("commonViewModels: $commonViewModels")
+ println("controlViewModels: $controlViewModels")
println("smartspaceMediaData: ${MediaPlayerData.smartspaceMediaData}")
println("shouldPrioritizeSs: ${MediaPlayerData.shouldPrioritizeSs}")
println("current size: $currentCarouselWidth x $currentCarouselHeight")
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/util/MediaViewModelCallback.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/util/MediaViewModelCallback.kt
index 2fc44ad3cce6..ad2d46263faf 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/util/MediaViewModelCallback.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/util/MediaViewModelCallback.kt
@@ -17,12 +17,12 @@
package com.android.systemui.media.controls.ui.util
import androidx.recyclerview.widget.DiffUtil
-import com.android.systemui.media.controls.ui.viewmodel.MediaCommonViewModel
+import com.android.systemui.media.controls.ui.viewmodel.MediaControlViewModel
/** A [DiffUtil.Callback] to calculate difference between old and new media view-model list. */
class MediaViewModelCallback(
- private val old: List<MediaCommonViewModel>,
- private val new: List<MediaCommonViewModel>,
+ private val old: List<MediaControlViewModel>,
+ private val new: List<MediaControlViewModel>,
) : DiffUtil.Callback() {
override fun getOldListSize(): Int {
@@ -36,27 +36,12 @@ class MediaViewModelCallback(
override fun areItemsTheSame(oldIndex: Int, newIndex: Int): Boolean {
val oldItem = old[oldIndex]
val newItem = new[newIndex]
- return if (
- oldItem is MediaCommonViewModel.MediaControl &&
- newItem is MediaCommonViewModel.MediaControl
- ) {
- oldItem.instanceId == newItem.instanceId
- } else {
- false
- }
+ return oldItem.instanceId == newItem.instanceId
}
override fun areContentsTheSame(oldIndex: Int, newIndex: Int): Boolean {
val oldItem = old[oldIndex]
val newItem = new[newIndex]
- return if (
- oldItem is MediaCommonViewModel.MediaControl &&
- newItem is MediaCommonViewModel.MediaControl
- ) {
- oldItem.immediatelyUpdateUi == newItem.immediatelyUpdateUi &&
- oldItem.updateTime == newItem.updateTime
- } else {
- false
- }
+ return oldItem.updateTime == newItem.updateTime
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/util/MediaViewModelListUpdateCallback.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/util/MediaViewModelListUpdateCallback.kt
index 6022b7b1fc13..1db8c58d4140 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/util/MediaViewModelListUpdateCallback.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/util/MediaViewModelListUpdateCallback.kt
@@ -17,17 +17,17 @@
package com.android.systemui.media.controls.ui.util
import androidx.recyclerview.widget.ListUpdateCallback
-import com.android.systemui.media.controls.ui.viewmodel.MediaCommonViewModel
+import com.android.systemui.media.controls.ui.viewmodel.MediaControlViewModel
import kotlin.math.min
/** A [ListUpdateCallback] to apply media events needed to reach the new state. */
class MediaViewModelListUpdateCallback(
- private val old: List<MediaCommonViewModel>,
- private val new: List<MediaCommonViewModel>,
- private val onAdded: (MediaCommonViewModel, Int) -> Unit,
- private val onUpdated: (MediaCommonViewModel, Int) -> Unit,
- private val onRemoved: (MediaCommonViewModel) -> Unit,
- private val onMoved: (MediaCommonViewModel, Int, Int) -> Unit,
+ private val old: List<MediaControlViewModel>,
+ private val new: List<MediaControlViewModel>,
+ private val onAdded: (MediaControlViewModel, Int) -> Unit,
+ private val onUpdated: (MediaControlViewModel, Int) -> Unit,
+ private val onRemoved: (MediaControlViewModel) -> Unit,
+ private val onMoved: (MediaControlViewModel, Int, Int) -> Unit,
) : ListUpdateCallback {
override fun onInserted(position: Int, count: Int) {
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaCarouselViewModel.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaCarouselViewModel.kt
index dfaee4434bcf..54d3151694bd 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaCarouselViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaCarouselViewModel.kt
@@ -56,7 +56,7 @@ constructor(
val hasAnyMediaOrRecommendations: StateFlow<Boolean> = interactor.hasAnyMediaOrRecommendation
val hasActiveMediaOrRecommendations: StateFlow<Boolean> =
interactor.hasActiveMediaOrRecommendation
- val mediaItems: StateFlow<List<MediaCommonViewModel>> =
+ val mediaItems: StateFlow<List<MediaControlViewModel>> =
interactor.currentMedia
.map { sortedItems ->
val mediaList = buildList {
@@ -91,8 +91,7 @@ constructor(
var updateHostVisibility: () -> Unit = {}
- private val mediaControlByInstanceId =
- mutableMapOf<InstanceId, MediaCommonViewModel.MediaControl>()
+ private val mediaControlByInstanceId = mutableMapOf<InstanceId, MediaControlViewModel>()
private var modelsPendingRemoval: MutableSet<MediaCommonModel> = mutableSetOf()
@@ -108,18 +107,16 @@ constructor(
interactor.reorderMedia()
}
- private fun toViewModel(
- commonModel: MediaCommonModel.MediaControl
- ): MediaCommonViewModel.MediaControl {
+ private fun toViewModel(commonModel: MediaCommonModel.MediaControl): MediaControlViewModel {
val instanceId = commonModel.mediaLoadedModel.instanceId
- return mediaControlByInstanceId[instanceId]?.copy(
- immediatelyUpdateUi = commonModel.mediaLoadedModel.immediatelyUpdateUi,
- updateTime = commonModel.updateTime,
- )
- ?: MediaCommonViewModel.MediaControl(
+ return mediaControlByInstanceId[instanceId]?.copy(updateTime = commonModel.updateTime)
+ ?: MediaControlViewModel(
+ applicationContext = applicationContext,
+ backgroundDispatcher = backgroundDispatcher,
+ backgroundExecutor = backgroundExecutor,
+ interactor = controlInteractorFactory.create(instanceId),
+ logger = logger,
instanceId = instanceId,
- immediatelyUpdateUi = commonModel.mediaLoadedModel.immediatelyUpdateUi,
- controlViewModel = createMediaControlViewModel(instanceId),
onAdded = {
mediaLogger.logMediaCardAdded(instanceId)
onMediaControlAddedOrUpdated(it, commonModel)
@@ -130,31 +127,20 @@ constructor(
mediaLogger.logMediaCardRemoved(instanceId)
},
onUpdated = { onMediaControlAddedOrUpdated(it, commonModel) },
- isMediaFromRec = commonModel.isMediaFromRec,
updateTime = commonModel.updateTime,
)
.also { mediaControlByInstanceId[instanceId] = it }
}
- private fun createMediaControlViewModel(instanceId: InstanceId): MediaControlViewModel {
- return MediaControlViewModel(
- applicationContext = applicationContext,
- backgroundDispatcher = backgroundDispatcher,
- backgroundExecutor = backgroundExecutor,
- interactor = controlInteractorFactory.create(instanceId),
- logger = logger,
- )
- }
-
private fun onMediaControlAddedOrUpdated(
- commonViewModel: MediaCommonViewModel,
+ controlViewModel: MediaControlViewModel,
commonModel: MediaCommonModel.MediaControl,
) {
if (commonModel.canBeRemoved && !Utils.useMediaResumption(applicationContext)) {
// This media control is due for removal as it is now paused + timed out, and resumption
// setting is off.
if (isReorderingAllowed()) {
- commonViewModel.onRemoved(true)
+ controlViewModel.onRemoved(true)
} else {
modelsPendingRemoval.add(commonModel)
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaCommonViewModel.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaCommonViewModel.kt
deleted file mode 100644
index d493d57051f7..000000000000
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaCommonViewModel.kt
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.media.controls.ui.viewmodel
-
-import com.android.internal.logging.InstanceId
-
-/** Models media view model UI state. */
-sealed class MediaCommonViewModel {
-
- abstract val onAdded: (MediaCommonViewModel) -> Unit
- abstract val onRemoved: (Boolean) -> Unit
- abstract val onUpdated: (MediaCommonViewModel) -> Unit
-
- data class MediaControl(
- val instanceId: InstanceId,
- val immediatelyUpdateUi: Boolean,
- val controlViewModel: MediaControlViewModel,
- override val onAdded: (MediaCommonViewModel) -> Unit,
- override val onRemoved: (Boolean) -> Unit,
- override val onUpdated: (MediaCommonViewModel) -> Unit,
- val isMediaFromRec: Boolean = false,
- val updateTime: Long = 0,
- ) : MediaCommonViewModel()
-}
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaControlViewModel.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaControlViewModel.kt
index 015274a10330..31cfb8479dd1 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaControlViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaControlViewModel.kt
@@ -45,12 +45,17 @@ import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
/** Models UI state and handles user input for a media control. */
-class MediaControlViewModel(
+data class MediaControlViewModel(
@Application private val applicationContext: Context,
@Background private val backgroundDispatcher: CoroutineDispatcher,
@Background private val backgroundExecutor: Executor,
private val interactor: MediaControlInteractor,
private val logger: MediaUiEventLogger,
+ val instanceId: InstanceId,
+ val onAdded: (MediaControlViewModel) -> Unit,
+ val onRemoved: (Boolean) -> Unit,
+ val onUpdated: (MediaControlViewModel) -> Unit,
+ val updateTime: Long = 0,
) {
val player: Flow<MediaPlayerViewModel?> =
interactor.mediaControl
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapterBase.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapterBase.java
index ac1672db9375..e3990d25f94e 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapterBase.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapterBase.java
@@ -237,8 +237,7 @@ public abstract class MediaOutputAdapterBase extends RecyclerView.Adapter<Recycl
clickListener = v -> onItemClick(v, device);
}
} else {
- deviceStatusIcon = getDeviceStatusIcon(device,
- device.hasOngoingSession());
+ deviceStatusIcon = getDeviceStatusIcon(device);
clickListener = getClickListenerBasedOnSelectionBehavior(device);
}
deviceDisabled = clickListener == null;
@@ -302,12 +301,8 @@ public abstract class MediaOutputAdapterBase extends RecyclerView.Adapter<Recycl
}
@Nullable
- private Drawable getDeviceStatusIcon(MediaDevice device, boolean hasOngoingSession) {
- if (hasOngoingSession) {
- return mContext.getDrawable(R.drawable.ic_sound_bars_anim);
- } else {
- return Api34Impl.getDeviceStatusIconBasedOnSelectionBehavior(device, mContext);
- }
+ private Drawable getDeviceStatusIcon(MediaDevice device) {
+ return Api34Impl.getDeviceStatusIconBasedOnSelectionBehavior(device, mContext);
}
protected void onExpandGroupButtonClicked() {
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapterLegacy.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapterLegacy.java
index 6ab4a52dc919..7ab6b3cfb8b1 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapterLegacy.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapterLegacy.java
@@ -231,7 +231,7 @@ public class MediaOutputAdapterLegacy extends MediaOutputAdapterBase {
updateFullItemClickListener(clickListener);
updateContentAlpha(deviceDisabled);
updateSubtitle(subtitle);
- updateDeviceStatusIcon(deviceStatusIcon);
+ updateDeviceStatusIcon(deviceStatusIcon, ongoingSessionStatus, connectionState);
updateItemBackground(connectionState);
}
@@ -523,11 +523,20 @@ public class MediaOutputAdapterLegacy extends MediaOutputAdapterBase {
mStatusIcon.setAlpha(alphaValue);
}
- private void updateDeviceStatusIcon(@Nullable Drawable deviceStatusIcon) {
- if (deviceStatusIcon == null) {
+ private void updateDeviceStatusIcon(@Nullable Drawable deviceStatusIcon,
+ @Nullable OngoingSessionStatus ongoingSessionStatus,
+ ConnectionState connectionState) {
+ boolean showOngoingSession =
+ ongoingSessionStatus != null && connectionState == ConnectionState.DISCONNECTED;
+ if (deviceStatusIcon == null && !showOngoingSession) {
mStatusIcon.setVisibility(View.GONE);
} else {
- mStatusIcon.setImageDrawable(deviceStatusIcon);
+ if (showOngoingSession) {
+ mStatusIcon.setImageDrawable(
+ mContext.getDrawable(R.drawable.ic_sound_bars_anim));
+ } else {
+ mStatusIcon.setImageDrawable(deviceStatusIcon);
+ }
mStatusIcon.setImageTintList(ColorStateList.valueOf(
mController.getColorSchemeLegacy().getColorItemContent()));
if (deviceStatusIcon instanceof AnimatedVectorDrawable) {
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaSwitchingController.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaSwitchingController.java
index d0c6a3e6a3ef..bf1f971c0f8c 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaSwitchingController.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaSwitchingController.java
@@ -580,7 +580,23 @@ public class MediaSwitchingController
synchronized (mMediaDevicesLock) {
if (!mLocalMediaManager.isPreferenceRouteListingExist()) {
attachRangeInfo(devices);
- Collections.sort(devices, Comparator.naturalOrder());
+ if (Flags.enableOutputSwitcherDeviceGrouping()) {
+ List<MediaDevice> selectedDevices = new ArrayList<>();
+ Set<String> selectedDeviceIds =
+ getSelectedMediaDevice().stream()
+ .map(MediaDevice::getId)
+ .collect(Collectors.toSet());
+ for (MediaDevice device : devices) {
+ if (selectedDeviceIds.contains(device.getId())) {
+ selectedDevices.add(device);
+ }
+ }
+ devices.removeAll(selectedDevices);
+ Collections.sort(devices, Comparator.naturalOrder());
+ devices.addAll(0, selectedDevices);
+ } else {
+ Collections.sort(devices, Comparator.naturalOrder());
+ }
}
if (Flags.fixOutputMediaItemListIndexOutOfBoundsException()) {
// For the first time building list, to make sure the top device is the connected
diff --git a/packages/SystemUI/src/com/android/systemui/media/remedia/ui/compose/DismissibleHorizontalPager.kt b/packages/SystemUI/src/com/android/systemui/media/remedia/ui/compose/DismissibleHorizontalPager.kt
deleted file mode 100644
index 8df916fe6969..000000000000
--- a/packages/SystemUI/src/com/android/systemui/media/remedia/ui/compose/DismissibleHorizontalPager.kt
+++ /dev/null
@@ -1,160 +0,0 @@
-/*
- * Copyright (C) 2025 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.media.remedia.ui.compose
-
-import androidx.compose.animation.core.Animatable
-import androidx.compose.animation.core.AnimationVector1D
-import androidx.compose.foundation.layout.Box
-import androidx.compose.foundation.layout.BoxScope
-import androidx.compose.foundation.pager.HorizontalPager
-import androidx.compose.foundation.pager.PagerScope
-import androidx.compose.foundation.pager.PagerState
-import androidx.compose.foundation.pager.rememberPagerState
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.remember
-import androidx.compose.runtime.rememberCoroutineScope
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.geometry.Offset
-import androidx.compose.ui.graphics.graphicsLayer
-import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
-import androidx.compose.ui.input.nestedscroll.NestedScrollSource
-import androidx.compose.ui.input.nestedscroll.nestedScroll
-import androidx.compose.ui.unit.Dp
-import androidx.compose.ui.unit.Velocity
-import androidx.compose.ui.unit.dp
-import com.android.compose.modifiers.thenIf
-import kotlinx.coroutines.launch
-
-/** State for a [DismissibleHorizontalPager] */
-class DismissibleHorizontalPagerState(
- val isDismissible: Boolean,
- val isScrollingEnabled: Boolean,
- val pagerState: PagerState,
- val offset: Animatable<Float, AnimationVector1D>,
-)
-
-/**
- * Returns a remembered [DismissibleHorizontalPagerState] that starts at [initialPage] and has
- * [pageCount] total pages.
- */
-@Composable
-fun rememberDismissibleHorizontalPagerState(
- isDismissible: Boolean = true,
- isScrollingEnabled: Boolean = true,
- initialPage: Int = 0,
- pageCount: () -> Int,
-): DismissibleHorizontalPagerState {
- val pagerState = rememberPagerState(initialPage = initialPage, pageCount = pageCount)
- val offset = remember { Animatable(0f) }
-
- return remember(isDismissible, isScrollingEnabled, pagerState, offset) {
- DismissibleHorizontalPagerState(
- isDismissible = isDismissible,
- isScrollingEnabled = isScrollingEnabled,
- pagerState = pagerState,
- offset = offset,
- )
- }
-}
-
-/**
- * A [HorizontalPager] that can be swiped-away to dismiss by the user when swiped farther left or
- * right once fully scrolled to the left-most or right-most page, respectively.
- */
-@Composable
-fun DismissibleHorizontalPager(
- state: DismissibleHorizontalPagerState,
- onDismissed: () -> Unit,
- modifier: Modifier = Modifier,
- key: ((Int) -> Any)? = null,
- pageSpacing: Dp = 0.dp,
- isFalseTouchDetected: Boolean,
- indicator: @Composable BoxScope.() -> Unit,
- pageContent: @Composable PagerScope.(page: Int) -> Unit,
-) {
- val scope = rememberCoroutineScope()
-
- val nestedScrollConnection = remember {
- object : NestedScrollConnection {
- override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset {
- return if (state.offset.value > 0f && available.x < 0f) {
- scope.launch { state.offset.snapTo(state.offset.value + available.x) }
- Offset(available.x, 0f)
- } else if (state.offset.value < 0f && available.x > 0f) {
- scope.launch { state.offset.snapTo(state.offset.value + available.x) }
- Offset(available.x, 0f)
- } else {
- Offset.Zero
- }
- }
-
- override fun onPostScroll(
- consumed: Offset,
- available: Offset,
- source: NestedScrollSource,
- ): Offset {
- return if (available.x > 0f) {
- scope.launch { state.offset.snapTo(state.offset.value + available.x) }
- Offset(available.x, 0f)
- } else if (available.x < 0f) {
- scope.launch { state.offset.snapTo(state.offset.value + available.x) }
- Offset(available.x, 0f)
- } else {
- Offset.Zero
- }
- }
-
- override suspend fun onPostFling(consumed: Velocity, available: Velocity): Velocity {
- scope.launch {
- state.offset.animateTo(
- if (state.offset.value >= state.pagerState.layoutInfo.pageSize / 2f) {
- state.pagerState.layoutInfo.pageSize * 2f
- } else if (
- state.offset.value <= -state.pagerState.layoutInfo.pageSize / 2f
- ) {
- -state.pagerState.layoutInfo.pageSize * 2f
- } else {
- 0f
- }
- )
- if (state.offset.value != 0f) {
- onDismissed()
- }
- }
- return super.onPostFling(consumed, available)
- }
- }
- }
-
- Box(modifier = modifier) {
- HorizontalPager(
- state = state.pagerState,
- userScrollEnabled = state.isScrollingEnabled && !isFalseTouchDetected,
- key = key,
- pageSpacing = pageSpacing,
- pageContent = pageContent,
- modifier =
- Modifier.thenIf(state.isDismissible) {
- Modifier.nestedScroll(nestedScrollConnection).graphicsLayer {
- translationX = state.offset.value
- }
- },
- )
-
- indicator()
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/media/remedia/ui/compose/Media.kt b/packages/SystemUI/src/com/android/systemui/media/remedia/ui/compose/Media.kt
index f07238895aa5..d6d185195c51 100644
--- a/packages/SystemUI/src/com/android/systemui/media/remedia/ui/compose/Media.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/remedia/ui/compose/Media.kt
@@ -33,6 +33,7 @@ import androidx.compose.animation.graphics.vector.AnimatedImageVector
import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.Canvas
import androidx.compose.foundation.Image
+import androidx.compose.foundation.OverscrollEffect
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.combinedClickable
@@ -54,6 +55,8 @@ import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.pager.HorizontalPager
+import androidx.compose.foundation.pager.rememberPagerState
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.ButtonDefaults
@@ -107,7 +110,9 @@ import androidx.compose.ui.semantics.contentDescription
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.Constraints
import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.DpSize
import androidx.compose.ui.unit.dp
+import androidx.compose.ui.util.fastCoerceIn
import androidx.compose.ui.util.fastForEach
import androidx.compose.ui.util.fastForEachIndexed
import androidx.compose.ui.util.fastRoundToInt
@@ -120,6 +125,7 @@ import com.android.compose.animation.scene.SceneKey
import com.android.compose.animation.scene.SceneTransitionLayout
import com.android.compose.animation.scene.rememberMutableSceneTransitionLayoutState
import com.android.compose.animation.scene.transitions
+import com.android.compose.gesture.effect.rememberOffsetOverscrollEffect
import com.android.compose.ui.graphics.painter.rememberDrawablePainter
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.common.ui.compose.Icon
@@ -137,7 +143,9 @@ import com.android.systemui.media.remedia.ui.viewmodel.MediaNavigationViewModel
import com.android.systemui.media.remedia.ui.viewmodel.MediaOutputSwitcherChipViewModel
import com.android.systemui.media.remedia.ui.viewmodel.MediaPlayPauseActionViewModel
import com.android.systemui.media.remedia.ui.viewmodel.MediaSecondaryActionViewModel
+import com.android.systemui.media.remedia.ui.viewmodel.MediaSettingsButtonViewModel
import com.android.systemui.media.remedia.ui.viewmodel.MediaViewModel
+import kotlin.math.abs
import kotlin.math.max
import kotlin.math.min
@@ -208,43 +216,16 @@ private fun CardCarouselContent(
onDismissed: () -> Unit,
modifier: Modifier = Modifier,
) {
- val pagerState =
- rememberDismissibleHorizontalPagerState(
- isDismissible = behavior.isCarouselDismissible,
- isScrollingEnabled = behavior.isCarouselScrollingEnabled,
- ) {
- viewModel.cards.size
- }
+ val pagerState = rememberPagerState { viewModel.cards.size }
+ LaunchedEffect(pagerState.currentPage) { viewModel.onCardSelected(pagerState.currentPage) }
+
var isFalseTouchDetected: Boolean by
remember(behavior.isCarouselScrollFalseTouch) { mutableStateOf(false) }
+ val isSwipingEnabled = behavior.isCarouselScrollingEnabled && !isFalseTouchDetected
val roundedCornerShape = RoundedCornerShape(32.dp)
- LaunchedEffect(pagerState.pagerState.currentPage) {
- viewModel.onCardSelected(pagerState.pagerState.currentPage)
- }
-
- DismissibleHorizontalPager(
- state = pagerState,
- onDismissed = onDismissed,
- pageSpacing = 8.dp,
- key = { index -> viewModel.cards[index].key },
- indicator = {
- if (pagerState.pagerState.pageCount > 1) {
- PagerDots(
- pagerState = pagerState.pagerState,
- activeColor = Color(0xffdee0ff),
- nonActiveColor = Color(0xffa7a9ca),
- dotSize = 6.dp,
- spaceSize = 6.dp,
- modifier =
- Modifier.align(Alignment.BottomCenter).padding(8.dp).graphicsLayer {
- translationX = pagerState.offset.value
- },
- )
- }
- },
- isFalseTouchDetected = isFalseTouchDetected,
+ Box(
modifier =
modifier.padding(8.dp).clip(roundedCornerShape).pointerInput(behavior) {
if (behavior.isCarouselScrollFalseTouch != null) {
@@ -253,13 +234,54 @@ private fun CardCarouselContent(
isFalseTouchDetected = behavior.isCarouselScrollFalseTouch.invoke()
}
}
- },
- ) { index ->
- Card(
- viewModel = viewModel.cards[index],
- presentationStyle = presentationStyle,
- modifier = Modifier.clip(roundedCornerShape),
- )
+ }
+ ) {
+ @Composable
+ fun PagerContent(overscrollEffect: OverscrollEffect? = null) {
+ Box {
+ HorizontalPager(
+ state = pagerState,
+ userScrollEnabled = isSwipingEnabled,
+ pageSpacing = 8.dp,
+ key = { index: Int -> viewModel.cards[index].key },
+ overscrollEffect = overscrollEffect ?: rememberOffsetOverscrollEffect(),
+ ) { pageIndex: Int ->
+ Card(
+ viewModel = viewModel.cards[pageIndex],
+ presentationStyle = presentationStyle,
+ modifier = Modifier.clip(roundedCornerShape),
+ )
+ }
+
+ if (pagerState.pageCount > 1) {
+ PagerDots(
+ pagerState = pagerState,
+ activeColor = Color(0xffdee0ff),
+ nonActiveColor = Color(0xffa7a9ca),
+ dotSize = 6.dp,
+ spaceSize = 6.dp,
+ modifier = Modifier.align(Alignment.BottomCenter).padding(8.dp),
+ )
+ }
+ }
+ }
+
+ if (behavior.isCarouselDismissible) {
+ SwipeToDismiss(content = { PagerContent() }, onDismissed = onDismissed)
+ } else {
+ val overscrollEffect = rememberOffsetOverscrollEffect()
+ SwipeToReveal(
+ foregroundContent = { PagerContent(overscrollEffect) },
+ foregroundContentEffect = overscrollEffect,
+ revealedContent = { revealAmount ->
+ RevealedContent(
+ viewModel = viewModel.settingsButtonViewModel,
+ revealAmount = revealAmount,
+ )
+ },
+ isSwipingEnabled = isSwipingEnabled,
+ )
+ }
}
}
@@ -496,16 +518,19 @@ private fun ContentScope.CardForegroundContent(
modifier = Modifier.weight(1f).padding(end = 8.dp),
)
+ val playPauseSize = DpSize(width = 48.dp, height = 48.dp)
if (viewModel.actionButtonLayout == MediaCardActionButtonLayout.WithPlayPause) {
AnimatedVisibility(visible = viewModel.playPauseAction != null) {
PlayPauseAction(
viewModel = checkNotNull(viewModel.playPauseAction),
- buttonWidth = 48.dp,
+ buttonSize = playPauseSize,
buttonColor = colorScheme.primary,
iconColor = colorScheme.onPrimary,
buttonCornerRadius = { isPlaying -> if (isPlaying) 16.dp else 48.dp },
)
}
+ } else {
+ Spacer(Modifier.size(playPauseSize))
}
}
@@ -565,14 +590,19 @@ private fun ContentScope.CardForegroundContent(
}
}
- AnimatedVisibility(visible = viewModel.playPauseAction != null) {
- PlayPauseAction(
- viewModel = checkNotNull(viewModel.playPauseAction),
- buttonWidth = 48.dp,
- buttonColor = colorScheme.primary,
- iconColor = colorScheme.onPrimary,
- buttonCornerRadius = { isPlaying -> if (isPlaying) 16.dp else 48.dp },
- )
+ val playPauseSize = DpSize(width = 48.dp, height = 48.dp)
+ if (viewModel.actionButtonLayout == MediaCardActionButtonLayout.WithPlayPause) {
+ AnimatedVisibility(visible = viewModel.playPauseAction != null) {
+ PlayPauseAction(
+ viewModel = checkNotNull(viewModel.playPauseAction),
+ buttonSize = playPauseSize,
+ buttonColor = colorScheme.primary,
+ iconColor = colorScheme.onPrimary,
+ buttonCornerRadius = { isPlaying -> if (isPlaying) 16.dp else 48.dp },
+ )
+ }
+ } else {
+ Spacer(Modifier.size(playPauseSize))
}
}
}
@@ -628,7 +658,7 @@ private fun ContentScope.CompactCardForeground(
AnimatedVisibility(visible = viewModel.playPauseAction != null) {
PlayPauseAction(
viewModel = checkNotNull(viewModel.playPauseAction),
- buttonWidth = 72.dp,
+ buttonSize = DpSize(width = 72.dp, height = 48.dp),
buttonColor = MaterialTheme.colorScheme.primaryContainer,
iconColor = MaterialTheme.colorScheme.onPrimaryContainer,
buttonCornerRadius = { isPlaying -> if (isPlaying) 16.dp else 24.dp },
@@ -1084,7 +1114,7 @@ private fun OutputSwitcherChip(
@Composable
private fun ContentScope.PlayPauseAction(
viewModel: MediaPlayPauseActionViewModel,
- buttonWidth: Dp,
+ buttonSize: DpSize,
buttonColor: Color,
iconColor: Color,
buttonCornerRadius: (isPlaying: Boolean) -> Dp,
@@ -1102,7 +1132,7 @@ private fun ContentScope.PlayPauseAction(
enabled = viewModel.onClick != null,
colors = ButtonDefaults.buttonColors(containerColor = buttonColor),
shape = RoundedCornerShape(cornerRadius),
- modifier = Modifier.size(width = buttonWidth, height = 48.dp),
+ modifier = Modifier.size(buttonSize),
) {
when (viewModel.state) {
is MediaSessionState.Playing,
@@ -1186,6 +1216,65 @@ private fun SecondaryActionContent(
}
}
+/**
+ * Renders the revealed content on the sides of the horizontal pager.
+ *
+ * @param revealAmount A callback that can return the amount of revealing done. This value will be
+ * in a range slightly larger than `-1` to `+1` where `1` is fully revealed on the left-hand side,
+ * `-1` is fully revealed on the right-hand side, and `0` is not revealed at all. Numbers lower
+ * than `-1` or greater than `1` are possible when the overscroll effect adds additional pixels of
+ * offset.
+ */
+@Composable
+private fun RevealedContent(
+ viewModel: MediaSettingsButtonViewModel,
+ revealAmount: () -> Float,
+ modifier: Modifier = Modifier,
+) {
+ val horizontalPadding = 18.dp
+
+ // This custom layout's purpose is only to place the icon in the center of the revealed content,
+ // taking into account the amount of reveal.
+ Layout(
+ content = {
+ Icon(
+ icon = viewModel.icon,
+ modifier =
+ Modifier.size(48.dp)
+ .padding(12.dp)
+ .graphicsLayer {
+ alpha = abs(revealAmount()).fastCoerceIn(0f, 1f)
+ rotationZ = revealAmount() * 90
+ }
+ .clickable { viewModel.onClick() },
+ )
+ },
+ modifier = modifier,
+ ) { measurables, constraints ->
+ check(measurables.size == 1)
+ val placeable = measurables[0].measure(constraints)
+ val totalWidth =
+ min(horizontalPadding.roundToPx() * 2 + placeable.measuredWidth, constraints.maxWidth)
+
+ layout(totalWidth, constraints.maxHeight) {
+ coordinates?.size?.let { size ->
+ val reveal = revealAmount()
+ val x =
+ if (reveal >= 0f) {
+ ((size.width * abs(reveal)) - placeable.measuredWidth) / 2
+ } else {
+ size.width * (1 - abs(reveal) / 2) - placeable.measuredWidth / 2
+ }
+
+ placeable.place(
+ x = x.fastRoundToInt(),
+ y = (size.height - placeable.measuredHeight) / 2,
+ )
+ }
+ }
+ }
+}
+
/** Enumerates all supported media presentation styles. */
enum class MediaPresentationStyle {
/** The "normal" 3-row carousel look. */
diff --git a/packages/SystemUI/src/com/android/systemui/media/remedia/ui/compose/SwipeToDismiss.kt b/packages/SystemUI/src/com/android/systemui/media/remedia/ui/compose/SwipeToDismiss.kt
new file mode 100644
index 000000000000..b80bf4143252
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/media/remedia/ui/compose/SwipeToDismiss.kt
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2025 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.media.remedia.ui.compose
+
+import androidx.compose.animation.core.Animatable
+import androidx.compose.foundation.OverscrollEffect
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.offset
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.rememberCoroutineScope
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
+import androidx.compose.ui.input.nestedscroll.NestedScrollSource
+import androidx.compose.ui.input.nestedscroll.nestedScroll
+import androidx.compose.ui.layout.onSizeChanged
+import androidx.compose.ui.unit.IntOffset
+import androidx.compose.ui.unit.Velocity
+import androidx.compose.ui.util.fastRoundToInt
+import kotlinx.coroutines.launch
+
+/** Swipe to dismiss that supports nested scrolling. */
+@Composable
+fun SwipeToDismiss(
+ content: @Composable (overscrollEffect: OverscrollEffect?) -> Unit,
+ onDismissed: () -> Unit,
+ modifier: Modifier = Modifier,
+) {
+ val scope = rememberCoroutineScope()
+ val offsetAnimatable = remember { Animatable(0f) }
+
+ // This is the width of the revealed content UI box. It's not a state because it's not
+ // observed in any composition and is an object with a value to avoid the extra cost
+ // associated with boxing and unboxing an int.
+ val revealedContentBoxWidth = remember {
+ object {
+ var value = 0
+ }
+ }
+
+ val nestedScrollConnection = remember {
+ object : NestedScrollConnection {
+ override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset {
+ return if (offsetAnimatable.value > 0f && available.x < 0f) {
+ scope.launch { offsetAnimatable.snapTo(offsetAnimatable.value + available.x) }
+ Offset(available.x, 0f)
+ } else if (offsetAnimatable.value < 0f && available.x > 0f) {
+ scope.launch { offsetAnimatable.snapTo(offsetAnimatable.value + available.x) }
+ Offset(available.x, 0f)
+ } else {
+ Offset.Zero
+ }
+ }
+
+ override fun onPostScroll(
+ consumed: Offset,
+ available: Offset,
+ source: NestedScrollSource,
+ ): Offset {
+ return if (available.x > 0f) {
+ scope.launch { offsetAnimatable.snapTo(offsetAnimatable.value + available.x) }
+ Offset(available.x, 0f)
+ } else if (available.x < 0f) {
+ scope.launch { offsetAnimatable.snapTo(offsetAnimatable.value + available.x) }
+ Offset(available.x, 0f)
+ } else {
+ Offset.Zero
+ }
+ }
+
+ override suspend fun onPostFling(consumed: Velocity, available: Velocity): Velocity {
+ scope.launch {
+ offsetAnimatable.animateTo(
+ if (offsetAnimatable.value >= revealedContentBoxWidth.value / 2f) {
+ revealedContentBoxWidth.value * 2f
+ } else if (offsetAnimatable.value <= -revealedContentBoxWidth.value / 2f) {
+ -revealedContentBoxWidth.value * 2f
+ } else {
+ 0f
+ }
+ )
+ if (offsetAnimatable.value != 0f) {
+ onDismissed()
+ }
+ }
+ return super.onPostFling(consumed, available)
+ }
+ }
+ }
+
+ Box(
+ modifier =
+ modifier
+ .onSizeChanged { revealedContentBoxWidth.value = it.width }
+ .nestedScroll(nestedScrollConnection)
+ .offset { IntOffset(x = offsetAnimatable.value.fastRoundToInt(), y = 0) }
+ ) {
+ content(null)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/media/remedia/ui/compose/SwipeToReveal.kt b/packages/SystemUI/src/com/android/systemui/media/remedia/ui/compose/SwipeToReveal.kt
new file mode 100644
index 000000000000..770762c7a29f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/media/remedia/ui/compose/SwipeToReveal.kt
@@ -0,0 +1,264 @@
+/*
+ * Copyright (C) 2025 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.media.remedia.ui.compose
+
+import androidx.compose.animation.core.Animatable
+import androidx.compose.foundation.OverscrollEffect
+import androidx.compose.foundation.gestures.Orientation
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.BoxScope
+import androidx.compose.foundation.layout.absoluteOffset
+import androidx.compose.foundation.layout.offset
+import androidx.compose.foundation.overscroll
+import androidx.compose.foundation.withoutVisualEffect
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.rememberCoroutineScope
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.input.pointer.PointerType
+import androidx.compose.ui.layout.Layout
+import androidx.compose.ui.platform.LocalDensity
+import androidx.compose.ui.unit.IntOffset
+import androidx.compose.ui.util.fastCoerceIn
+import androidx.compose.ui.util.fastRoundToInt
+import com.android.compose.gesture.NestedDraggable
+import com.android.compose.gesture.effect.OffsetOverscrollEffect
+import com.android.compose.gesture.effect.rememberOffsetOverscrollEffect
+import com.android.compose.gesture.nestedDraggable
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.launch
+
+/**
+ * Swipe to reveal that supports nested scrolling and an overscroll effect.
+ *
+ * @param foregroundContent The content to show above all else; this is the content that can be
+ * swiped sideways to reveal the [revealedContent]. This may contain a horizontally-scrollable
+ * component (for example a `HorizontalPager`).
+ * @param revealedContent The content that is shown below the [foregroundContent]; this is the
+ * content that can be revealed by swiping the [foregroundContent] sideways.
+ */
+@Composable
+fun SwipeToReveal(
+ foregroundContent: @Composable (overscrollEffect: OverscrollEffect?) -> Unit,
+ foregroundContentEffect: OffsetOverscrollEffect,
+ revealedContent: @Composable BoxScope.(revealAmount: () -> Float) -> Unit,
+ isSwipingEnabled: Boolean,
+ modifier: Modifier = Modifier,
+) {
+ // This composable supports an overscroll effect, to make it possible for the user to
+ // "stretch" the UI when the side is fully revealed but the user keeps trying to reveal it
+ // further.
+ val revealedContentEffect = rememberOffsetOverscrollEffect()
+
+ // This is the width of the revealed content UI box. It's not a state because it's not
+ // observed in any composition and is an object with a value to avoid the extra cost
+ // associated with boxing and unboxing an int.
+ val revealedContentBoxWidth = remember {
+ object {
+ var value = 0
+ }
+ }
+
+ // In order to support the drag to reveal, infrastructure has to be put in place where a
+ // NestedDraggable helps by consuming the unconsumed drags and flings and applying the
+ // overscroll visual effect.
+ //
+ // This is the NestedDraggalbe controller.
+ val revealedContentDragController = rememberRevealedContentDragController {
+ revealedContentBoxWidth.value.toFloat()
+ }
+
+ Box(
+ modifier =
+ modifier
+ .nestedDraggable(
+ enabled = isSwipingEnabled,
+ draggable =
+ remember {
+ object : NestedDraggable {
+ override fun onDragStarted(
+ position: Offset,
+ sign: Float,
+ pointersDown: Int,
+ pointerType: PointerType?,
+ ): NestedDraggable.Controller {
+ return revealedContentDragController
+ }
+
+ override fun shouldConsumeNestedPostScroll(sign: Float): Boolean {
+ return revealedContentDragController.shouldConsumePostScrolls(
+ sign
+ )
+ }
+
+ override fun shouldConsumeNestedPreScroll(sign: Float): Boolean {
+ return revealedContentDragController.shouldConsumePreScrolls(
+ sign
+ )
+ }
+ }
+ },
+ orientation = Orientation.Horizontal,
+ overscrollEffect = revealedContentEffect.withoutVisualEffect(),
+ )
+ .overscroll(revealedContentEffect)
+ ) {
+ val density = LocalDensity.current
+
+ /**
+ * Returns the amount of visual offset, in pixels, that is comprised of both the offset from
+ * dragging and the overscroll effect's additional pixels after applying its animation curve
+ * on the raw distance.
+ */
+ fun offsetWithOverscroll(): Float {
+ return revealedContentDragController.offset +
+ OffsetOverscrollEffect.computeOffset(
+ density,
+ foregroundContentEffect.overscrollDistance,
+ ) +
+ OffsetOverscrollEffect.computeOffset(
+ density,
+ revealedContentEffect.overscrollDistance,
+ )
+ }
+
+ /**
+ * Returns the ratio of the amount by which the revealed content is revealed, where:
+ * - `0` means none of it is revealed
+ * - `+1` means all of it is revealed to the start of the foreground content
+ * - `-1` means all of it is revealed to the end of the foreground content
+ *
+ * The number could be smaller than `-1` or larger than `+1` to model overscrolling.
+ */
+ fun revealAmount(): Float {
+ return (offsetWithOverscroll() / revealedContentBoxWidth.value)
+ }
+
+ Layout(
+ content = { revealedContent { revealAmount() } },
+ modifier = Modifier.matchParentSize(),
+ ) { measurables, constraints ->
+ check(measurables.size == 1)
+ val placeable = measurables[0].measure(constraints.copy(minWidth = 0, minHeight = 0))
+ // Keep revealedContentBoxWidth up to date with the latest value.
+ revealedContentBoxWidth.value = placeable.measuredWidth
+
+ // Place the revealed content on the correct side, depending on the direction of the
+ // reveal.
+ val alignedToStart = revealAmount() >= 0f
+ layout(constraints.maxWidth, constraints.maxHeight) {
+ coordinates?.size?.let { size ->
+ placeable.place(
+ x = if (alignedToStart) 0 else size.width - placeable.measuredWidth,
+ y = 0,
+ )
+ }
+ }
+ }
+
+ Box(
+ modifier =
+ Modifier.absoluteOffset {
+ IntOffset(revealedContentDragController.offset.fastRoundToInt(), y = 0)
+ }
+ ) {
+ foregroundContent(foregroundContentEffect)
+ }
+ }
+}
+
+@Composable
+private fun rememberRevealedContentDragController(
+ maxBound: () -> Float
+): RevealedContentDragController {
+ val scope = rememberCoroutineScope()
+ return remember { RevealedContentDragController(scope = scope, maxBound = maxBound) }
+}
+
+private class RevealedContentDragController(
+ private val scope: CoroutineScope,
+ private val maxBound: () -> Float,
+) : NestedDraggable.Controller {
+ private val offsetAnimatable = Animatable(0f)
+ private var lastTarget = 0f
+ private var range = 0f..1f
+ private var shouldConsumePreScrolls by mutableStateOf(false)
+
+ override val autoStopNestedDrags: Boolean
+ get() = true
+
+ val offset: Float
+ get() = offsetAnimatable.value
+
+ fun shouldConsumePreScrolls(sign: Float): Boolean {
+ if (!shouldConsumePreScrolls) return false
+
+ if (lastTarget > 0f && sign < 0f) {
+ range = 0f..maxBound()
+ return true
+ }
+
+ if (lastTarget < 0f && sign > 0f) {
+ range = -maxBound()..0f
+ return true
+ }
+
+ return false
+ }
+
+ fun shouldConsumePostScrolls(sign: Float): Boolean {
+ val max = maxBound()
+ if (sign > 0f && lastTarget < max) {
+ range = 0f..maxBound()
+ return true
+ }
+
+ if (sign < 0f && lastTarget > -max) {
+ range = -maxBound()..0f
+ return true
+ }
+
+ return false
+ }
+
+ override fun onDrag(delta: Float): Float {
+ val previousTarget = lastTarget
+ lastTarget = (lastTarget + delta).fastCoerceIn(range.start, range.endInclusive)
+ val newTarget = lastTarget
+ scope.launch { offsetAnimatable.snapTo(newTarget) }
+ return lastTarget - previousTarget
+ }
+
+ override suspend fun onDragStopped(velocity: Float, awaitFling: suspend () -> Unit): Float {
+ val rangeMiddle = range.start + (range.endInclusive - range.start) / 2f
+ lastTarget =
+ when {
+ lastTarget >= rangeMiddle -> range.endInclusive
+ else -> range.start
+ }
+
+ shouldConsumePreScrolls = lastTarget != 0f
+ val newTarget = lastTarget
+
+ scope.launch { offsetAnimatable.animateTo(newTarget) }
+ return velocity
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/media/remedia/ui/viewmodel/MediaSettingsButtonViewModel.kt b/packages/SystemUI/src/com/android/systemui/media/remedia/ui/viewmodel/MediaSettingsButtonViewModel.kt
new file mode 100644
index 000000000000..4f4c25c3c74f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/media/remedia/ui/viewmodel/MediaSettingsButtonViewModel.kt
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2025 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.media.remedia.ui.viewmodel
+
+import com.android.systemui.common.shared.model.Icon
+
+data class MediaSettingsButtonViewModel(val icon: Icon.Resource, val onClick: () -> Unit)
diff --git a/packages/SystemUI/src/com/android/systemui/media/remedia/ui/viewmodel/MediaViewModel.kt b/packages/SystemUI/src/com/android/systemui/media/remedia/ui/viewmodel/MediaViewModel.kt
index 19b08fa212db..a57da63a7a81 100644
--- a/packages/SystemUI/src/com/android/systemui/media/remedia/ui/viewmodel/MediaViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/remedia/ui/viewmodel/MediaViewModel.kt
@@ -252,6 +252,21 @@ constructor(
}
}
+ val settingsButtonViewModel =
+ MediaSettingsButtonViewModel(
+ icon =
+ Icon.Resource(
+ res = R.drawable.ic_settings,
+ contentDescription =
+ ContentDescription.Resource(res = R.string.controls_media_settings_button),
+ ),
+ onClick = {
+ falsingSystem.runIfNotFalseTap(FalsingManager.LOW_PENALTY) {
+ interactor.openMediaSettings()
+ }
+ },
+ )
+
/** Whether the carousel should be visible. */
val isCarouselVisible: Boolean
get() =
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt
index 2a23620839e5..6a2c4519e1a5 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt
@@ -35,7 +35,6 @@ import android.view.ViewGroup
import android.view.WindowManager
import android.view.accessibility.AccessibilityManager
import com.android.app.animation.Interpolators
-import com.android.app.viewcapture.ViewCaptureAwareWindowManager
import com.android.internal.logging.InstanceId
import com.android.internal.widget.CachingIconView
import com.android.systemui.common.shared.model.ContentDescription
@@ -73,7 +72,7 @@ constructor(
private val commandQueue: CommandQueue,
context: Context,
logger: MediaTttReceiverLogger,
- viewCaptureAwareWindowManager: ViewCaptureAwareWindowManager,
+ windowManager: WindowManager,
@Main private val mainExecutor: DelayableExecutor,
accessibilityManager: AccessibilityManager,
configurationController: ConfigurationController,
@@ -90,7 +89,7 @@ constructor(
TemporaryViewDisplayController<ChipReceiverInfo, MediaTttReceiverLogger>(
context,
logger,
- viewCaptureAwareWindowManager,
+ windowManager,
mainExecutor,
accessibilityManager,
configurationController,
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/MediaProjectionTaskView.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/MediaProjectionTaskView.kt
index 9265bfb2f66b..5ffa7fa0ef8d 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/MediaProjectionTaskView.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/MediaProjectionTaskView.kt
@@ -26,13 +26,13 @@ import android.graphics.Shader
import android.util.AttributeSet
import android.view.View
import android.view.WindowManager
-import androidx.core.content.getSystemService
import androidx.core.content.res.use
import com.android.systemui.res.R
import com.android.systemui.mediaprojection.appselector.data.RecentTask
import com.android.systemui.shared.recents.model.ThumbnailData
import com.android.systemui.shared.recents.utilities.PreviewPositionHelper
import com.android.systemui.shared.recents.utilities.Utilities.isLargeScreen
+import com.android.systemui.utils.windowmanager.WindowManagerUtils
/**
* Custom view that shows a thumbnail preview of one recent task based on [ThumbnailData].
@@ -53,7 +53,7 @@ constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
}
}
- private val windowManager: WindowManager = context.getSystemService()!!
+ private val windowManager: WindowManager = WindowManagerUtils.getWindowManager(context)
private val paint = Paint(Paint.ANTI_ALIAS_FLAG)
private val backgroundPaint =
Paint(Paint.ANTI_ALIAS_FLAG).apply { color = defaultBackgroundColor }
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/TaskPreviewSizeProvider.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/TaskPreviewSizeProvider.kt
index c829471f53f3..57fd1e790f0b 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/TaskPreviewSizeProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/TaskPreviewSizeProvider.kt
@@ -19,6 +19,7 @@ package com.android.systemui.mediaprojection.appselector.view
import android.content.Context
import android.content.res.Configuration
import android.graphics.Rect
+import android.view.WindowManager
import androidx.lifecycle.DefaultLifecycleObserver
import androidx.lifecycle.LifecycleOwner
import com.android.systemui.mediaprojection.appselector.MediaProjectionAppSelectorScope
@@ -36,6 +37,7 @@ constructor(
private val context: Context,
private val windowMetricsProvider: WindowMetricsProvider,
private val configurationController: ConfigurationController,
+ private val windowManager: WindowManager,
) : CallbackController<TaskPreviewSizeListener>, ConfigurationListener, DefaultLifecycleObserver {
/** Returns the size of the task preview on the screen in pixels */
@@ -65,7 +67,7 @@ constructor(
val width = maxWindowBounds.width()
var height = maximumWindowHeight
- val isLargeScreen = isLargeScreen(context)
+ val isLargeScreen = isLargeScreen(windowManager, context.resources)
if (isLargeScreen) {
val taskbarSize = windowMetricsProvider.currentWindowInsets.bottom
height -= taskbarSize
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/BaseMediaProjectionPermissionViewBinder.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/BaseMediaProjectionPermissionViewBinder.kt
index c6e4db7af2d9..324a3efc2989 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/BaseMediaProjectionPermissionViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/BaseMediaProjectionPermissionViewBinder.kt
@@ -150,6 +150,13 @@ private class OptionsAdapter(context: Context, private val options: List<ScreenS
titleTextView.isEnabled = true
} else {
errorTextView.visibility = View.VISIBLE
+ if (com.android.systemui.Flags.mediaProjectionGreyErrorText()) {
+ errorTextView.isEnabled = false
+ errorTextView.setTextColor(context.getColorStateList(R.color.menu_item_text))
+ errorTextView.setText(
+ R.string.media_projection_entry_app_permission_dialog_single_app_not_supported
+ )
+ }
titleTextView.isEnabled = false
}
return view
diff --git a/packages/SystemUI/src/com/android/systemui/model/SceneContainerPlugin.kt b/packages/SystemUI/src/com/android/systemui/model/SceneContainerPlugin.kt
index 4559a7aea1a2..7b3f4c61088b 100644
--- a/packages/SystemUI/src/com/android/systemui/model/SceneContainerPlugin.kt
+++ b/packages/SystemUI/src/com/android/systemui/model/SceneContainerPlugin.kt
@@ -79,6 +79,7 @@ constructor(
SceneContainerPluginState(
scene = idleState.currentScene,
overlays = idleState.currentOverlays,
+ isVisible = sceneInteractor.get().isVisible.value,
invisibleDueToOcclusion = invisibleDueToOcclusion,
)
)
@@ -100,12 +101,17 @@ constructor(
mapOf<Long, (SceneContainerPluginState) -> Boolean>(
SYSUI_STATE_NOTIFICATION_PANEL_VISIBLE to
{
- it.scene != Scenes.Gone || it.overlays.isNotEmpty()
+ when {
+ !it.isVisible -> false
+ it.scene != Scenes.Gone -> true
+ it.overlays.isNotEmpty() -> true
+ else -> false
+ }
},
SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED to
{
when {
- it.invisibleDueToOcclusion -> false
+ !it.isVisible -> false
it.scene == Scenes.Lockscreen -> true
it.scene == Scenes.Shade -> true
Overlays.NotificationsShade in it.overlays -> true
@@ -114,19 +120,23 @@ constructor(
},
SYSUI_STATE_QUICK_SETTINGS_EXPANDED to
{
- it.scene == Scenes.QuickSettings ||
- Overlays.QuickSettingsShade in it.overlays
+ when {
+ !it.isVisible -> false
+ it.scene == Scenes.QuickSettings -> true
+ Overlays.QuickSettingsShade in it.overlays -> true
+ else -> false
+ }
},
- SYSUI_STATE_BOUNCER_SHOWING to { Overlays.Bouncer in it.overlays },
+ SYSUI_STATE_BOUNCER_SHOWING to { it.isVisible && Overlays.Bouncer in it.overlays },
SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING to
{
- it.scene == Scenes.Lockscreen && !it.invisibleDueToOcclusion
+ it.isVisible && it.scene == Scenes.Lockscreen
},
SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED to
{
it.scene == Scenes.Lockscreen && it.invisibleDueToOcclusion
},
- SYSUI_STATE_COMMUNAL_HUB_SHOWING to { it.scene == Scenes.Communal },
+ SYSUI_STATE_COMMUNAL_HUB_SHOWING to { it.isVisible && it.scene == Scenes.Communal },
)
}
@@ -134,5 +144,6 @@ constructor(
val scene: SceneKey,
val overlays: Set<OverlayKey>,
val invisibleDueToOcclusion: Boolean,
+ val isVisible: Boolean,
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/model/SysUiState.kt b/packages/SystemUI/src/com/android/systemui/model/SysUiState.kt
index 71cb74543485..68cd80798c4b 100644
--- a/packages/SystemUI/src/com/android/systemui/model/SysUiState.kt
+++ b/packages/SystemUI/src/com/android/systemui/model/SysUiState.kt
@@ -17,9 +17,9 @@ package com.android.systemui.model
import android.util.Log
import android.view.Display
+import com.android.app.displaylib.PerDisplayInstanceProviderWithTeardown
import com.android.systemui.Dumpable
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.display.data.repository.PerDisplayInstanceProviderWithTeardown
import com.android.systemui.dump.DumpManager
import com.android.systemui.model.SysUiState.SysUiStateCallback
import com.android.systemui.shade.shared.flag.ShadeWindowGoesAround
diff --git a/packages/SystemUI/src/com/android/systemui/model/SysUiStateExt.kt b/packages/SystemUI/src/com/android/systemui/model/SysUiStateExt.kt
index 1e18f24c9e65..195535669c7e 100644
--- a/packages/SystemUI/src/com/android/systemui/model/SysUiStateExt.kt
+++ b/packages/SystemUI/src/com/android/systemui/model/SysUiStateExt.kt
@@ -16,8 +16,6 @@
package com.android.systemui.model
-import com.android.systemui.dagger.qualifiers.DisplayId
-
/**
* In-bulk updates multiple flag values and commits the update.
*
@@ -32,16 +30,8 @@ import com.android.systemui.dagger.qualifiers.DisplayId
* SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING to (sceneKey == Scenes.Lockscreen),
* )
* ```
- *
- * You can inject [displayId] by injecting it using:
- * ```
- * @DisplayId private val displayId: Int`,
- * ```
*/
-fun SysUiState.updateFlags(
- @DisplayId displayId: Int,
- vararg flagValuePairs: Pair<Long, Boolean>,
-) {
+fun SysUiState.updateFlags(vararg flagValuePairs: Pair<Long, Boolean>) {
flagValuePairs.forEach { (flag, enabled) -> setFlag(flag, enabled) }
- commitUpdate(displayId)
+ commitUpdate()
}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarModule.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarModule.java
index 58ddbf60e8fb..c5c8c01f8b39 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarModule.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarModule.java
@@ -16,26 +16,21 @@
package com.android.systemui.navigationbar;
-import static com.android.systemui.Flags.enableViewCaptureTracing;
-import static com.android.systemui.util.ConvenienceExtensionsKt.toKotlinLazy;
-
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.WindowManager;
-import com.android.app.viewcapture.ViewCapture;
-import com.android.app.viewcapture.ViewCaptureAwareWindowManager;
+import com.android.app.displaylib.PerDisplayRepository;
import com.android.systemui.dagger.qualifiers.DisplayId;
-import com.android.systemui.display.data.repository.PerDisplayRepository;
import com.android.systemui.model.SysUiState;
import com.android.systemui.navigationbar.NavigationBarComponent.NavigationBarScope;
import com.android.systemui.navigationbar.views.NavigationBarFrame;
import com.android.systemui.navigationbar.views.NavigationBarView;
import com.android.systemui.res.R;
import com.android.systemui.shade.shared.flag.ShadeWindowGoesAround;
+import com.android.systemui.utils.windowmanager.WindowManagerProvider;
-import dagger.Lazy;
import dagger.Module;
import dagger.Provides;
@@ -70,8 +65,9 @@ public interface NavigationBarModule {
@Provides
@NavigationBarScope
@DisplayId
- static WindowManager provideWindowManager(@DisplayId Context context) {
- return context.getSystemService(WindowManager.class);
+ static WindowManager provideWindowManager(@DisplayId Context context,
+ WindowManagerProvider windowManagerProvider) {
+ return windowManagerProvider.getWindowManager(context);
}
/** A SysUiState for the navigation bar display. */
@@ -87,15 +83,4 @@ public interface NavigationBarModule {
return defaultState;
}
}
-
- /** A ViewCaptureAwareWindowManager specific to the display's context. */
- @Provides
- @NavigationBarScope
- @DisplayId
- static ViewCaptureAwareWindowManager provideViewCaptureAwareWindowManager(
- @DisplayId WindowManager windowManager, Lazy<ViewCapture> daggerLazyViewCapture) {
- return new ViewCaptureAwareWindowManager(windowManager,
- /* lazyViewCapture= */ toKotlinLazy(daggerLazyViewCapture),
- /* isViewCaptureEnabled= */ enableViewCaptureTracing());
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt
index 44c828731e24..6d7492686985 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt
@@ -33,7 +33,6 @@ import androidx.annotation.VisibleForTesting
import androidx.core.os.postDelayed
import androidx.core.view.isVisible
import androidx.dynamicanimation.animation.DynamicAnimation
-import com.android.app.viewcapture.ViewCaptureAwareWindowManager
import com.android.internal.jank.Cuj
import com.android.internal.jank.InteractionJankMonitor
import com.android.internal.util.LatencyTracker
@@ -85,7 +84,7 @@ class BackPanelController
@AssistedInject
constructor(
@Assisted context: Context,
- private val windowManager: ViewCaptureAwareWindowManager,
+ private val windowManager: WindowManager,
private val viewConfiguration: ViewConfiguration,
@Assisted private val mainHandler: Handler,
private val systemClock: SystemClock,
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
index 237ec7cfce7f..6cda192c4198 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
@@ -22,6 +22,7 @@ import static android.view.MotionEvent.TOOL_TYPE_FINGER;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_EXCLUDE_FROM_SCREEN_MAGNIFICATION;
import static com.android.systemui.Flags.edgebackGestureHandlerGetRunningTasksBackground;
+import static com.android.systemui.Flags.predictiveBackDelayWmTransition;
import static com.android.systemui.classifier.Classifier.BACK_GESTURE;
import static com.android.systemui.navigationbar.gestural.Utilities.isTrackpadThreeFingerSwipe;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_TOUCHPAD_GESTURES_DISABLED;
@@ -1182,6 +1183,9 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack
return;
} else if (dx > dy && dx > mTouchSlop) {
if (mAllowGesture) {
+ if (!predictiveBackDelayWmTransition() && mBackAnimation != null) {
+ mBackAnimation.onThresholdCrossed();
+ }
if (mBackAnimation == null) {
pilferPointers();
}
@@ -1197,7 +1201,8 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack
// forward touch
mEdgeBackPlugin.onMotionEvent(ev);
dispatchToBackAnimation(ev);
- if (mBackAnimation != null && mThresholdCrossed && !mLastFrameThresholdCrossed) {
+ if (predictiveBackDelayWmTransition() && mBackAnimation != null
+ && mThresholdCrossed && !mLastFrameThresholdCrossed) {
mBackAnimation.onThresholdCrossed();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/views/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/views/NavigationBar.java
index 8b5b3adeef1f..ad0acbdaf702 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/views/NavigationBar.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/views/NavigationBar.java
@@ -104,7 +104,6 @@ import android.view.inputmethod.InputMethodManager;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
-import com.android.app.viewcapture.ViewCaptureAwareWindowManager;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.UiEvent;
import com.android.internal.logging.UiEventLogger;
@@ -199,7 +198,6 @@ public class NavigationBar extends ViewController<NavigationBarView> implements
private final Context mContext;
private final Bundle mSavedState;
private final WindowManager mWindowManager;
- private final ViewCaptureAwareWindowManager mViewCaptureAwareWindowManager;
private final AccessibilityManager mAccessibilityManager;
private final DeviceProvisionedController mDeviceProvisionedController;
private final StatusBarStateController mStatusBarStateController;
@@ -560,7 +558,6 @@ public class NavigationBar extends ViewController<NavigationBarView> implements
@Nullable Bundle savedState,
@DisplayId Context context,
@DisplayId WindowManager windowManager,
- @DisplayId ViewCaptureAwareWindowManager viewCaptureAwareWindowManager,
Lazy<AssistManager> assistManagerLazy,
AccessibilityManager accessibilityManager,
DeviceProvisionedController deviceProvisionedController,
@@ -605,7 +602,6 @@ public class NavigationBar extends ViewController<NavigationBarView> implements
mContext = context;
mSavedState = savedState;
mWindowManager = windowManager;
- mViewCaptureAwareWindowManager = viewCaptureAwareWindowManager;
mAccessibilityManager = accessibilityManager;
mDeviceProvisionedController = deviceProvisionedController;
mStatusBarStateController = statusBarStateController;
@@ -726,7 +722,7 @@ public class NavigationBar extends ViewController<NavigationBarView> implements
if (DEBUG) Log.v(TAG, "addNavigationBar: about to add " + mView);
try {
- mViewCaptureAwareWindowManager.addView(
+ mWindowManager.addView(
mFrame,
getBarLayoutParams(
mContext.getResources()
@@ -783,7 +779,7 @@ public class NavigationBar extends ViewController<NavigationBarView> implements
mCommandQueue.removeCallback(this);
Trace.beginSection("NavigationBar#removeViewImmediate");
try {
- mViewCaptureAwareWindowManager.removeViewImmediate(mView.getRootView());
+ mWindowManager.removeViewImmediate(mView.getRootView());
} catch (IllegalArgumentException e) {
// Wrapping this in a try/catch to avoid crashes when a display is instantly removed
// after being added, and initialization hasn't finished yet.
@@ -888,7 +884,7 @@ public class NavigationBar extends ViewController<NavigationBarView> implements
resetSecondaryHandle();
getBarTransitions().removeDarkIntensityListener(mOrientationHandleIntensityListener);
try {
- mViewCaptureAwareWindowManager.removeView(mOrientationHandle);
+ mWindowManager.removeView(mOrientationHandle);
} catch (IllegalArgumentException e) {
// Wrapping this in a try/catch to avoid crashes when a display is instantly removed
// after being added, and initialization hasn't finished yet.
@@ -967,7 +963,7 @@ public class NavigationBar extends ViewController<NavigationBarView> implements
mOrientationParams.privateFlags |= PRIVATE_FLAG_NO_MOVE_ANIMATION
| WindowManager.LayoutParams.PRIVATE_FLAG_LAYOUT_SIZE_EXTENDED_BY_CUTOUT;
try {
- mViewCaptureAwareWindowManager.addView(mOrientationHandle, mOrientationParams);
+ mWindowManager.addView(mOrientationHandle, mOrientationParams);
} catch (WindowManager.InvalidDisplayException e) {
// Wrapping this in a try/catch to avoid crashes when a display is instantly removed
// after being added, and initialization hasn't finished yet.
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/views/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/navigationbar/views/NavigationBarView.java
index cbc4c26b2f94..d2974e9f90bd 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/views/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/views/NavigationBarView.java
@@ -87,6 +87,7 @@ import com.android.systemui.shared.system.QuickStepContract;
import com.android.systemui.statusbar.phone.AutoHideController;
import com.android.systemui.statusbar.phone.CentralSurfaces;
import com.android.systemui.statusbar.phone.LightBarTransitionsController;
+import com.android.systemui.utils.windowmanager.WindowManagerUtils;
import com.android.wm.shell.back.BackAnimation;
import com.android.wm.shell.pip.Pip;
@@ -729,7 +730,7 @@ public class NavigationBarView extends FrameLayout {
} else {
return;
}
- WindowManager wm = getContext().getSystemService(WindowManager.class);
+ WindowManager wm = WindowManagerUtils.getWindowManager(getContext());
wm.updateViewLayout((View) getParent(), lp);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInitializer.kt b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInitializer.kt
index 8dc27bf4ac3e..080940169e46 100644
--- a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInitializer.kt
+++ b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInitializer.kt
@@ -86,7 +86,10 @@ constructor(
*/
private fun initializeKeyGestureEventHandler() {
if (useKeyGestureEventHandler()) {
- inputManager.registerKeyGestureEventHandler(callbacks)
+ inputManager.registerKeyGestureEventHandler(
+ listOf(KeyGestureEvent.KEY_GESTURE_TYPE_OPEN_NOTES),
+ callbacks,
+ )
}
}
@@ -156,11 +159,8 @@ constructor(
controller.updateNoteTaskForCurrentUserAndManagedProfiles()
}
- override fun handleKeyGestureEvent(
- event: KeyGestureEvent,
- focusedToken: IBinder?,
- ): Boolean {
- return this@NoteTaskInitializer.handleKeyGestureEvent(event)
+ override fun handleKeyGestureEvent(event: KeyGestureEvent, focusedToken: IBinder?) {
+ this@NoteTaskInitializer.handleKeyGestureEvent(event)
}
}
@@ -202,23 +202,19 @@ constructor(
return !isMultiPress && !isLongPress
}
- private fun handleKeyGestureEvent(event: KeyGestureEvent): Boolean {
- // This method is on input hot path and should be kept lightweight. Shift all complex
- // processing onto background executor wherever possible.
+ private fun handleKeyGestureEvent(event: KeyGestureEvent) {
if (event.keyGestureType != KeyGestureEvent.KEY_GESTURE_TYPE_OPEN_NOTES) {
- return false
+ return
}
debugLog {
"handleKeyGestureEvent: Received OPEN_NOTES gesture event from keycodes: " +
event.keycodes.contentToString()
}
if (event.keycodes.size == 1 && event.keycodes[0] == KEYCODE_STYLUS_BUTTON_TAIL) {
- debugLog { "Note task triggered by stylus tail button" }
backgroundExecutor.execute { controller.showNoteTask(TAIL_BUTTON) }
- return true
+ } else {
+ backgroundExecutor.execute { controller.showNoteTask(KEYBOARD_SHORTCUT) }
}
- backgroundExecutor.execute { controller.showNoteTask(KEYBOARD_SHORTCUT) }
- return true
}
companion object {
diff --git a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskModule.kt b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskModule.kt
index 5d5465633f9f..fb3271e57b08 100644
--- a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskModule.kt
@@ -32,16 +32,16 @@ import com.android.systemui.qs.pipeline.shared.TileSpec
import com.android.systemui.qs.shared.model.TileCategory
import com.android.systemui.qs.tileimpl.QSTileImpl
import com.android.systemui.qs.tiles.NotesTile
-import com.android.systemui.qs.tiles.base.interactor.QSTileAvailabilityInteractor
-import com.android.systemui.qs.tiles.base.viewmodel.QSTileViewModelFactory
-import com.android.systemui.qs.tiles.impl.notes.domain.NotesTileMapper
+import com.android.systemui.qs.tiles.base.domain.interactor.QSTileAvailabilityInteractor
+import com.android.systemui.qs.tiles.base.shared.model.QSTileConfig
+import com.android.systemui.qs.tiles.base.shared.model.QSTileUIConfig
+import com.android.systemui.qs.tiles.base.ui.viewmodel.QSTileViewModel
+import com.android.systemui.qs.tiles.base.ui.viewmodel.QSTileViewModelFactory
+import com.android.systemui.qs.tiles.base.ui.viewmodel.StubQSTileViewModel
import com.android.systemui.qs.tiles.impl.notes.domain.interactor.NotesTileDataInteractor
import com.android.systemui.qs.tiles.impl.notes.domain.interactor.NotesTileUserActionInteractor
import com.android.systemui.qs.tiles.impl.notes.domain.model.NotesTileModel
-import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
-import com.android.systemui.qs.tiles.viewmodel.QSTileUIConfig
-import com.android.systemui.qs.tiles.viewmodel.QSTileViewModel
-import com.android.systemui.qs.tiles.viewmodel.StubQSTileViewModel
+import com.android.systemui.qs.tiles.impl.notes.ui.mapper.NotesTileMapper
import com.android.systemui.res.R
import dagger.Binds
import dagger.Module
diff --git a/packages/SystemUI/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayActionsViewModel.kt b/packages/SystemUI/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayActionsViewModel.kt
index 9319961f5b68..0a5222e3df44 100644
--- a/packages/SystemUI/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayActionsViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/notifications/ui/viewmodel/NotificationsShadeOverlayActionsViewModel.kt
@@ -21,8 +21,7 @@ import com.android.compose.animation.scene.Swipe
import com.android.compose.animation.scene.UserAction
import com.android.compose.animation.scene.UserActionResult
import com.android.compose.animation.scene.UserActionResult.HideOverlay
-import com.android.compose.animation.scene.UserActionResult.ShowOverlay
-import com.android.compose.animation.scene.UserActionResult.ShowOverlay.HideCurrentOverlays
+import com.android.compose.animation.scene.UserActionResult.ReplaceByOverlay
import com.android.systemui.scene.shared.model.Overlays
import com.android.systemui.scene.ui.viewmodel.SceneContainerArea
import com.android.systemui.scene.ui.viewmodel.UserActionsViewModel
@@ -38,11 +37,8 @@ class NotificationsShadeOverlayActionsViewModel @AssistedInject constructor() :
mapOf(
Swipe.Up to HideOverlay(Overlays.NotificationsShade),
Back to HideOverlay(Overlays.NotificationsShade),
- Swipe.Down(fromSource = SceneContainerArea.EndHalf) to
- ShowOverlay(
- Overlays.QuickSettingsShade,
- hideCurrentOverlays = HideCurrentOverlays.Some(Overlays.NotificationsShade),
- ),
+ Swipe.Down(fromSource = SceneContainerArea.TopEdgeEndHalf) to
+ ReplaceByOverlay(Overlays.QuickSettingsShade),
)
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/power/InattentiveSleepWarningView.java b/packages/SystemUI/src/com/android/systemui/power/InattentiveSleepWarningView.java
index 2ecca2d8c776..f37a86e2b9d5 100644
--- a/packages/SystemUI/src/com/android/systemui/power/InattentiveSleepWarningView.java
+++ b/packages/SystemUI/src/com/android/systemui/power/InattentiveSleepWarningView.java
@@ -16,8 +16,6 @@
package com.android.systemui.power;
-import static com.android.systemui.Flags.enableViewCaptureTracing;
-
import android.animation.Animator;
import android.animation.AnimatorInflater;
import android.animation.AnimatorListenerAdapter;
@@ -30,28 +28,21 @@ import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.widget.FrameLayout;
-
-import com.android.app.viewcapture.ViewCapture;
-import com.android.app.viewcapture.ViewCaptureAwareWindowManager;
import com.android.systemui.res.R;
-import kotlin.Lazy;
-
/**
* View that shows a warning shortly before the device goes into sleep
* after prolonged user inactivity when bound to.
*/
public class InattentiveSleepWarningView extends FrameLayout {
private final IBinder mWindowToken = new Binder();
- private final ViewCaptureAwareWindowManager mWindowManager;
+ private final WindowManager mWindowManager;
private Animator mFadeOutAnimator;
private boolean mDismissing;
- InattentiveSleepWarningView(Context context, Lazy<ViewCapture> lazyViewCapture) {
+ InattentiveSleepWarningView(Context context, WindowManager windowManager) {
super(context);
- WindowManager wm = mContext.getSystemService(WindowManager.class);
- mWindowManager = new ViewCaptureAwareWindowManager(wm, lazyViewCapture,
- enableViewCaptureTracing());
+ mWindowManager = windowManager;
final LayoutInflater layoutInflater = LayoutInflater.from(mContext);
layoutInflater.inflate(R.layout.inattentive_sleep_warning, this, true /* attachToRoot */);
diff --git a/packages/SystemUI/src/com/android/systemui/power/PowerUI.java b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
index 861a7ce282af..4e1bfd15e58c 100644
--- a/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
+++ b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
@@ -16,8 +16,6 @@
package com.android.systemui.power;
-import static com.android.systemui.util.ConvenienceExtensionsKt.toKotlinLazy;
-
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
import android.content.Context;
@@ -42,11 +40,11 @@ import android.service.vr.IVrStateCallbacks;
import android.text.format.DateUtils;
import android.util.Log;
import android.util.Slog;
+import android.view.WindowManager;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
-import com.android.app.viewcapture.ViewCapture;
import com.android.internal.annotations.VisibleForTesting;
import com.android.settingslib.fuelgauge.Estimate;
import com.android.settingslib.utils.ThreadUtils;
@@ -59,8 +57,6 @@ import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.policy.ConfigurationController;
-import kotlin.Lazy;
-
import java.io.PrintWriter;
import java.util.Arrays;
import java.util.concurrent.Future;
@@ -120,9 +116,9 @@ public class PowerUI implements
private IThermalEventListener mSkinThermalEventListener;
private IThermalEventListener mUsbThermalEventListener;
private final Context mContext;
+ private final WindowManager mWindowManager;
private final BroadcastDispatcher mBroadcastDispatcher;
private final CommandQueue mCommandQueue;
- private final Lazy<ViewCapture> mLazyViewCapture;
@Nullable
private final IVrManager mVrManager;
private final WakefulnessLifecycle.Observer mWakefulnessObserver =
@@ -164,7 +160,7 @@ public class PowerUI implements
WakefulnessLifecycle wakefulnessLifecycle,
PowerManager powerManager,
UserTracker userTracker,
- dagger.Lazy<ViewCapture> daggerLazyViewCapture) {
+ WindowManager windowManager) {
mContext = context;
mBroadcastDispatcher = broadcastDispatcher;
mCommandQueue = commandQueue;
@@ -174,7 +170,7 @@ public class PowerUI implements
mPowerManager = powerManager;
mWakefulnessLifecycle = wakefulnessLifecycle;
mUserTracker = userTracker;
- mLazyViewCapture = toKotlinLazy(daggerLazyViewCapture);
+ mWindowManager = windowManager;
}
public void start() {
@@ -649,7 +645,7 @@ public class PowerUI implements
@Override
public void showInattentiveSleepWarning() {
if (mOverlayView == null) {
- mOverlayView = new InattentiveSleepWarningView(mContext, mLazyViewCapture);
+ mOverlayView = new InattentiveSleepWarningView(mContext, mWindowManager);
}
mOverlayView.show();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt b/packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt
index 05a60a6db31e..9f04f69bd0bf 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt
@@ -18,6 +18,9 @@ package com.android.systemui.qs.composefragment
import android.annotation.SuppressLint
import android.content.Context
+import android.content.res.Configuration
+import android.graphics.Canvas
+import android.graphics.Path
import android.graphics.PointF
import android.graphics.Rect
import android.os.Bundle
@@ -35,6 +38,7 @@ import androidx.activity.setViewTreeOnBackPressedDispatcherOwner
import androidx.annotation.VisibleForTesting
import androidx.compose.animation.core.tween
import androidx.compose.foundation.ScrollState
+import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.foundation.layout.Arrangement.spacedBy
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
@@ -48,6 +52,7 @@ import androidx.compose.foundation.layout.requiredHeightIn
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
@@ -70,6 +75,8 @@ import androidx.compose.ui.layout.onSizeChanged
import androidx.compose.ui.layout.positionInRoot
import androidx.compose.ui.layout.positionOnScreen
import androidx.compose.ui.platform.ComposeView
+import androidx.compose.ui.platform.LocalConfiguration
+import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.dimensionResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.semantics.CustomAccessibilityAction
@@ -102,6 +109,7 @@ import com.android.compose.theme.PlatformTheme
import com.android.mechanics.GestureContext
import com.android.systemui.Dumpable
import com.android.systemui.Flags
+import com.android.systemui.Flags.notificationShadeBlur
import com.android.systemui.brightness.ui.compose.BrightnessSliderContainer
import com.android.systemui.brightness.ui.compose.ContainerColors
import com.android.systemui.compose.modifiers.sysuiResTag
@@ -119,7 +127,6 @@ import com.android.systemui.qs.composefragment.SceneKeys.debugName
import com.android.systemui.qs.composefragment.SceneKeys.toIdleSceneKey
import com.android.systemui.qs.composefragment.ui.GridAnchor
import com.android.systemui.qs.composefragment.ui.NotificationScrimClipParams
-import com.android.systemui.qs.composefragment.ui.notificationScrimClip
import com.android.systemui.qs.composefragment.ui.quickQuickSettingsToQuickSettings
import com.android.systemui.qs.composefragment.ui.toEditMode
import com.android.systemui.qs.composefragment.viewmodel.QSFragmentComposeViewModel
@@ -235,7 +242,7 @@ constructor(
FrameLayoutTouchPassthrough(
context,
{ notificationScrimClippingParams.isEnabled },
- { notificationScrimClippingParams.params.top },
+ snapshotFlow { notificationScrimClippingParams.params },
// Only allow scrolling when we are fully expanded. That way, we don't intercept
// swipes in lockscreen (when somehow QS is receiving touches).
{ (scrollState.canScrollForward && viewModel.isQsFullyExpanded) || isCustomizing },
@@ -251,7 +258,7 @@ constructor(
@Composable
private fun Content() {
- PlatformTheme {
+ PlatformTheme(isDarkTheme = if (notificationShadeBlur()) isSystemInDarkTheme() else true) {
ProvideShortcutHelperIndication(interactionsConfig = interactionsConfig()) {
// TODO(b/389985793): Make sure that there is no coroutine work or recompositions
// happening when alwaysCompose is true but isQsVisibleAndAnyShadeExpanded is false.
@@ -270,11 +277,6 @@ constructor(
}
}
.graphicsLayer { alpha = viewModel.viewAlpha }
- .thenIf(notificationScrimClippingParams.isEnabled) {
- Modifier.notificationScrimClip {
- notificationScrimClippingParams.params
- }
- }
.thenIf(!Flags.notificationShadeBlur()) {
Modifier.offset {
IntOffset(
@@ -746,19 +748,31 @@ constructor(
Box(
Modifier.systemGestureExclusionInShade(
enabled = {
- layoutState.transitionState is TransitionState.Idle
+ /*
+ * While we are transitioning into QS (either from QQS
+ * or from gone), the global position of the brightness
+ * slider will change in every frame. This causes
+ * the modifier to send a new gesture exclusion
+ * rectangle on every frame. Instead, only apply the
+ * modifier when this is settled.
+ */
+ layoutState.transitionState is TransitionState.Idle &&
+ viewModel.isNotTransitioning
}
)
) {
- BrightnessSliderContainer(
- viewModel = containerViewModel.brightnessSliderViewModel,
- containerColors =
- ContainerColors(
- Color.Transparent,
- ContainerColors.defaultContainerColor,
- ),
- modifier = Modifier.fillMaxWidth(),
- )
+ AlwaysDarkMode {
+ BrightnessSliderContainer(
+ viewModel =
+ containerViewModel.brightnessSliderViewModel,
+ containerColors =
+ ContainerColors(
+ Color.Transparent,
+ ContainerColors.defaultContainerColor,
+ ),
+ modifier = Modifier.fillMaxWidth(),
+ )
+ }
}
}
val TileGrid =
@@ -1043,17 +1057,75 @@ private const val EDIT_MODE_TIME_MILLIS = 500
private class FrameLayoutTouchPassthrough(
context: Context,
private val clippingEnabledProvider: () -> Boolean,
- private val clippingTopProvider: () -> Int,
+ private val clippingParams: Flow<NotificationScrimClipParams>,
private val canScrollForwardQs: () -> Boolean,
private val emitMotionEventForFalsing: () -> Unit,
) : FrameLayout(context) {
+
+ init {
+ repeatWhenAttached {
+ repeatOnLifecycle(Lifecycle.State.STARTED) {
+ clippingParams.collect { currentClipParams = it }
+ }
+ }
+ }
+
+ private val currentClippingPath = Path()
+ private var lastWidth = -1
+ set(value) {
+ if (field != value) {
+ field = value
+ updateClippingPath()
+ }
+ }
+
+ private var currentClipParams = NotificationScrimClipParams()
+ set(value) {
+ if (field != value) {
+ field = value
+ updateClippingPath()
+ }
+ }
+
+ private fun updateClippingPath() {
+ currentClippingPath.rewind()
+ if (clippingEnabledProvider()) {
+ val right = width + currentClipParams.rightInset
+ val left = -currentClipParams.leftInset
+ val top = currentClipParams.top
+ val bottom = currentClipParams.bottom
+ currentClippingPath.addRoundRect(
+ left.toFloat(),
+ top.toFloat(),
+ right.toFloat(),
+ bottom.toFloat(),
+ currentClipParams.radius.toFloat(),
+ currentClipParams.radius.toFloat(),
+ Path.Direction.CW,
+ )
+ }
+ invalidate()
+ }
+
+ override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
+ super.onLayout(changed, left, top, right, bottom)
+ lastWidth = right - left
+ }
+
+ override fun dispatchDraw(canvas: Canvas) {
+ if (!currentClippingPath.isEmpty) {
+ canvas.clipOutPath(currentClippingPath)
+ }
+ super.dispatchDraw(canvas)
+ }
+
override fun isTransformedTouchPointInView(
x: Float,
y: Float,
child: View?,
outLocalPoint: PointF?,
): Boolean {
- return if (clippingEnabledProvider() && y + translationY > clippingTopProvider()) {
+ return if (clippingEnabledProvider() && y + translationY > currentClipParams.top) {
false
} else {
super.isTransformedTouchPointInView(x, y, child, outLocalPoint)
@@ -1236,3 +1308,31 @@ private fun interactionsConfig() =
private inline val alwaysCompose
get() = Flags.alwaysComposeQsUiFragment()
+
+/**
+ * Forces the configuration and themes to be dark theme. This is needed in order to have
+ * [colorResource] retrieve the dark mode colors.
+ *
+ * This should be removed when [notificationShadeBlur] is removed
+ */
+@Composable
+private fun AlwaysDarkMode(content: @Composable () -> Unit) {
+ if (notificationShadeBlur()) {
+ content()
+ } else {
+ val currentConfig = LocalConfiguration.current
+ val darkConfig =
+ Configuration(currentConfig).apply {
+ uiMode =
+ (uiMode and (Configuration.UI_MODE_NIGHT_MASK.inv())) or
+ Configuration.UI_MODE_NIGHT_YES
+ }
+ val newContext = LocalContext.current.createConfigurationContext(darkConfig)
+ CompositionLocalProvider(
+ LocalConfiguration provides darkConfig,
+ LocalContext provides newContext,
+ ) {
+ content()
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/composefragment/ui/NotificationScrimClip.kt b/packages/SystemUI/src/com/android/systemui/qs/composefragment/ui/NotificationScrimClip.kt
deleted file mode 100644
index 3049a40f18c4..000000000000
--- a/packages/SystemUI/src/com/android/systemui/qs/composefragment/ui/NotificationScrimClip.kt
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.qs.composefragment.ui
-
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.draw.drawWithContent
-import androidx.compose.ui.geometry.CornerRadius
-import androidx.compose.ui.geometry.Offset
-import androidx.compose.ui.geometry.Size
-import androidx.compose.ui.graphics.BlendMode
-import androidx.compose.ui.graphics.ClipOp
-import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.graphics.CompositingStrategy
-import androidx.compose.ui.graphics.drawscope.clipRect
-import androidx.compose.ui.graphics.graphicsLayer
-
-/**
- * Clipping modifier for clipping out the notification scrim as it slides over QS. It will clip out
- * ([ClipOp.Difference]) a `RoundRect(-leftInset, top, width + rightInset, bottom, radius, radius)`
- * from the QS container.
- */
-fun Modifier.notificationScrimClip(clipParams: () -> NotificationScrimClipParams): Modifier {
- return this.graphicsLayer { compositingStrategy = CompositingStrategy.Offscreen }
- .drawWithContent {
- drawContent()
- val params = clipParams()
- val left = -params.leftInset.toFloat()
- val right = size.width + params.rightInset.toFloat()
- val top = params.top.toFloat()
- val bottom = params.bottom.toFloat()
- val clipSize = Size(right - left, bottom - top)
- if (!clipSize.isEmpty()) {
- clipRect {
- drawRoundRect(
- color = Color.Black,
- cornerRadius = CornerRadius(params.radius.toFloat()),
- blendMode = BlendMode.Clear,
- topLeft = Offset(left, top),
- size = Size(right - left, bottom - top),
- )
- }
- }
- }
-}
-
-/** Params for [notificationScrimClip]. */
-data class NotificationScrimClipParams(
- val top: Int = 0,
- val bottom: Int = 0,
- val leftInset: Int = 0,
- val rightInset: Int = 0,
- val radius: Int = 0,
-)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/composefragment/ui/NotificationScrimClipParams.kt b/packages/SystemUI/src/com/android/systemui/qs/composefragment/ui/NotificationScrimClipParams.kt
new file mode 100644
index 000000000000..db320d3b9f1c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/composefragment/ui/NotificationScrimClipParams.kt
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.composefragment.ui
+
+/** Params for [notificationScrimClip]. */
+data class NotificationScrimClipParams(
+ val top: Int = 0,
+ val bottom: Int = 0,
+ val leftInset: Int = 0,
+ val rightInset: Int = 0,
+ val radius: Int = 0,
+)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModel.kt
index b61fa9cfe264..b7e9c5296bc9 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModel.kt
@@ -312,6 +312,11 @@ constructor(
source = containerViewModel.editModeViewModel.isEditing,
)
+ /** True if we are not in an expansion (from Gone to QQS/QS) animation. */
+ val isNotTransitioning by derivedStateOf {
+ viewTranslationY == 0f && viewAlpha == 1f && constrainedSquishinessFraction == 1f
+ }
+
private val inFirstPage: Boolean
get() = inFirstPageViewModel.inFirstPage
diff --git a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModuleBase.kt b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModuleBase.kt
index e2a39160defc..f1193fa11dce 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModuleBase.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModuleBase.kt
@@ -24,7 +24,7 @@ import com.android.systemui.qs.external.QSExternalModule
import com.android.systemui.qs.panels.dagger.PanelsModule
import com.android.systemui.qs.pipeline.dagger.QSPipelineModule
import com.android.systemui.qs.tileimpl.QSTileImpl
-import com.android.systemui.qs.tiles.di.QSTilesModule
+import com.android.systemui.qs.tiles.base.ui.model.QSTilesModule
import com.android.systemui.qs.ui.adapter.QSSceneAdapter
import com.android.systemui.qs.ui.adapter.QSSceneAdapterImpl
import dagger.Binds
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/EditTilesListInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/EditTilesListInteractor.kt
index a2cee3b68d49..ec0a754d8f1f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/EditTilesListInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/EditTilesListInteractor.kt
@@ -25,7 +25,7 @@ import com.android.systemui.qs.panels.data.repository.StockTilesRepository
import com.android.systemui.qs.panels.domain.model.EditTilesModel
import com.android.systemui.qs.panels.shared.model.EditTileData
import com.android.systemui.qs.shared.model.TileCategory
-import com.android.systemui.qs.tiles.viewmodel.QSTileConfigProvider
+import com.android.systemui.qs.tiles.base.shared.model.QSTileConfigProvider
import javax.inject.Inject
@SysUISingleton
@@ -50,7 +50,7 @@ constructor(
it,
Icon.Resource(
config.uiConfig.iconRes,
- ContentDescription.Resource(config.uiConfig.labelRes)
+ ContentDescription.Resource(config.uiConfig.labelRes),
),
Text.Resource(config.uiConfig.labelRes),
null,
@@ -61,7 +61,7 @@ constructor(
it,
Icon.Resource(
android.R.drawable.star_on,
- ContentDescription.Loaded(it.spec)
+ ContentDescription.Loaded(it.spec),
),
Text.Loaded(it.spec),
null,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/NewTilesAvailabilityInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/NewTilesAvailabilityInteractor.kt
index 00cd96ce37cb..46a6e4a90c57 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/NewTilesAvailabilityInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/NewTilesAvailabilityInteractor.kt
@@ -19,14 +19,14 @@ package com.android.systemui.qs.panels.domain.interactor
import android.os.UserHandle
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.qs.pipeline.shared.TileSpec
-import com.android.systemui.qs.tiles.base.interactor.QSTileAvailabilityInteractor
+import com.android.systemui.qs.tiles.base.domain.interactor.QSTileAvailabilityInteractor
import com.android.systemui.user.data.repository.UserRepository
import com.android.systemui.utils.coroutines.flow.flatMapLatestConflated
+import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.map
-import javax.inject.Inject
/**
* Uses the [QSTileAvailabilityInteractor] from the new tiles to provide a map of availability, for
@@ -38,23 +38,26 @@ import javax.inject.Inject
class NewTilesAvailabilityInteractor
@Inject
constructor(
- private val availabilityInteractors:
- Map<String, @JvmSuppressWildcards QSTileAvailabilityInteractor>,
- userRepository: UserRepository,
+ private val availabilityInteractors:
+ Map<String, @JvmSuppressWildcards QSTileAvailabilityInteractor>,
+ userRepository: UserRepository,
) {
val newTilesAvailable: Flow<Map<TileSpec, Boolean>> =
- userRepository.selectedUserInfo.map { it.id }
- .flatMapLatestConflated { userId ->
- if (availabilityInteractors.isEmpty()) {
- flowOf(emptyMap())
- } else {
- combine(availabilityInteractors.map { (spec, interactor) ->
- interactor.availability(UserHandle.of(userId)).map {
- TileSpec.create(spec) to it
- }
- }) {
- it.toMap()
+ userRepository.selectedUserInfo
+ .map { it.id }
+ .flatMapLatestConflated { userId ->
+ if (availabilityInteractors.isEmpty()) {
+ flowOf(emptyMap())
+ } else {
+ combine(
+ availabilityInteractors.map { (spec, interactor) ->
+ interactor.availability(UserHandle.of(userId)).map {
+ TileSpec.create(spec) to it
}
}
+ ) {
+ it.toMap()
}
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/DragAndDropState.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/DragAndDropState.kt
index 405ce8a8e5e0..005c8b26b0d8 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/DragAndDropState.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/DragAndDropState.kt
@@ -35,7 +35,6 @@ import androidx.compose.ui.draganddrop.mimeTypes
import androidx.compose.ui.draganddrop.toAndroidDragEvent
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.unit.IntRect
-import androidx.compose.ui.unit.center
import androidx.compose.ui.unit.toRect
import com.android.systemui.qs.panels.shared.model.SizedTile
import com.android.systemui.qs.panels.ui.viewmodel.EditTileViewModel
@@ -44,6 +43,7 @@ import com.android.systemui.qs.pipeline.shared.TileSpec
/** Holds the [TileSpec] of the tile being moved and receives drag and drop events. */
interface DragAndDropState {
val draggedCell: SizedTile<EditTileViewModel>?
+ val isDraggedCellRemovable: Boolean
val draggedPosition: Offset
val dragInProgress: Boolean
val dragType: DragType?
@@ -76,7 +76,7 @@ enum class DragType {
@Composable
fun Modifier.dragAndDropRemoveZone(
dragAndDropState: DragAndDropState,
- onDrop: (TileSpec) -> Unit,
+ onDrop: (TileSpec, removalEnabled: Boolean) -> Unit,
): Modifier {
val target =
remember(dragAndDropState) {
@@ -87,13 +87,15 @@ fun Modifier.dragAndDropRemoveZone(
override fun onDrop(event: DragAndDropEvent): Boolean {
return dragAndDropState.draggedCell?.let {
- onDrop(it.tile.tileSpec)
+ onDrop(it.tile.tileSpec, dragAndDropState.isDraggedCellRemovable)
dragAndDropState.onDrop()
true
} ?: false
}
override fun onEntered(event: DragAndDropEvent) {
+ if (!dragAndDropState.isDraggedCellRemovable) return
+
dragAndDropState.movedOutOfBounds()
}
}
@@ -168,10 +170,10 @@ private fun DragAndDropEvent.toOffset(): Offset {
}
private fun insertAfter(item: LazyGridItemInfo, offset: Offset): Boolean {
- // We want to insert the tile after the target if we're aiming at the right side of a large tile
+ // We want to insert the tile after the target if we're aiming at the end of a large tile
// TODO(ostonge): Verify this behavior in RTL
- val itemCenter = item.offset + item.size.center
- return item.span != 1 && offset.x > itemCenter.x
+ val itemCenter = item.offset.x + item.size.width * .75
+ return item.span != 1 && offset.x > itemCenter
}
@OptIn(ExperimentalFoundationApi::class)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/EditTileListState.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/EditTileListState.kt
index 868855840922..70f1674acd3b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/EditTileListState.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/EditTileListState.kt
@@ -25,6 +25,7 @@ import androidx.compose.runtime.snapshots.SnapshotStateList
import androidx.compose.runtime.toMutableStateList
import androidx.compose.ui.geometry.Offset
import com.android.systemui.qs.panels.shared.model.SizedTile
+import com.android.systemui.qs.panels.ui.compose.selection.PlacementEvent
import com.android.systemui.qs.panels.ui.model.GridCell
import com.android.systemui.qs.panels.ui.model.TileGridCell
import com.android.systemui.qs.panels.ui.model.toGridCells
@@ -60,6 +61,11 @@ class EditTileListState(
override var dragType by mutableStateOf<DragType?>(null)
private set
+ // A dragged cell can be removed if it was added in the drag movement OR if it's marked as
+ // removable
+ override val isDraggedCellRemovable: Boolean
+ get() = dragType == DragType.Add || draggedCell?.tile?.isRemovable ?: false
+
override val dragInProgress: Boolean
get() = draggedCell != null
@@ -76,10 +82,16 @@ class EditTileListState(
return _tiles.indexOfFirst { it is TileGridCell && it.tile.tileSpec == tileSpec }
}
+ fun isRemovable(tileSpec: TileSpec): Boolean {
+ return _tiles.find {
+ it is TileGridCell && it.tile.tileSpec == tileSpec && it.tile.isRemovable
+ } != null
+ }
+
/** Resize the tile corresponding to the [TileSpec] to [toIcon] */
fun resizeTile(tileSpec: TileSpec, toIcon: Boolean) {
val fromIndex = indexOf(tileSpec)
- if (fromIndex != -1) {
+ if (fromIndex != INVALID_INDEX) {
val cell = _tiles[fromIndex] as TileGridCell
if (cell.isIcon == toIcon) return
@@ -97,9 +109,6 @@ class EditTileListState(
override fun onStarted(cell: SizedTile<EditTileViewModel>, dragType: DragType) {
draggedCell = cell
this.dragType = dragType
-
- // Add spacers to the grid to indicate where the user can move a tile
- regenerateGrid()
}
override fun onTargeting(target: Int, insertAfter: Boolean) {
@@ -111,7 +120,7 @@ class EditTileListState(
}
val insertionIndex = if (insertAfter) target + 1 else target
- if (fromIndex != -1) {
+ if (fromIndex != INVALID_INDEX) {
val cell = _tiles.removeAt(fromIndex)
regenerateGrid()
_tiles.add(insertionIndex.coerceIn(0, _tiles.size), cell)
@@ -149,6 +158,43 @@ class EditTileListState(
regenerateGrid()
}
+ /**
+ * Return the appropriate index to move the tile to for the placement [event]
+ *
+ * The grid includes spacers. As a result, indexes from the grid need to be translated to the
+ * corresponding index from [currentTileSpecs].
+ */
+ fun targetIndexForPlacement(event: PlacementEvent): Int {
+ val currentTileSpecs = tileSpecs()
+ return when (event) {
+ is PlacementEvent.PlaceToTileSpec -> {
+ currentTileSpecs.indexOf(event.targetSpec)
+ }
+ is PlacementEvent.PlaceToIndex -> {
+ if (event.targetIndex >= _tiles.size) {
+ currentTileSpecs.size
+ } else if (event.targetIndex <= 0) {
+ 0
+ } else {
+ // The index may point to a spacer, so first find the first tile located
+ // after index, then use its position as a target
+ val targetTile =
+ _tiles.subList(event.targetIndex, _tiles.size).firstOrNull {
+ it is TileGridCell
+ } as? TileGridCell
+
+ if (targetTile == null) {
+ currentTileSpecs.size
+ } else {
+ val targetIndex = currentTileSpecs.indexOf(targetTile.tile.tileSpec)
+ val fromIndex = currentTileSpecs.indexOf(event.movingSpec)
+ if (fromIndex < targetIndex) targetIndex - 1 else targetIndex
+ }
+ }
+ }
+ }
+ }
+
/** Regenerate the list of [GridCell] with their new potential rows */
private fun regenerateGrid() {
_tiles.filterIsInstance<TileGridCell>().toGridCells(columns).let {
@@ -170,4 +216,8 @@ class EditTileListState(
_tiles.addAll(it)
}
}
+
+ companion object {
+ const val INVALID_INDEX = -1
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/QuickQuickSettings.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/QuickQuickSettings.kt
index d20b360756d7..728652e6e5c4 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/QuickQuickSettings.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/QuickQuickSettings.kt
@@ -82,6 +82,7 @@ fun ContentScope.QuickQuickSettings(
viewModel.tileHapticsViewModelFactoryProvider,
// There should be no QuickQuickSettings when the details view is enabled.
detailsViewModel = null,
+ isVisible = listening,
)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/TileDetails.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/TileDetails.kt
index d40ecc9565ae..f3ed07a8a753 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/TileDetails.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/TileDetails.kt
@@ -16,6 +16,7 @@
package com.android.systemui.qs.panels.ui.compose
+import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
@@ -23,11 +24,13 @@ import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.width
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.ArrowBack
import androidx.compose.material.icons.filled.Settings
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
+import androidx.compose.material3.IconButtonDefaults
import androidx.compose.material3.LocalContentColor
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
@@ -43,6 +46,8 @@ import com.android.systemui.bluetooth.qsdialog.BluetoothDetailsViewModel
import com.android.systemui.plugins.qs.TileDetailsViewModel
import com.android.systemui.qs.flags.QsDetailedView
import com.android.systemui.qs.panels.ui.viewmodel.DetailsViewModel
+import com.android.systemui.qs.tiles.dialog.CastDetailsContent
+import com.android.systemui.qs.tiles.dialog.CastDetailsViewModel
import com.android.systemui.qs.tiles.dialog.InternetDetailsContent
import com.android.systemui.qs.tiles.dialog.InternetDetailsViewModel
import com.android.systemui.qs.tiles.dialog.ModesDetailsContent
@@ -63,6 +68,7 @@ fun TileDetails(modifier: Modifier = Modifier, detailsViewModel: DetailsViewMode
val title = tileDetailedViewModel.title
val subTitle = tileDetailedViewModel.subTitle
+ val colors = MaterialTheme.colorScheme
Column(
modifier =
@@ -70,20 +76,33 @@ fun TileDetails(modifier: Modifier = Modifier, detailsViewModel: DetailsViewMode
.fillMaxWidth()
// The height of the details view is TBD.
.fillMaxHeight()
+ .background(color = colors.onPrimary)
) {
CompositionLocalProvider(
value = LocalContentColor provides MaterialTheme.colorScheme.onSurfaceVariant
) {
Row(
- modifier = Modifier.fillMaxWidth(),
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(
+ start = TileDetailsDefaults.TitleRowStart,
+ top = TileDetailsDefaults.TitleRowTop,
+ end = TileDetailsDefaults.TitleRowEnd,
+ bottom = TileDetailsDefaults.TitleRowBottom
+ ),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically,
) {
IconButton(
onClick = { detailsViewModel.closeDetailedView() },
+ colors = IconButtonDefaults.iconButtonColors(
+ contentColor = colors.onSurface
+ ),
modifier =
- Modifier.align(Alignment.CenterVertically)
+ Modifier
+ .align(Alignment.CenterVertically)
.height(TileDetailsDefaults.IconHeight)
+ .width(TileDetailsDefaults.IconWidth)
.padding(start = TileDetailsDefaults.IconPadding),
) {
Icon(
@@ -96,13 +115,19 @@ fun TileDetails(modifier: Modifier = Modifier, detailsViewModel: DetailsViewMode
text = title,
modifier = Modifier.align(Alignment.CenterVertically),
textAlign = TextAlign.Center,
- style = MaterialTheme.typography.titleLarge,
+ style = MaterialTheme.typography.titleMedium,
+ color = colors.onSurface,
)
IconButton(
onClick = { tileDetailedViewModel.clickOnSettingsButton() },
+ colors = IconButtonDefaults.iconButtonColors(
+ contentColor = colors.onSurface
+ ),
modifier =
- Modifier.align(Alignment.CenterVertically)
+ Modifier
+ .align(Alignment.CenterVertically)
.height(TileDetailsDefaults.IconHeight)
+ .width(TileDetailsDefaults.IconWidth)
.padding(end = TileDetailsDefaults.IconPadding),
) {
Icon(
@@ -116,7 +141,8 @@ fun TileDetails(modifier: Modifier = Modifier, detailsViewModel: DetailsViewMode
text = subTitle,
modifier = Modifier.fillMaxWidth(),
textAlign = TextAlign.Center,
- style = MaterialTheme.typography.titleSmall,
+ style = MaterialTheme.typography.bodySmall,
+ color = colors.onSurfaceVariant,
)
}
MapTileDetailsContent(tileDetailedViewModel)
@@ -131,10 +157,16 @@ private fun MapTileDetailsContent(tileDetailsViewModel: TileDetailsViewModel) {
is BluetoothDetailsViewModel ->
BluetoothDetailsContent(tileDetailsViewModel.detailsContentViewModel)
is ModesDetailsViewModel -> ModesDetailsContent(tileDetailsViewModel)
+ is CastDetailsViewModel -> CastDetailsContent(tileDetailsViewModel)
}
}
private object TileDetailsDefaults {
- val IconHeight = 48.dp
+ val IconHeight = 24.dp
+ val IconWidth = 24.dp
val IconPadding = 4.dp
+ val TitleRowStart = 14.dp
+ val TitleRowTop = 22.dp
+ val TitleRowEnd = 20.dp
+ val TitleRowBottom = 8.dp
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/CommonTile.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/CommonTile.kt
index 50012abc69d6..699778f3b6f9 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/CommonTile.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/CommonTile.kt
@@ -39,12 +39,14 @@ import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.BasicText
+import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.key
+import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
@@ -102,6 +104,7 @@ fun LargeTileContent(
sideDrawable: Drawable?,
colors: TileColors,
squishiness: () -> Float,
+ isVisible: () -> Boolean = { true },
accessibilityUiState: AccessibilityUiState? = null,
iconShape: RoundedCornerShape = RoundedCornerShape(CommonTileDefaults.InactiveCornerRadius),
toggleClick: (() -> Unit)? = null,
@@ -158,6 +161,7 @@ fun LargeTileContent(
secondaryLabel = secondaryLabel,
colors = colors,
accessibilityUiState = accessibilityUiState,
+ isVisible = isVisible,
)
if (sideDrawable != null) {
@@ -170,12 +174,14 @@ fun LargeTileContent(
}
}
+@OptIn(ExperimentalMaterial3ExpressiveApi::class)
@Composable
fun LargeTileLabels(
label: String,
secondaryLabel: String?,
colors: TileColors,
modifier: Modifier = Modifier,
+ isVisible: () -> Boolean = { true },
accessibilityUiState: AccessibilityUiState? = null,
) {
val animatedLabelColor by animateColorAsState(colors.label, label = "QSTileLabelColor")
@@ -184,14 +190,16 @@ fun LargeTileLabels(
Column(verticalArrangement = Arrangement.Center, modifier = modifier.fillMaxHeight()) {
TileLabel(
text = label,
- style = MaterialTheme.typography.labelLarge,
+ style = MaterialTheme.typography.titleSmallEmphasized,
color = { animatedLabelColor },
+ isVisible = isVisible,
)
if (!TextUtils.isEmpty(secondaryLabel)) {
TileLabel(
secondaryLabel ?: "",
color = { animatedSecondaryLabelColor },
- style = MaterialTheme.typography.bodyMedium,
+ style = MaterialTheme.typography.labelMedium,
+ isVisible = isVisible,
modifier =
Modifier.thenIf(
accessibilityUiState?.stateDescription?.contains(secondaryLabel ?: "") ==
@@ -277,36 +285,50 @@ private fun TileLabel(
color: ColorProducer,
style: TextStyle,
modifier: Modifier = Modifier,
+ isVisible: () -> Boolean = { true },
) {
+ var textSize by remember { mutableIntStateOf(0) }
+
BasicText(
text = text,
color = color,
style = style,
maxLines = 1,
+ onTextLayout = { textSize = it.size.width },
modifier =
modifier
.fillMaxWidth()
- .graphicsLayer { compositingStrategy = CompositingStrategy.Offscreen }
+ .graphicsLayer {
+ if (textSize > size.width) {
+ compositingStrategy = CompositingStrategy.Offscreen
+ }
+ }
.drawWithContent {
drawContent()
- // Draw a blur over the end of the text
- val edgeWidthPx = TileLabelBlurWidth.toPx()
- drawRect(
- topLeft = Offset(size.width - edgeWidthPx, 0f),
- size = Size(edgeWidthPx, size.height),
- brush =
- Brush.horizontalGradient(
- colors = listOf(Color.Transparent, Color.Black),
- startX = size.width,
- endX = size.width - edgeWidthPx,
- ),
- blendMode = BlendMode.DstIn,
- )
+ if (textSize > size.width) {
+ // Draw a blur over the end of the text
+ val edgeWidthPx = TileLabelBlurWidth.toPx()
+ drawRect(
+ topLeft = Offset(size.width - edgeWidthPx, 0f),
+ size = Size(edgeWidthPx, size.height),
+ brush =
+ Brush.horizontalGradient(
+ colors = listOf(Color.Transparent, Color.Black),
+ startX = size.width,
+ endX = size.width - edgeWidthPx,
+ ),
+ blendMode = BlendMode.DstIn,
+ )
+ }
}
- .basicMarquee(
- iterations = TILE_MARQUEE_ITERATIONS,
- initialDelayMillis = TILE_INITIAL_DELAY_MILLIS,
- ),
+ .thenIf(isVisible()) {
+ // Only apply the marquee when the label is visible, which is needed for the
+ // always composed QS
+ Modifier.basicMarquee(
+ iterations = TILE_MARQUEE_ITERATIONS,
+ initialDelayMillis = TILE_INITIAL_DELAY_MILLIS,
+ )
+ },
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/EditTile.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/EditTile.kt
index ccbd8fdbe00c..f8eaa6c3bcfb 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/EditTile.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/EditTile.kt
@@ -19,6 +19,7 @@
package com.android.systemui.qs.panels.ui.compose.infinitegrid
import androidx.compose.animation.AnimatedContent
+import androidx.compose.animation.animateColorAsState
import androidx.compose.animation.animateContentSize
import androidx.compose.animation.core.LinearEasing
import androidx.compose.animation.core.animateDpAsState
@@ -34,6 +35,7 @@ import androidx.compose.foundation.border
import androidx.compose.foundation.clickable
import androidx.compose.foundation.clipScrollableContainer
import androidx.compose.foundation.gestures.Orientation
+import androidx.compose.foundation.gestures.detectTapGestures
import androidx.compose.foundation.layout.Arrangement.spacedBy
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.BoxScope
@@ -65,6 +67,7 @@ import androidx.compose.material.icons.filled.Add
import androidx.compose.material.icons.filled.Clear
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.ExperimentalMaterial3Api
+import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.LocalContentColor
@@ -77,6 +80,7 @@ import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.State
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.key
@@ -95,6 +99,7 @@ import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.geometry.isSpecified
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.graphicsLayer
+import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.layout.MeasureScope
import androidx.compose.ui.layout.layout
import androidx.compose.ui.layout.onGloballyPositioned
@@ -109,11 +114,11 @@ import androidx.compose.ui.semantics.contentDescription
import androidx.compose.ui.semantics.customActions
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.semantics.stateDescription
+import androidx.compose.ui.text.style.Hyphens
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
-import androidx.compose.ui.unit.sp
import androidx.compose.ui.util.fastMap
import com.android.compose.gesture.effect.rememberOffsetOverscrollEffectFactory
import com.android.compose.modifiers.height
@@ -124,6 +129,7 @@ import com.android.systemui.qs.panels.shared.model.SizedTileImpl
import com.android.systemui.qs.panels.ui.compose.DragAndDropState
import com.android.systemui.qs.panels.ui.compose.DragType
import com.android.systemui.qs.panels.ui.compose.EditTileListState
+import com.android.systemui.qs.panels.ui.compose.EditTileListState.Companion.INVALID_INDEX
import com.android.systemui.qs.panels.ui.compose.dragAndDropRemoveZone
import com.android.systemui.qs.panels.ui.compose.dragAndDropTileList
import com.android.systemui.qs.panels.ui.compose.dragAndDropTileSource
@@ -151,7 +157,6 @@ import com.android.systemui.qs.panels.ui.model.AvailableTileGridCell
import com.android.systemui.qs.panels.ui.model.GridCell
import com.android.systemui.qs.panels.ui.model.SpacerGridCell
import com.android.systemui.qs.panels.ui.model.TileGridCell
-import com.android.systemui.qs.panels.ui.viewmodel.AvailableEditActions
import com.android.systemui.qs.panels.ui.viewmodel.BounceableTileViewModel
import com.android.systemui.qs.panels.ui.viewmodel.EditTileViewModel
import com.android.systemui.qs.pipeline.shared.TileSpec
@@ -160,12 +165,11 @@ import com.android.systemui.res.R
import kotlin.math.abs
import kotlin.math.roundToInt
import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
object TileType
-@OptIn(ExperimentalMaterial3Api::class)
+@OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterial3ExpressiveApi::class)
@Composable
private fun EditModeTopBar(onStopEditing: () -> Unit, onReset: (() -> Unit)?) {
val primaryContainerColor = MaterialTheme.colorScheme.primaryContainer
@@ -177,7 +181,8 @@ private fun EditModeTopBar(onStopEditing: () -> Unit, onReset: (() -> Unit)?) {
),
title = {
Text(
- text = stringResource(id = R.string.qs_edit),
+ text = stringResource(id = R.string.qs_edit_tiles),
+ style = MaterialTheme.typography.titleLargeEmphasized,
modifier = Modifier.padding(start = 24.dp),
)
},
@@ -204,7 +209,10 @@ private fun EditModeTopBar(onStopEditing: () -> Unit, onReset: (() -> Unit)?) {
contentColor = MaterialTheme.colorScheme.onPrimary,
),
) {
- Text(stringResource(id = com.android.internal.R.string.reset))
+ Text(
+ text = stringResource(id = com.android.internal.R.string.reset),
+ style = MaterialTheme.typography.labelLarge,
+ )
}
}
},
@@ -212,6 +220,7 @@ private fun EditModeTopBar(onStopEditing: () -> Unit, onReset: (() -> Unit)?) {
)
}
+@OptIn(ExperimentalMaterial3ExpressiveApi::class)
@Composable
fun DefaultEditTileGrid(
listState: EditTileListState,
@@ -219,7 +228,7 @@ fun DefaultEditTileGrid(
columns: Int,
largeTilesSpan: Int,
modifier: Modifier,
- onAddTile: (TileSpec) -> Unit,
+ onAddTile: (TileSpec, Int) -> Unit,
onRemoveTile: (TileSpec) -> Unit,
onSetTiles: (List<TileSpec>) -> Unit,
onResize: (TileSpec, toIcon: Boolean) -> Unit,
@@ -237,6 +246,15 @@ fun DefaultEditTileGrid(
null
}
+ LaunchedEffect(selectionState.placementEvent) {
+ selectionState.placementEvent?.let { event ->
+ listState
+ .targetIndexForPlacement(event)
+ .takeIf { it != INVALID_INDEX }
+ ?.let { onAddTile(event.movingSpec, it) }
+ }
+ }
+
Scaffold(
containerColor = Color.Transparent,
topBar = { EditModeTopBar(onStopEditing = onStopEditing, onReset = reset) },
@@ -266,27 +284,23 @@ fun DefaultEditTileGrid(
.padding(top = innerPadding.calculateTopPadding())
.clipScrollableContainer(Orientation.Vertical)
.verticalScroll(scrollState)
- .dragAndDropRemoveZone(listState, onRemoveTile),
+ .dragAndDropRemoveZone(listState) { spec, removalEnabled ->
+ if (removalEnabled) {
+ // If removal is enabled, remove the tile
+ onRemoveTile(spec)
+ } else {
+ // Otherwise submit the new tile ordering
+ onSetTiles(listState.tileSpecs())
+ selectionState.select(spec)
+ }
+ },
) {
- AnimatedContent(
- targetState = listState.dragInProgress || selectionState.selected,
- label = "QSEditHeader",
- contentAlignment = Alignment.Center,
+ CurrentTilesGridHeader(
+ listState,
+ selectionState,
+ onRemoveTile,
modifier = Modifier.fillMaxWidth().heightIn(min = 48.dp),
- ) { showRemoveTarget ->
- EditGridHeader {
- if (showRemoveTarget) {
- RemoveTileTarget {
- selectionState.selection?.let {
- selectionState.unSelect()
- onRemoveTile(it)
- }
- }
- } else {
- Text(text = stringResource(id = R.string.drag_to_rearrange_tiles))
- }
- }
- }
+ )
CurrentTilesGrid(
listState,
@@ -307,7 +321,7 @@ fun DefaultEditTileGrid(
// Using the fully qualified name here as a workaround for AnimatedVisibility
// not being available from a Box
androidx.compose.animation.AnimatedVisibility(
- visible = !listState.dragInProgress,
+ visible = !listState.dragInProgress && !selectionState.placementEnabled,
enter = fadeIn(),
exit = fadeOut(),
) {
@@ -332,7 +346,7 @@ fun DefaultEditTileGrid(
availableTiles,
selectionState,
columns,
- onAddTile,
+ { onAddTile(it, listState.tileSpecs().size) }, // Add to the end
listState,
)
}
@@ -390,6 +404,76 @@ private fun AutoScrollGrid(
}
}
+private enum class EditModeHeaderState {
+ Remove,
+ Place,
+ Idle,
+}
+
+@Composable
+private fun rememberEditModeState(
+ listState: EditTileListState,
+ selectionState: MutableSelectionState,
+): State<EditModeHeaderState> {
+ val editGridHeaderState = remember { mutableStateOf(EditModeHeaderState.Idle) }
+ LaunchedEffect(
+ listState.dragInProgress,
+ selectionState.selected,
+ selectionState.placementEnabled,
+ ) {
+ val canRemove =
+ listState.isDraggedCellRemovable ||
+ selectionState.selection?.let { listState.isRemovable(it) } ?: false
+
+ editGridHeaderState.value =
+ when {
+ selectionState.placementEnabled -> EditModeHeaderState.Place
+ canRemove -> EditModeHeaderState.Remove
+ else -> EditModeHeaderState.Idle
+ }
+ }
+
+ return editGridHeaderState
+}
+
+@Composable
+private fun CurrentTilesGridHeader(
+ listState: EditTileListState,
+ selectionState: MutableSelectionState,
+ onRemoveTile: (TileSpec) -> Unit,
+ modifier: Modifier = Modifier,
+) {
+ val editGridHeaderState by rememberEditModeState(listState, selectionState)
+
+ AnimatedContent(
+ targetState = editGridHeaderState,
+ label = "QSEditHeader",
+ contentAlignment = Alignment.Center,
+ modifier = modifier,
+ ) { state ->
+ EditGridHeader {
+ when (state) {
+ EditModeHeaderState.Remove -> {
+ RemoveTileTarget {
+ selectionState.selection?.let {
+ selectionState.unSelect()
+ onRemoveTile(it)
+ }
+ }
+ }
+ EditModeHeaderState.Place -> {
+ EditGridCenteredText(text = stringResource(id = R.string.tap_to_position_tile))
+ }
+ EditModeHeaderState.Idle -> {
+ EditGridCenteredText(
+ text = stringResource(id = R.string.drag_to_rearrange_tiles)
+ )
+ }
+ }
+ }
+ }
+}
+
@Composable
private fun EditGridHeader(
modifier: Modifier = Modifier,
@@ -401,6 +485,11 @@ private fun EditGridHeader(
}
@Composable
+private fun EditGridCenteredText(text: String, modifier: Modifier = Modifier) {
+ Text(text = text, style = MaterialTheme.typography.titleSmall, modifier = modifier)
+}
+
+@Composable
private fun RemoveTileTarget(onClick: () -> Unit) {
Row(
verticalAlignment = Alignment.CenterVertically,
@@ -471,8 +560,14 @@ private fun CurrentTilesGrid(
}
.testTag(CURRENT_TILES_GRID_TEST_TAG),
) {
- EditTiles(cells, listState, selectionState, coroutineScope, largeTilesSpan, onRemoveTile) {
- resizingOperation ->
+ EditTiles(
+ cells,
+ listState,
+ selectionState,
+ coroutineScope,
+ largeTilesSpan,
+ onRemoveTile = onRemoveTile,
+ ) { resizingOperation ->
when (resizingOperation) {
is TemporaryResizeOperation -> {
currentListState.resizeTile(resizingOperation.spec, resizingOperation.toIcon)
@@ -486,6 +581,7 @@ private fun CurrentTilesGrid(
}
}
+@OptIn(ExperimentalMaterial3ExpressiveApi::class)
@Composable
private fun AvailableTileGrid(
tiles: List<AvailableTileGridCell>,
@@ -524,7 +620,7 @@ private fun AvailableTileGrid(
) {
Text(
text = category.label.load() ?: "",
- fontSize = 20.sp,
+ style = MaterialTheme.typography.titleMediumEmphasized,
color = MaterialTheme.colorScheme.onSurface,
modifier = Modifier.fillMaxWidth().padding(start = 8.dp, bottom = 16.dp),
)
@@ -571,6 +667,7 @@ private fun GridCell.key(index: Int): Any {
* @param selectionState the [MutableSelectionState] for this grid
* @param coroutineScope the [CoroutineScope] to be used for the tiles
* @param largeTilesSpan the width used for large tiles
+ * @param onRemoveTile the callback when a tile is removed from this grid
* @param onResize the callback when a tile has a new [ResizeOperation]
*/
fun LazyGridScope.EditTiles(
@@ -614,12 +711,33 @@ fun LazyGridScope.EditTiles(
modifier = Modifier.animateItem(),
)
}
- is SpacerGridCell -> SpacerGridCell()
+ is SpacerGridCell ->
+ SpacerGridCell(
+ Modifier.pointerInput(Unit) {
+ detectTapGestures(onTap = { selectionState.onTap(index) })
+ }
+ )
}
}
}
@Composable
+private fun rememberTileState(
+ tile: EditTileViewModel,
+ selectionState: MutableSelectionState,
+): State<TileState> {
+ val tileState = remember { mutableStateOf(TileState.None) }
+ val canShowRemovalBadge = tile.isRemovable
+
+ LaunchedEffect(selectionState.selection, selectionState.placementEnabled, canShowRemovalBadge) {
+ tileState.value =
+ selectionState.tileStateFor(tile.tileSpec, tileState.value, canShowRemovalBadge)
+ }
+
+ return tileState
+}
+
+@Composable
private fun TileGridCell(
cell: TileGridCell,
index: Int,
@@ -632,29 +750,7 @@ private fun TileGridCell(
modifier: Modifier = Modifier,
) {
val stateDescription = stringResource(id = R.string.accessibility_qs_edit_position, index + 1)
- val canShowRemovalBadge = cell.tile.availableEditActions.contains(AvailableEditActions.REMOVE)
- var tileState by remember { mutableStateOf(TileState.None) }
-
- LaunchedEffect(selectionState.selection, canShowRemovalBadge) {
- tileState =
- when {
- selectionState.selection == cell.tile.tileSpec -> {
- if (tileState == TileState.None && canShowRemovalBadge) {
- // The tile decoration is None if a tile is newly composed OR the removal
- // badge can't be shown.
- // For newly composed and selected tiles, such as dragged tiles or moved
- // tiles from resizing, introduce a short delay. This avoids clipping issues
- // on the border and resizing handle, as well as letting the selection
- // animation play correctly.
- delay(250)
- }
- TileState.Selected
- }
- canShowRemovalBadge -> TileState.Removable
- else -> TileState.None
- }
- }
-
+ val tileState by rememberTileState(cell.tile, selectionState)
val resizingState = rememberResizingState(cell.tile.tileSpec, cell.isIcon)
val progress: () -> Float = {
if (tileState == TileState.Selected) {
@@ -682,12 +778,16 @@ private fun TileGridCell(
with(LocalDensity.current) { (largeTilesSpan - 1) * TileArrangementPadding.roundToPx() }
val colors = EditModeTileDefaults.editTileColors()
val toggleSizeLabel = stringResource(R.string.accessibility_qs_edit_toggle_tile_size_action)
- val clickLabel =
+ val togglePlacementModeLabel =
+ stringResource(R.string.accessibility_qs_edit_toggle_placement_mode)
+ val decorationClickLabel =
when (tileState) {
- TileState.None -> null
TileState.Removable ->
stringResource(id = R.string.accessibility_qs_edit_remove_tile_action)
TileState.Selected -> toggleSizeLabel
+ TileState.None,
+ TileState.Placeable,
+ TileState.GreyedOut -> null
}
InteractiveTileContainer(
tileState = tileState,
@@ -706,8 +806,13 @@ private fun TileGridCell(
coroutineScope.launch { resizingState.toggleCurrentValue() }
}
},
- onClickLabel = clickLabel,
+ onClickLabel = decorationClickLabel,
) {
+ val placeableColor = MaterialTheme.colorScheme.primary.copy(alpha = .4f)
+ val backgroundColor by
+ animateColorAsState(
+ if (tileState == TileState.Placeable) placeableColor else colors.background
+ )
Box(
modifier
.fillMaxSize()
@@ -720,7 +825,11 @@ private fun TileGridCell(
CustomAccessibilityAction(toggleSizeLabel) {
onResize(FinalResizeOperation(cell.tile.tileSpec, !cell.isIcon))
true
- }
+ },
+ CustomAccessibilityAction(togglePlacementModeLabel) {
+ selectionState.togglePlacementMode(cell.tile.tileSpec)
+ true
+ },
)
}
.selectableTile(cell.tile.tileSpec, selectionState)
@@ -730,13 +839,19 @@ private fun TileGridCell(
DragType.Move,
selectionState::unSelect,
)
- .tileBackground(colors.background)
+ .tileBackground { backgroundColor }
) {
- EditTile(tile = cell.tile, state = resizingState, progress = progress)
+ EditTile(
+ tile = cell.tile,
+ tileState = tileState,
+ state = resizingState,
+ progress = progress,
+ )
}
}
}
+@OptIn(ExperimentalMaterial3ExpressiveApi::class)
@Composable
private fun AvailableTileGridCell(
cell: AvailableTileGridCell,
@@ -776,7 +891,7 @@ private fun AvailableTileGridCell(
} else {
Modifier
}
- Box(draggableModifier.fillMaxSize().tileBackground(colors.background)) {
+ Box(draggableModifier.fillMaxSize().tileBackground { colors.background }) {
// Icon
SmallTileContent(
iconProvider = { cell.tile.icon },
@@ -803,6 +918,7 @@ private fun AvailableTileGridCell(
color = colors.label,
overflow = TextOverflow.Ellipsis,
textAlign = TextAlign.Center,
+ style = MaterialTheme.typography.labelMedium.copy(hyphens = Hyphens.Auto),
modifier = Modifier.align(Alignment.TopCenter),
)
}
@@ -818,11 +934,13 @@ private fun SpacerGridCell(modifier: Modifier = Modifier) {
@Composable
fun EditTile(
tile: EditTileViewModel,
+ tileState: TileState,
state: ResizingState,
progress: () -> Float,
colors: TileColors = EditModeTileDefaults.editTileColors(),
) {
val iconSizeDiff = CommonTileDefaults.IconSize - CommonTileDefaults.LargeTileIconSize
+ val alpha by animateFloatAsState(if (tileState == TileState.GreyedOut) .4f else 1f)
Row(
horizontalArrangement = spacedBy(6.dp),
verticalAlignment = Alignment.CenterVertically,
@@ -855,7 +973,8 @@ fun EditTile(
placeable.place(startPadding.roundToInt(), 0)
}
}
- .largeTilePadding(),
+ .largeTilePadding()
+ .graphicsLayer { this.alpha = alpha },
) {
// Icon
Box(Modifier.size(ToggleTargetSize)) {
@@ -873,7 +992,7 @@ fun EditTile(
label = tile.label.text,
secondaryLabel = tile.appName?.text,
colors = colors,
- modifier = Modifier.weight(1f).graphicsLayer { alpha = progress() },
+ modifier = Modifier.weight(1f).graphicsLayer { this.alpha = progress() },
)
}
}
@@ -892,9 +1011,9 @@ private fun MeasureScope.iconHorizontalCenter(containerSize: Int): Float {
CommonTileDefaults.TileStartPadding.toPx()
}
-private fun Modifier.tileBackground(color: Color): Modifier {
+private fun Modifier.tileBackground(color: () -> Color): Modifier {
// Clip tile contents from overflowing past the tile
- return clip(RoundedCornerShape(InactiveCornerRadius)).drawBehind { drawRect(color) }
+ return clip(RoundedCornerShape(InactiveCornerRadius)).drawBehind { drawRect(color()) }
}
private object EditModeTileDefaults {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/InfiniteGridLayout.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/InfiniteGridLayout.kt
index 27e609232a4c..233af548fff2 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/InfiniteGridLayout.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/InfiniteGridLayout.kt
@@ -42,7 +42,6 @@ import com.android.systemui.qs.panels.ui.viewmodel.EditTileViewModel
import com.android.systemui.qs.panels.ui.viewmodel.IconTilesViewModel
import com.android.systemui.qs.panels.ui.viewmodel.InfiniteGridViewModel
import com.android.systemui.qs.panels.ui.viewmodel.TileViewModel
-import com.android.systemui.qs.pipeline.domain.interactor.CurrentTilesInteractor.Companion.POSITION_AT_END
import com.android.systemui.qs.pipeline.shared.TileSpec
import com.android.systemui.qs.shared.ui.ElementKeys.toElementKey
import com.android.systemui.res.R
@@ -119,6 +118,7 @@ constructor(
isLastInRow = isLastInColumn,
),
detailsViewModel = detailsViewModel,
+ isVisible = listening,
)
}
}
@@ -170,7 +170,7 @@ constructor(
otherTiles = otherTiles,
columns = columns,
modifier = modifier,
- onAddTile = { onAddTile(it, POSITION_AT_END) },
+ onAddTile = onAddTile,
onRemoveTile = onRemoveTile,
onSetTiles = onSetTiles,
onResize = iconTilesViewModel::resize,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/Tile.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/Tile.kt
index 6bafd432669a..e24718285312 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/Tile.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/Tile.kt
@@ -139,6 +139,7 @@ fun Tile(
bounceableInfo: BounceableInfo,
tileHapticsViewModelFactoryProvider: TileHapticsViewModelFactoryProvider,
modifier: Modifier = Modifier,
+ isVisible: () -> Boolean = { true },
detailsViewModel: DetailsViewModel?,
) {
trace(tile.traceName) {
@@ -249,6 +250,7 @@ fun Tile(
onLongClick = longClick,
accessibilityUiState = uiState.accessibilityUiState,
squishiness = squishiness,
+ isVisible = isVisible,
)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/selection/MutableSelectionState.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/selection/MutableSelectionState.kt
index 3dfde86bf8d9..50b29557683d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/selection/MutableSelectionState.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/selection/MutableSelectionState.kt
@@ -16,15 +16,17 @@
package com.android.systemui.qs.panels.ui.compose.selection
-import androidx.compose.foundation.gestures.detectTapGestures
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.Stable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.input.pointer.pointerInput
+import com.android.systemui.common.ui.compose.gestures.detectEagerTapGestures
import com.android.systemui.qs.pipeline.shared.TileSpec
+import kotlinx.coroutines.delay
/** Creates the state of the current selected tile that is remembered across compositions. */
@Composable
@@ -38,6 +40,17 @@ class MutableSelectionState {
var selection by mutableStateOf<TileSpec?>(null)
private set
+ /**
+ * Whether the current selection is in placement mode or not.
+ *
+ * A tile in placement mode can be positioned by tapping at the desired location in the grid.
+ */
+ var placementEnabled by mutableStateOf(false)
+ private set
+
+ /** Latest event from coming from placement mode. */
+ var placementEvent by mutableStateOf<PlacementEvent?>(null)
+
val selected: Boolean
get() = selection != null
@@ -47,37 +60,122 @@ class MutableSelectionState {
fun unSelect() {
selection = null
+ exitPlacementMode()
}
-}
-/**
- * Listens for click events to select/unselect the given [TileSpec]. Use this on current tiles as
- * they can be selected.
- */
-fun Modifier.selectableTile(
- tileSpec: TileSpec,
- selectionState: MutableSelectionState,
- onClick: () -> Unit = {},
-): Modifier {
- return pointerInput(Unit) {
- detectTapGestures(
- onTap = {
- if (selectionState.selection == tileSpec) {
- selectionState.unSelect()
- } else {
- selectionState.select(tileSpec)
+ /** Selects [tileSpec] and enable placement mode. */
+ fun enterPlacementMode(tileSpec: TileSpec) {
+ selection = tileSpec
+ placementEnabled = true
+ }
+
+ /** Disable placement mode but maintains current selection. */
+ private fun exitPlacementMode() {
+ placementEnabled = false
+ }
+
+ fun togglePlacementMode(tileSpec: TileSpec) {
+ if (placementEnabled) exitPlacementMode() else enterPlacementMode(tileSpec)
+ }
+
+ suspend fun tileStateFor(
+ tileSpec: TileSpec,
+ previousState: TileState,
+ canShowRemovalBadge: Boolean,
+ ): TileState {
+ return when {
+ placementEnabled && selection == tileSpec -> TileState.Placeable
+ placementEnabled -> TileState.GreyedOut
+ selection == tileSpec -> {
+ if (previousState == TileState.None && canShowRemovalBadge) {
+ // The tile decoration is None if a tile is newly composed OR the removal
+ // badge can't be shown.
+ // For newly composed and selected tiles, such as dragged tiles or moved
+ // tiles from resizing, introduce a short delay. This avoids clipping issues
+ // on the border and resizing handle, as well as letting the selection
+ // animation play correctly.
+ delay(250)
}
- onClick()
+ TileState.Selected
}
- )
+ canShowRemovalBadge -> TileState.Removable
+ else -> TileState.None
+ }
+ }
+
+ /**
+ * Tap callback on a tile.
+ *
+ * Tiles can be selected and placed using placement mode.
+ */
+ fun onTap(tileSpec: TileSpec) {
+ when {
+ placementEnabled && selection == tileSpec -> {
+ exitPlacementMode()
+ }
+ placementEnabled -> {
+ selection?.let { placementEvent = PlacementEvent.PlaceToTileSpec(it, tileSpec) }
+ exitPlacementMode()
+ }
+ selection == tileSpec -> {
+ unSelect()
+ }
+ else -> {
+ select(tileSpec)
+ }
+ }
+ }
+
+ /**
+ * Tap on a position.
+ *
+ * Use on grid items not associated with a [TileSpec], such as a spacer. Spacers can't be
+ * selected, but selections can be moved to their position.
+ */
+ fun onTap(index: Int) {
+ when {
+ placementEnabled -> {
+ selection?.let { placementEvent = PlacementEvent.PlaceToIndex(it, index) }
+ exitPlacementMode()
+ }
+ selected -> {
+ unSelect()
+ }
+ }
}
}
+// Not using data classes here as distinct placement events may have the same moving spec and target
+@Stable
+sealed interface PlacementEvent {
+ val movingSpec: TileSpec
+
+ /** Placement event corresponding to [movingSpec] moving to [targetSpec]'s position */
+ class PlaceToTileSpec(override val movingSpec: TileSpec, val targetSpec: TileSpec) :
+ PlacementEvent
+
+ /** Placement event corresponding to [movingSpec] moving to [targetIndex] */
+ class PlaceToIndex(override val movingSpec: TileSpec, val targetIndex: Int) : PlacementEvent
+}
+
/**
- * Listens for click events to unselect any tile. Use this on available tiles as they can't be
- * selected.
+ * Listens for click events on selectable tiles.
+ *
+ * Use this on current tiles as they can be selected.
+ *
+ * @param tileSpec the [TileSpec] of the tile this modifier is applied to
+ * @param selectionState the [MutableSelectionState] representing the grid's selection
*/
@Composable
-fun Modifier.clearSelectionTile(selectionState: MutableSelectionState): Modifier {
- return pointerInput(Unit) { detectTapGestures(onTap = { selectionState.unSelect() }) }
+fun Modifier.selectableTile(tileSpec: TileSpec, selectionState: MutableSelectionState): Modifier {
+ return pointerInput(Unit) {
+ detectEagerTapGestures(
+ doubleTapEnabled = {
+ // Double tap enabled if where not in placement mode already
+ !selectionState.placementEnabled
+ },
+ onDoubleTap = { selectionState.enterPlacementMode(tileSpec) },
+ onTap = { selectionState.onTap(tileSpec) },
+ )
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/selection/Selection.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/selection/Selection.kt
index 57f63c755b43..8ffc4be88e7c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/selection/Selection.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/selection/Selection.kt
@@ -17,6 +17,7 @@
package com.android.systemui.qs.panels.ui.compose.selection
import androidx.compose.animation.animateColor
+import androidx.compose.animation.core.Animatable
import androidx.compose.animation.core.Transition
import androidx.compose.animation.core.animateFloat
import androidx.compose.animation.core.animateFloatAsState
@@ -37,9 +38,13 @@ import androidx.compose.material3.Icon
import androidx.compose.material3.LocalMinimumInteractiveComponentSize
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.State
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.drawBehind
@@ -73,7 +78,9 @@ import com.android.systemui.qs.panels.ui.compose.selection.SelectionDefaults.RES
import com.android.systemui.qs.panels.ui.compose.selection.SelectionDefaults.ResizingPillHeight
import com.android.systemui.qs.panels.ui.compose.selection.SelectionDefaults.ResizingPillWidth
import com.android.systemui.qs.panels.ui.compose.selection.SelectionDefaults.SelectedBorderWidth
+import com.android.systemui.qs.panels.ui.compose.selection.TileState.GreyedOut
import com.android.systemui.qs.panels.ui.compose.selection.TileState.None
+import com.android.systemui.qs.panels.ui.compose.selection.TileState.Placeable
import com.android.systemui.qs.panels.ui.compose.selection.TileState.Removable
import com.android.systemui.qs.panels.ui.compose.selection.TileState.Selected
import kotlin.math.cos
@@ -104,10 +111,11 @@ fun InteractiveTileContainer(
) {
val transition: Transition<TileState> = updateTransition(tileState)
val decorationColor by transition.animateColor()
- val decorationAngle by transition.animateAngle()
+ val decorationAngle by animateAngle(tileState)
val decorationSize by transition.animateSize()
val decorationOffset by transition.animateOffset()
- val decorationAlpha by transition.animateFloat { state -> if (state == None) 0f else 1f }
+ val decorationAlpha by
+ transition.animateFloat { state -> if (state == Removable || state == Selected) 1f else 0f }
val badgeIconAlpha by transition.animateFloat { state -> if (state == Removable) 1f else 0f }
val selectionBorderAlpha by
transition.animateFloat { state -> if (state == Selected) 1f else 0f }
@@ -282,27 +290,61 @@ private fun Modifier.resizable(selected: Boolean, state: ResizingState): Modifie
}
enum class TileState {
+ /** Tile is displayed as-is, no additional decoration needed. */
None,
+ /** Tile can be removed by the user. This is displayed by a badge in the upper end corner. */
Removable,
+ /**
+ * Tile is selected and resizable. One tile can be selected at a time in the grid. This is when
+ * we display the resizing handle and a highlighted border around the tile.
+ */
Selected,
+ /**
+ * Tile placeable. This state means that the grid is in placement mode and this tile is
+ * selected. It should be highlighted to stand out in the grid.
+ */
+ Placeable,
+ /**
+ * Tile is faded out. This state means that the grid is in placement mode and this tile isn't
+ * selected. It serves as a target to place the selected tile.
+ */
+ GreyedOut,
}
@Composable
private fun Transition<TileState>.animateColor(): State<Color> {
return animateColor { state ->
when (state) {
- None -> Color.Transparent
+ None,
+ GreyedOut -> Color.Transparent
Removable -> MaterialTheme.colorScheme.primaryContainer
- Selected -> MaterialTheme.colorScheme.primary
+ Selected,
+ Placeable -> MaterialTheme.colorScheme.primary
}
}
}
+/**
+ * Animate the angle of the tile decoration based on the previous state
+ *
+ * Some [TileState] don't have a visible decoration, and the angle should only animate when going
+ * between visible states.
+ */
@Composable
-private fun Transition<TileState>.animateAngle(): State<Float> {
- return animateFloat { state ->
- if (state == Removable) BADGE_ANGLE_RAD else RESIZING_PILL_ANGLE_RAD
+private fun animateAngle(tileState: TileState): State<Float> {
+ val animatable = remember { Animatable(0f) }
+ var animate by remember { mutableStateOf(false) }
+ LaunchedEffect(tileState) {
+ val targetAngle = tileState.decorationAngle()
+
+ if (targetAngle == null) {
+ animate = false
+ } else {
+ if (animate) animatable.animateTo(targetAngle) else animatable.snapTo(targetAngle)
+ animate = true
+ }
}
+ return animatable.asState()
}
@Composable
@@ -310,7 +352,9 @@ private fun Transition<TileState>.animateSize(): State<Size> {
return animateSize { state ->
with(LocalDensity.current) {
when (state) {
- None -> Size.Zero
+ None,
+ Placeable,
+ GreyedOut -> Size.Zero
Removable -> Size(BadgeSize.toPx())
Selected -> Size(ResizingPillWidth.toPx(), ResizingPillHeight.toPx())
}
@@ -323,7 +367,9 @@ private fun Transition<TileState>.animateOffset(): State<Offset> {
return animateOffset { state ->
with(LocalDensity.current) {
when (state) {
- None -> Offset.Zero
+ None,
+ Placeable,
+ GreyedOut -> Offset.Zero
Removable -> Offset(BadgeXOffset.toPx(), BadgeYOffset.toPx())
Selected -> Offset(-SelectedBorderWidth.toPx(), 0f)
}
@@ -331,6 +377,16 @@ private fun Transition<TileState>.animateOffset(): State<Offset> {
}
}
+private fun TileState.decorationAngle(): Float? {
+ return when (this) {
+ Removable -> BADGE_ANGLE_RAD
+ Selected -> RESIZING_PILL_ANGLE_RAD
+ None,
+ Placeable,
+ GreyedOut -> null // No visible decoration
+ }
+}
+
private fun Size(size: Float) = Size(size, size)
private fun offsetForAngle(angle: Float, radius: Float, center: Offset): Offset {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/DetailsViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/DetailsViewModel.kt
index 3287443f0405..60ca6e6e67a7 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/DetailsViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/DetailsViewModel.kt
@@ -23,11 +23,15 @@ import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.plugins.qs.TileDetailsViewModel
import com.android.systemui.qs.pipeline.domain.interactor.CurrentTilesInteractor
import com.android.systemui.qs.pipeline.shared.TileSpec
+import com.android.systemui.shade.domain.interactor.ShadeModeInteractor
import javax.inject.Inject
@SysUISingleton
@Stable
-class DetailsViewModel @Inject constructor(val currentTilesInteractor: CurrentTilesInteractor) {
+class DetailsViewModel @Inject constructor(
+ val currentTilesInteractor: CurrentTilesInteractor,
+ val shadeModeInteractor: ShadeModeInteractor
+) {
/**
* The current active [TileDetailsViewModel]. If it's `null`, it means the qs overlay is not
@@ -52,6 +56,10 @@ class DetailsViewModel @Inject constructor(val currentTilesInteractor: CurrentTi
* @see activeTileDetails
*/
fun onTileClicked(spec: TileSpec?): Boolean {
+ if (!shadeModeInteractor.isDualShade){
+ return false
+ }
+
if (spec == null) {
_activeTileDetails.value = null
return false
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/EditTileViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/EditTileViewModel.kt
index be6ce5c5b4f4..cf325f531c38 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/EditTileViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/EditTileViewModel.kt
@@ -66,6 +66,9 @@ data class EditTileViewModel(
) : CategoryAndName {
override val name
get() = label.text
+
+ val isRemovable
+ get() = availableEditActions.contains(AvailableEditActions.REMOVE)
}
enum class AvailableEditActions {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/dagger/QSPipelineModule.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/dagger/QSPipelineModule.kt
index 16aa99e22ae2..f155629e093f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/dagger/QSPipelineModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/dagger/QSPipelineModule.kt
@@ -35,8 +35,8 @@ import com.android.systemui.qs.pipeline.domain.interactor.CurrentTilesInteractor
import com.android.systemui.qs.pipeline.domain.interactor.CurrentTilesInteractorImpl
import com.android.systemui.qs.pipeline.domain.startable.QSPipelineCoreStartable
import com.android.systemui.qs.pipeline.shared.logging.QSPipelineLogger
-import com.android.systemui.qs.tiles.base.interactor.DisabledByPolicyInteractor
-import com.android.systemui.qs.tiles.base.interactor.DisabledByPolicyInteractorImpl
+import com.android.systemui.qs.tiles.base.domain.interactor.DisabledByPolicyInteractor
+import com.android.systemui.qs.tiles.base.domain.interactor.DisabledByPolicyInteractorImpl
import dagger.Binds
import dagger.Module
import dagger.Provides
diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractor.kt
index c70a854a2ca0..a3846e3a5903 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractor.kt
@@ -42,7 +42,7 @@ import com.android.systemui.qs.pipeline.domain.model.TileModel
import com.android.systemui.qs.pipeline.shared.QSPipelineFlagsRepository
import com.android.systemui.qs.pipeline.shared.TileSpec
import com.android.systemui.qs.pipeline.shared.logging.QSPipelineLogger
-import com.android.systemui.qs.tiles.di.NewQSTileFactory
+import com.android.systemui.qs.tiles.base.ui.model.NewQSTileFactory
import com.android.systemui.qs.toProto
import com.android.systemui.retail.data.repository.RetailModeRepository
import com.android.systemui.settings.UserTracker
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
index c60e3da9d833..49ec1baeeeee 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java
@@ -48,11 +48,13 @@ import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.qs.QSTile.BooleanState;
+import com.android.systemui.plugins.qs.TileDetailsViewModel;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.QSHost;
import com.android.systemui.qs.QsEventLogger;
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSTileImpl;
+import com.android.systemui.qs.tiles.dialog.CastDetailsViewModel;
import com.android.systemui.res.R;
import com.android.systemui.shade.domain.interactor.ShadeDialogContextInteractor;
import com.android.systemui.statusbar.connectivity.NetworkController;
@@ -93,6 +95,7 @@ public class CastTile extends QSTileImpl<BooleanState> {
private final ShadeDialogContextInteractor mShadeDialogContextInteractor;
private boolean mCastTransportAllowed;
private boolean mHotspotConnected;
+ private final CastDetailsViewModel.Factory mCastDetailsViewModelFactory;
@Inject
public CastTile(
@@ -113,7 +116,8 @@ public class CastTile extends QSTileImpl<BooleanState> {
ConnectivityRepository connectivityRepository,
TileJavaAdapter javaAdapter,
FeatureFlags featureFlags,
- ShadeDialogContextInteractor shadeDialogContextInteractor
+ ShadeDialogContextInteractor shadeDialogContextInteractor,
+ CastDetailsViewModel.Factory castDetailsViewModelFactory
) {
super(host, uiEventLogger, backgroundLooper, mainHandler, falsingManager, metricsLogger,
statusBarStateController, activityStarter, qsLogger);
@@ -124,6 +128,7 @@ public class CastTile extends QSTileImpl<BooleanState> {
mJavaAdapter = javaAdapter;
mFeatureFlags = featureFlags;
mShadeDialogContextInteractor = shadeDialogContextInteractor;
+ mCastDetailsViewModelFactory = castDetailsViewModelFactory;
mController.observe(this, mCallback);
mKeyguard.observe(this, mCallback);
if (!mFeatureFlags.isEnabled(SIGNAL_CALLBACK_DEPRECATION)) {
@@ -172,12 +177,7 @@ public class CastTile extends QSTileImpl<BooleanState> {
@Override
protected void handleClick(@Nullable Expandable expandable) {
- if (getState().state == Tile.STATE_UNAVAILABLE) {
- return;
- }
-
- List<CastDevice> activeDevices = getActiveDevices();
- if (willPopDialog()) {
+ handleClick(() -> {
if (!mKeyguard.isShowing()) {
showDialog(expandable);
} else {
@@ -187,16 +187,44 @@ public class CastTile extends QSTileImpl<BooleanState> {
showDialog(null /* view */);
});
}
+ });
+ }
+
+ @Override
+ public boolean getDetailsViewModel(Consumer<TileDetailsViewModel> callback) {
+ CastDetailsViewModel viewModel = mCastDetailsViewModelFactory
+ .create(mShadeDialogContextInteractor.getContext(), ROUTE_TYPE_REMOTE_DISPLAY);
+ handleClick(() -> {
+ if (!mKeyguard.isShowing()) {
+ callback.accept(viewModel);
+ } else {
+ mActivityStarter.dismissKeyguardThenExecute(() -> {
+ callback.accept(viewModel);
+ return false;
+ }, null /* cancelAction */, true/* afterKeyguardGone */);
+ }
+ });
+ return true;
+ }
+
+ private void handleClick(Runnable showPromptCallback) {
+ if (getState().state == Tile.STATE_UNAVAILABLE) {
+ return;
+ }
+
+ List<CastDevice> activeDevices = getActiveDevices();
+ if (willShowPrompt()) {
+ showPromptCallback.run();
} else {
mController.stopCasting(activeDevices.get(0), StopReason.STOP_QS_TILE);
}
}
- // We want to pop up the media route selection dialog if we either have no active devices
- // (neither routes nor projection), or if we have an active route. In other cases, we assume
- // that a projection is active. This is messy, but this tile never correctly handled the
- // case where multiple devices were active :-/.
- private boolean willPopDialog() {
+ // We want to pop up the media route selection dialog (or show the cast details view) if we
+ // either have no active devices (neither routes nor projection), or if we have an active
+ // route. In other cases, we assume that a projection is active. This is messy, but this tile
+ // never correctly handled the case where multiple devices were active :-/.
+ private boolean willShowPrompt() {
List<CastDevice> activeDevices = getActiveDevices();
return activeDevices.isEmpty() || (activeDevices.get(0).getTag() instanceof RouteInfo);
}
@@ -303,7 +331,7 @@ public class CastTile extends QSTileImpl<BooleanState> {
state.secondaryLabel = "";
}
state.expandedAccessibilityClassName = Button.class.getName();
- state.forceExpandIcon = willPopDialog();
+ state.forceExpandIcon = willShowPrompt();
} else {
state.state = Tile.STATE_UNAVAILABLE;
String noWifi = mContext.getString(R.string.quick_settings_cast_no_network);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/ModesDndTile.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/ModesDndTile.kt
index eb53d81b674f..0236256ec9ae 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/ModesDndTile.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/ModesDndTile.kt
@@ -37,14 +37,15 @@ import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.qs.QSHost
import com.android.systemui.qs.QsEventLogger
import com.android.systemui.qs.asQSTileIcon
+import com.android.systemui.qs.flags.QsInCompose
import com.android.systemui.qs.logging.QSLogger
import com.android.systemui.qs.tileimpl.QSTileImpl
+import com.android.systemui.qs.tiles.base.shared.model.QSTileConfigProvider
+import com.android.systemui.qs.tiles.base.shared.model.QSTileState
import com.android.systemui.qs.tiles.impl.modes.domain.interactor.ModesDndTileDataInteractor
import com.android.systemui.qs.tiles.impl.modes.domain.interactor.ModesDndTileUserActionInteractor
import com.android.systemui.qs.tiles.impl.modes.domain.model.ModesDndTileModel
import com.android.systemui.qs.tiles.impl.modes.ui.ModesDndTileMapper
-import com.android.systemui.qs.tiles.viewmodel.QSTileConfigProvider
-import com.android.systemui.qs.tiles.viewmodel.QSTileState
import com.android.systemui.res.R
import javax.inject.Inject
import kotlinx.coroutines.runBlocking
@@ -88,7 +89,11 @@ constructor(
init {
lifecycle.coroutineScope.launch {
- lifecycle.repeatOnLifecycle(Lifecycle.State.RESUMED) {
+ lifecycle.repeatOnLifecycle(
+ // TODO: b/403434908 - Workaround for "not listening to tile updates". Can be reset
+ // to RESUMED if either b/403434908 is fixed or QsInCompose is inlined.
+ if (QsInCompose.isEnabled) Lifecycle.State.RESUMED else Lifecycle.State.CREATED
+ ) {
dataInteractor.tileData().collect { refreshState(it) }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/ModesTile.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/ModesTile.kt
index ad5dd27f07c2..6a842c31a3aa 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/ModesTile.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/ModesTile.kt
@@ -31,7 +31,6 @@ import com.android.systemui.animation.Expandable
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.modes.shared.ModesUi
-import com.android.systemui.modes.shared.ModesUiIcons
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.plugins.FalsingManager
import com.android.systemui.plugins.qs.QSTile
@@ -40,15 +39,16 @@ import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.qs.QSHost
import com.android.systemui.qs.QsEventLogger
import com.android.systemui.qs.asQSTileIcon
+import com.android.systemui.qs.flags.QsInCompose
import com.android.systemui.qs.logging.QSLogger
import com.android.systemui.qs.tileimpl.QSTileImpl
+import com.android.systemui.qs.tiles.base.shared.model.QSTileConfigProvider
+import com.android.systemui.qs.tiles.base.shared.model.QSTileState
import com.android.systemui.qs.tiles.dialog.ModesDetailsViewModel
import com.android.systemui.qs.tiles.impl.modes.domain.interactor.ModesTileDataInteractor
import com.android.systemui.qs.tiles.impl.modes.domain.interactor.ModesTileUserActionInteractor
import com.android.systemui.qs.tiles.impl.modes.domain.model.ModesTileModel
-import com.android.systemui.qs.tiles.impl.modes.ui.ModesTileMapper
-import com.android.systemui.qs.tiles.viewmodel.QSTileConfigProvider
-import com.android.systemui.qs.tiles.viewmodel.QSTileState
+import com.android.systemui.qs.tiles.impl.modes.ui.mapper.ModesTileMapper
import com.android.systemui.res.R
import com.android.systemui.statusbar.policy.ui.dialog.viewmodel.ModesDialogViewModel
import javax.inject.Inject
@@ -88,10 +88,12 @@ constructor(
private val config = qsTileConfigProvider.getConfig(TILE_SPEC)
init {
- /* Check if */ ModesUiIcons.isUnexpectedlyInLegacyMode()
-
lifecycle.coroutineScope.launch {
- lifecycle.repeatOnLifecycle(Lifecycle.State.RESUMED) {
+ lifecycle.repeatOnLifecycle(
+ // TODO: b/403434908 - Workaround for "not listening to tile updates". Can be reset
+ // to RESUMED if either b/403434908 is fixed or QsInCompose is inlined.
+ if (QsInCompose.isEnabled) Lifecycle.State.RESUMED else Lifecycle.State.CREATED
+ ) {
dataInteractor.tileData().collect { refreshState(it) }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/NotesTile.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/NotesTile.kt
index 5ba1527dbf69..c021b2270b7b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/NotesTile.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/NotesTile.kt
@@ -33,12 +33,12 @@ import com.android.systemui.qs.QSHost
import com.android.systemui.qs.QsEventLogger
import com.android.systemui.qs.logging.QSLogger
import com.android.systemui.qs.tileimpl.QSTileImpl
-import com.android.systemui.qs.tiles.impl.notes.domain.NotesTileMapper
+import com.android.systemui.qs.tiles.base.shared.model.QSTileConfigProvider
+import com.android.systemui.qs.tiles.base.shared.model.QSTileState
import com.android.systemui.qs.tiles.impl.notes.domain.interactor.NotesTileDataInteractor
import com.android.systemui.qs.tiles.impl.notes.domain.interactor.NotesTileUserActionInteractor
import com.android.systemui.qs.tiles.impl.notes.domain.model.NotesTileModel
-import com.android.systemui.qs.tiles.viewmodel.QSTileConfigProvider
-import com.android.systemui.qs.tiles.viewmodel.QSTileState
+import com.android.systemui.qs.tiles.impl.notes.ui.mapper.NotesTileMapper
import com.android.systemui.res.R
import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserInputHandler.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/domain/actions/QSTileIntentUserInputHandler.kt
index 972b20e138d9..386e1a448611 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserInputHandler.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/domain/actions/QSTileIntentUserInputHandler.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2025 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.qs.tiles.base.actions
+package com.android.systemui.qs.tiles.base.domain.actions
import android.app.PendingIntent
import android.content.Intent
@@ -36,14 +36,14 @@ interface QSTileIntentUserInputHandler {
fun handle(
expandable: Expandable?,
intent: Intent,
- dismissShadeShowOverLockScreenWhenLocked: Boolean = false
+ dismissShadeShowOverLockScreenWhenLocked: Boolean = false,
)
/** @param requestLaunchingDefaultActivity used in case !pendingIndent.isActivity */
fun handle(
expandable: Expandable?,
pendingIntent: PendingIntent,
- requestLaunchingDefaultActivity: Boolean = false
+ requestLaunchingDefaultActivity: Boolean = false,
)
}
@@ -59,7 +59,7 @@ constructor(
override fun handle(
expandable: Expandable?,
intent: Intent,
- dismissShadeShowOverLockScreenWhenLocked: Boolean
+ dismissShadeShowOverLockScreenWhenLocked: Boolean,
) {
val animationController: ActivityTransitionAnimator.Controller? =
expandable?.activityTransitionController(
@@ -70,7 +70,7 @@ constructor(
intent,
true /* dismissShade */,
animationController,
- true /* showOverLockscreenWhenLocked */
+ true, /* showOverLockscreenWhenLocked */
)
} else {
activityStarter.postStartActivityDismissingKeyguard(intent, 0, animationController)
@@ -81,7 +81,7 @@ constructor(
override fun handle(
expandable: Expandable?,
pendingIntent: PendingIntent,
- requestLaunchingDefaultActivity: Boolean
+ requestLaunchingDefaultActivity: Boolean,
) {
if (pendingIntent.isActivity) {
val animationController: ActivityTransitionAnimator.Controller? =
@@ -101,7 +101,7 @@ constructor(
packageManager.queryIntentActivitiesAsUser(
intent,
PackageManager.ResolveInfoFlags.of(0L),
- userHandle.identifier
+ userHandle.identifier,
)
intents
.firstOrNull { it.activityInfo.exported }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/interactor/DisabledByPolicyInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/domain/interactor/DisabledByPolicyInteractor.kt
index 45775272e01f..da4d3d408c84 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/interactor/DisabledByPolicyInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/domain/interactor/DisabledByPolicyInteractor.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2025 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.qs.tiles.base.interactor
+package com.android.systemui.qs.tiles.base.domain.interactor
import android.content.Context
import android.os.UserHandle
@@ -26,7 +26,7 @@ import com.android.settingslib.RestrictedLockUtilsInternal
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.plugins.ActivityStarter
-import com.android.systemui.qs.tiles.base.interactor.DisabledByPolicyInteractor.PolicyResult
+import com.android.systemui.qs.tiles.base.domain.interactor.DisabledByPolicyInteractor.PolicyResult
import com.android.systemui.shade.ShadeDisplayAware
import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
@@ -110,17 +110,9 @@ class RestrictedLockProxy @Inject constructor(@ShadeDisplayAware private val con
@WorkerThread
fun hasBaseUserRestriction(userId: Int, userRestriction: String?): Boolean =
- RestrictedLockUtilsInternal.hasBaseUserRestriction(
- context,
- userRestriction,
- userId,
- )
+ RestrictedLockUtilsInternal.hasBaseUserRestriction(context, userRestriction, userId)
@WorkerThread
fun getEnforcedAdmin(userId: Int, userRestriction: String?): EnforcedAdmin? =
- RestrictedLockUtilsInternal.checkIfRestrictionEnforced(
- context,
- userRestriction,
- userId,
- )
+ RestrictedLockUtilsInternal.checkIfRestrictionEnforced(context, userRestriction, userId)
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/interactor/QSTileDataInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/domain/interactor/QSTileDataInteractor.kt
index 9a776f2832d4..1eb5315a5512 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/interactor/QSTileDataInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/domain/interactor/QSTileDataInteractor.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2025 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.
@@ -14,9 +14,10 @@
* limitations under the License.
*/
-package com.android.systemui.qs.tiles.base.interactor
+package com.android.systemui.qs.tiles.base.domain.interactor
import android.os.UserHandle
+import com.android.systemui.qs.tiles.base.domain.model.DataUpdateTrigger
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flowOf
@@ -49,10 +50,11 @@ interface QSTileAvailabilityInteractor {
fun availability(user: UserHandle): Flow<Boolean>
companion object {
- val AlwaysAvailableInteractor = object : QSTileAvailabilityInteractor {
- override fun availability(user: UserHandle): Flow<Boolean> {
- return flowOf(true)
+ val AlwaysAvailableInteractor =
+ object : QSTileAvailabilityInteractor {
+ override fun availability(user: UserHandle): Flow<Boolean> {
+ return flowOf(true)
+ }
}
- }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/interactor/QSTileUserActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/domain/interactor/QSTileUserActionInteractor.kt
index 8ad4e16291c2..e49616c8326f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/interactor/QSTileUserActionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/domain/interactor/QSTileUserActionInteractor.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2025 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.
@@ -14,10 +14,10 @@
* limitations under the License.
*/
-package com.android.systemui.qs.tiles.base.interactor
+package com.android.systemui.qs.tiles.base.domain.interactor
import android.annotation.WorkerThread
-import com.android.systemui.plugins.qs.TileDetailsViewModel
+import com.android.systemui.qs.tiles.base.domain.model.QSTileInput
interface QSTileUserActionInteractor<DATA_TYPE> {
/**
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/interactor/DataUpdateTrigger.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/domain/model/DataUpdateTrigger.kt
index 4f25d3cde6c3..a77e551a4a33 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/interactor/DataUpdateTrigger.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/domain/model/DataUpdateTrigger.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2025 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.qs.tiles.base.interactor
+package com.android.systemui.qs.tiles.base.domain.model
/** Event that triggers data update */
sealed interface DataUpdateTrigger {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/interactor/QSTileInput.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/domain/model/QSTileInput.kt
index 77ff6092063f..59236be8ebab 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/interactor/QSTileInput.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/domain/model/QSTileInput.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2025 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.
@@ -14,14 +14,10 @@
* limitations under the License.
*/
-package com.android.systemui.qs.tiles.base.interactor
+package com.android.systemui.qs.tiles.base.domain.model
import android.os.UserHandle
-import com.android.systemui.qs.tiles.viewmodel.QSTileUserAction
+import com.android.systemui.qs.tiles.base.shared.model.QSTileUserAction
/** @see QSTileUserActionInteractor.handleInput */
-data class QSTileInput<T>(
- val user: UserHandle,
- val action: QSTileUserAction,
- val data: T,
-)
+data class QSTileInput<T>(val user: UserHandle, val action: QSTileUserAction, val data: T)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/logging/QSTileLogger.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/shared/logging/QSTileLogger.kt
index 8ec8a6d1e730..04d40eeb3800 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/logging/QSTileLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/shared/logging/QSTileLogger.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.qs.tiles.base.logging
+package com.android.systemui.qs.tiles.base.shared.logging
import androidx.annotation.GuardedBy
import com.android.systemui.dagger.SysUISingleton
@@ -24,8 +24,8 @@ import com.android.systemui.log.core.LogLevel
import com.android.systemui.log.dagger.QSTilesLogBuffers
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.qs.pipeline.shared.TileSpec
-import com.android.systemui.qs.tiles.viewmodel.QSTileState
-import com.android.systemui.qs.tiles.viewmodel.QSTileUserAction
+import com.android.systemui.qs.tiles.base.shared.model.QSTileState
+import com.android.systemui.qs.tiles.base.shared.model.QSTileUserAction
import com.android.systemui.statusbar.StatusBarState
import javax.inject.Inject
@@ -65,22 +65,19 @@ constructor(
"statusBarState=${StatusBarState.toString(int1)}, " +
"hasState=$bool1, " +
"hasData=$bool2"
- }
+ },
)
}
/** Tracks user action when it's rejected by false gestures */
- fun logUserActionRejectedByFalsing(
- userAction: QSTileUserAction,
- tileSpec: TileSpec,
- ) {
+ fun logUserActionRejectedByFalsing(userAction: QSTileUserAction, tileSpec: TileSpec) {
tileSpec
.getLogBuffer()
.log(
tileSpec.getLogTag(),
LogLevel.DEBUG,
{ str1 = userAction.toLogString() },
- { "tile $str1: rejected by falsing" }
+ { "tile $str1: rejected by falsing" },
)
}
@@ -96,7 +93,7 @@ constructor(
tileSpec.getLogTag(),
LogLevel.DEBUG,
{ str1 = userAction.toLogString() },
- { "tile $str1: rejected by policy, restriction: $restriction" }
+ { "tile $str1: rejected by policy, restriction: $restriction" },
)
}
@@ -124,7 +121,7 @@ constructor(
"statusBarState=${StatusBarState.toString(int1)}, " +
"state=$str2, " +
"data=$str3"
- }
+ },
)
}
@@ -141,11 +138,7 @@ constructor(
}
/** Tracks state changes based on the data and trigger event. */
- fun <T> logStateUpdate(
- tileSpec: TileSpec,
- tileState: QSTileState,
- data: T,
- ) {
+ fun <T> logStateUpdate(tileSpec: TileSpec, tileState: QSTileState, data: T) {
tileSpec
.getLogBuffer()
.log(
@@ -155,41 +148,23 @@ constructor(
str1 = tileState.toLogString()
str2 = data.toString().take(DATA_MAX_LENGTH)
},
- { "tile state update: state=$str1, data=$str2" }
+ { "tile state update: state=$str1, data=$str2" },
)
}
- fun logError(
- tileSpec: TileSpec,
- message: String,
- error: Throwable,
- ) {
- tileSpec
- .getLogBuffer()
- .log(
- tileSpec.getLogTag(),
- LogLevel.ERROR,
- {},
- { message },
- error,
- )
+ fun logError(tileSpec: TileSpec, message: String, error: Throwable) {
+ tileSpec.getLogBuffer().log(tileSpec.getLogTag(), LogLevel.ERROR, {}, { message }, error)
}
/** Log with level [LogLevel.WARNING] */
- fun logWarning(
- tileSpec: TileSpec,
- message: String,
- ) {
+ fun logWarning(tileSpec: TileSpec, message: String) {
tileSpec
.getLogBuffer()
.log(tileSpec.getLogTag(), LogLevel.WARNING, { str1 = message }, { str1!! })
}
/** Log with level [LogLevel.INFO] */
- fun logInfo(
- tileSpec: TileSpec,
- message: String,
- ) {
+ fun logInfo(tileSpec: TileSpec, message: String) {
tileSpec
.getLogBuffer()
.log(tileSpec.getLogTag(), LogLevel.INFO, { str1 = message }, { str1!! })
@@ -214,7 +189,7 @@ constructor(
factory.create(
this.getLogTag(),
BUFFER_MAX_SIZE /* maxSize */,
- false /* systrace */
+ false, /* systrace */
)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfig.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/shared/model/QSTileConfig.kt
index 3a9cb170040c..7c2a08f6c4a2 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/shared/model/QSTileConfig.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2025 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.qs.tiles.viewmodel
+package com.android.systemui.qs.tiles.base.shared.model
import android.content.res.Resources
import androidx.annotation.DrawableRes
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfigProvider.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/shared/model/QSTileConfigProvider.kt
index 4dbddd91092a..e2d1605ca1fa 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfigProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/shared/model/QSTileConfigProvider.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2025 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.qs.tiles.viewmodel
+package com.android.systemui.qs.tiles.base.shared.model
import com.android.internal.util.Preconditions
import com.android.systemui.dagger.SysUISingleton
@@ -51,7 +51,7 @@ constructor(
val keyTileSpec = entry.key
Preconditions.checkArgument(
configTileSpec == keyTileSpec,
- "A wrong config is injected keySpec=$keyTileSpec configSpec=$configTileSpec"
+ "A wrong config is injected keySpec=$keyTileSpec configSpec=$configTileSpec",
)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileCoroutineScopeFactory.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/shared/model/QSTileCoroutineScopeFactory.kt
index 5f476ea7e274..018a06f94190 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileCoroutineScopeFactory.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/shared/model/QSTileCoroutineScopeFactory.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.qs.tiles.base.viewmodel
+package com.android.systemui.qs.tiles.base.shared.model
import com.android.systemui.coroutines.newTracingContext
import com.android.systemui.dagger.qualifiers.Background
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/di/QSTileScope.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/shared/model/QSTileScope.kt
index a412de364c19..a0caedb21d29 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/di/QSTileScope.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/shared/model/QSTileScope.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.qs.tiles.impl.di
+package com.android.systemui.qs.tiles.base.shared.model
import javax.inject.Scope
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileState.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/shared/model/QSTileState.kt
index c6af729cd4a7..5a19eae73d5e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileState.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/shared/model/QSTileState.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2025 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.qs.tiles.viewmodel
+package com.android.systemui.qs.tiles.base.shared.model
import android.content.res.Resources
import android.content.res.Resources.Theme
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileUserAction.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/shared/model/QSTileUserAction.kt
index bf3bc73cf72e..bbf04f63fa49 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileUserAction.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/shared/model/QSTileUserAction.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2025 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.qs.tiles.viewmodel
+package com.android.systemui.qs.tiles.base.shared.model
import com.android.systemui.animation.Expandable
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/analytics/QSTileAnalytics.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/ui/analytics/QSTileAnalytics.kt
index 1d427775dad6..86991485de9d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/analytics/QSTileAnalytics.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/ui/analytics/QSTileAnalytics.kt
@@ -14,22 +14,18 @@
* limitations under the License.
*/
-package com.android.systemui.qs.tiles.base.analytics
+package com.android.systemui.qs.tiles.base.ui.analytics
import com.android.internal.logging.UiEventLogger
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.qs.QSEvent
-import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
-import com.android.systemui.qs.tiles.viewmodel.QSTileUserAction
+import com.android.systemui.qs.tiles.base.shared.model.QSTileConfig
+import com.android.systemui.qs.tiles.base.shared.model.QSTileUserAction
import javax.inject.Inject
/** Tracks QS tiles analytic events to [UiEventLogger]. */
@SysUISingleton
-class QSTileAnalytics
-@Inject
-constructor(
- private val uiEventLogger: UiEventLogger,
-) {
+class QSTileAnalytics @Inject constructor(private val uiEventLogger: UiEventLogger) {
fun trackUserAction(config: QSTileConfig, action: QSTileUserAction) {
logAction(config, action)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/di/NewQSTileFactory.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/ui/model/NewQSTileFactory.kt
index f65fdb540527..fca7abef5d32 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/di/NewQSTileFactory.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/ui/model/NewQSTileFactory.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2025 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.
@@ -14,18 +14,18 @@
* limitations under the License.
*/
-package com.android.systemui.qs.tiles.di
+package com.android.systemui.qs.tiles.base.ui.model
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.plugins.qs.QSFactory
import com.android.systemui.plugins.qs.QSTile
import com.android.systemui.qs.pipeline.shared.QSPipelineFlagsRepository
import com.android.systemui.qs.pipeline.shared.TileSpec
-import com.android.systemui.qs.tiles.base.viewmodel.QSTileViewModelFactory
-import com.android.systemui.qs.tiles.viewmodel.QSTileConfigProvider
-import com.android.systemui.qs.tiles.viewmodel.QSTileViewModel
-import com.android.systemui.qs.tiles.viewmodel.QSTileViewModelAdapter
-import com.android.systemui.qs.tiles.viewmodel.StubQSTileViewModel
+import com.android.systemui.qs.tiles.base.shared.model.QSTileConfigProvider
+import com.android.systemui.qs.tiles.base.ui.viewmodel.QSTileViewModel
+import com.android.systemui.qs.tiles.base.ui.viewmodel.QSTileViewModelAdapter
+import com.android.systemui.qs.tiles.base.ui.viewmodel.QSTileViewModelFactory
+import com.android.systemui.qs.tiles.base.ui.viewmodel.StubQSTileViewModel
import javax.inject.Inject
import javax.inject.Provider
@@ -58,8 +58,7 @@ constructor(
is TileSpec.PlatformTileSpec ->
tileMap[tileSpec]?.get()?.takeIf { it !is StubQSTileViewModel }
is TileSpec.Invalid -> null
- }
- ?: return null
+ } ?: return null
return adapterFactory.create(viewModel)
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/di/QSTileComponent.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/ui/model/QSTileComponent.kt
index 5f692f2f5a73..f63c2db70131 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/di/QSTileComponent.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/ui/model/QSTileComponent.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2025 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.
@@ -14,12 +14,11 @@
* limitations under the License.
*/
-package com.android.systemui.qs.tiles.impl.di
+package com.android.systemui.qs.tiles.base.ui.model
-import com.android.systemui.plugins.qs.TileDetailsViewModel
-import com.android.systemui.qs.tiles.base.interactor.QSTileDataInteractor
-import com.android.systemui.qs.tiles.base.interactor.QSTileDataToStateMapper
-import com.android.systemui.qs.tiles.base.interactor.QSTileUserActionInteractor
+import com.android.systemui.qs.tiles.base.domain.interactor.QSTileDataInteractor
+import com.android.systemui.qs.tiles.base.domain.interactor.QSTileUserActionInteractor
+import com.android.systemui.qs.tiles.base.shared.model.QSTileScope
import kotlinx.coroutines.CoroutineScope
/**
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/interactor/QSTileDataToStateMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/ui/model/QSTileDataToStateMapper.kt
index 2bc664311644..7be2ed114130 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/interactor/QSTileDataToStateMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/ui/model/QSTileDataToStateMapper.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2025 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.
@@ -14,11 +14,11 @@
* limitations under the License.
*/
-package com.android.systemui.qs.tiles.base.interactor
+package com.android.systemui.qs.tiles.base.ui.model
import androidx.annotation.WorkerThread
-import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
-import com.android.systemui.qs.tiles.viewmodel.QSTileState
+import com.android.systemui.qs.tiles.base.shared.model.QSTileConfig
+import com.android.systemui.qs.tiles.base.shared.model.QSTileState
interface QSTileDataToStateMapper<DATA_TYPE> {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/di/QSTilesModule.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/ui/model/QSTilesModule.kt
index 222fa3efbe94..6b31539f1a03 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/di/QSTilesModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/ui/model/QSTilesModule.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2025 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.
@@ -14,20 +14,20 @@
* limitations under the License.
*/
-package com.android.systemui.qs.tiles.di
+package com.android.systemui.qs.tiles.base.ui.model
import android.content.Context
import android.content.res.Resources.Theme
import com.android.systemui.qs.external.CustomTileStatePersister
import com.android.systemui.qs.external.CustomTileStatePersisterImpl
-import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandler
-import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandlerImpl
-import com.android.systemui.qs.tiles.base.interactor.QSTileAvailabilityInteractor
-import com.android.systemui.qs.tiles.impl.custom.di.CustomTileComponent
-import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
-import com.android.systemui.qs.tiles.viewmodel.QSTileConfigProvider
-import com.android.systemui.qs.tiles.viewmodel.QSTileConfigProviderImpl
-import com.android.systemui.qs.tiles.viewmodel.QSTileViewModel
+import com.android.systemui.qs.tiles.base.domain.actions.QSTileIntentUserInputHandler
+import com.android.systemui.qs.tiles.base.domain.actions.QSTileIntentUserInputHandlerImpl
+import com.android.systemui.qs.tiles.base.domain.interactor.QSTileAvailabilityInteractor
+import com.android.systemui.qs.tiles.base.shared.model.QSTileConfig
+import com.android.systemui.qs.tiles.base.shared.model.QSTileConfigProvider
+import com.android.systemui.qs.tiles.base.shared.model.QSTileConfigProviderImpl
+import com.android.systemui.qs.tiles.base.ui.viewmodel.QSTileViewModel
+import com.android.systemui.qs.tiles.impl.custom.ui.model.CustomTileComponent
import com.android.systemui.shade.ShadeDisplayAware
import dagger.Binds
import dagger.Module
@@ -35,12 +35,7 @@ import dagger.Provides
import dagger.multibindings.Multibinds
/** Module listing subcomponents */
-@Module(
- subcomponents =
- [
- CustomTileComponent::class,
- ]
-)
+@Module(subcomponents = [CustomTileComponent::class])
interface QSTilesModule {
/**
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/ui/viewmodel/QSTileViewModel.kt
index 7a533883444e..51ebf149ea23 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/ui/viewmodel/QSTileViewModel.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2025 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.
@@ -14,10 +14,13 @@
* limitations under the License.
*/
-package com.android.systemui.qs.tiles.viewmodel
+package com.android.systemui.qs.tiles.base.ui.viewmodel
import android.os.UserHandle
import com.android.systemui.plugins.qs.TileDetailsViewModel
+import com.android.systemui.qs.tiles.base.shared.model.QSTileConfig
+import com.android.systemui.qs.tiles.base.shared.model.QSTileState
+import com.android.systemui.qs.tiles.base.shared.model.QSTileUserAction
import kotlinx.coroutines.flow.StateFlow
/**
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelAdapter.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/ui/viewmodel/QSTileViewModelAdapter.kt
index e607eae8f38d..f604de654085 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelAdapter.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/ui/viewmodel/QSTileViewModelAdapter.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2025 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.qs.tiles.viewmodel
+package com.android.systemui.qs.tiles.base.ui.viewmodel
import android.content.Context
import android.os.UserHandle
@@ -31,6 +31,10 @@ import com.android.systemui.qs.QSHost
import com.android.systemui.qs.tileimpl.QSTileImpl.DrawableIcon
import com.android.systemui.qs.tileimpl.QSTileImpl.DrawableIconWithRes
import com.android.systemui.qs.tileimpl.QSTileImpl.ResourceIcon
+import com.android.systemui.qs.tiles.base.shared.model.QSTileConfig
+import com.android.systemui.qs.tiles.base.shared.model.QSTileState
+import com.android.systemui.qs.tiles.base.shared.model.QSTileUIConfig
+import com.android.systemui.qs.tiles.base.shared.model.QSTileUserAction
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelFactory.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/ui/viewmodel/QSTileViewModelFactory.kt
index 7f475f31b940..7404710f7e4d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelFactory.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/ui/viewmodel/QSTileViewModelFactory.kt
@@ -14,26 +14,25 @@
* limitations under the License.
*/
-package com.android.systemui.qs.tiles.base.viewmodel
+package com.android.systemui.qs.tiles.base.ui.viewmodel
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.UiBackground
import com.android.systemui.plugins.FalsingManager
import com.android.systemui.plugins.qs.TileDetailsViewModel
import com.android.systemui.qs.pipeline.shared.TileSpec
-import com.android.systemui.qs.tiles.base.analytics.QSTileAnalytics
-import com.android.systemui.qs.tiles.base.interactor.DisabledByPolicyInteractor
-import com.android.systemui.qs.tiles.base.interactor.QSTileDataInteractor
-import com.android.systemui.qs.tiles.base.interactor.QSTileDataToStateMapper
-import com.android.systemui.qs.tiles.base.interactor.QSTileUserActionInteractor
-import com.android.systemui.qs.tiles.base.logging.QSTileLogger
-import com.android.systemui.qs.tiles.impl.custom.di.CustomTileComponent
-import com.android.systemui.qs.tiles.impl.custom.di.QSTileConfigModule
-import com.android.systemui.qs.tiles.impl.custom.domain.entity.CustomTileDataModel
-import com.android.systemui.qs.tiles.impl.di.QSTileComponent
-import com.android.systemui.qs.tiles.viewmodel.QSTileConfigProvider
-import com.android.systemui.qs.tiles.viewmodel.QSTileState
-import com.android.systemui.qs.tiles.viewmodel.QSTileViewModel
+import com.android.systemui.qs.tiles.base.domain.interactor.DisabledByPolicyInteractor
+import com.android.systemui.qs.tiles.base.domain.interactor.QSTileDataInteractor
+import com.android.systemui.qs.tiles.base.domain.interactor.QSTileUserActionInteractor
+import com.android.systemui.qs.tiles.base.shared.logging.QSTileLogger
+import com.android.systemui.qs.tiles.base.shared.model.QSTileConfigProvider
+import com.android.systemui.qs.tiles.base.shared.model.QSTileCoroutineScopeFactory
+import com.android.systemui.qs.tiles.base.ui.analytics.QSTileAnalytics
+import com.android.systemui.qs.tiles.base.ui.model.QSTileComponent
+import com.android.systemui.qs.tiles.base.ui.model.QSTileDataToStateMapper
+import com.android.systemui.qs.tiles.impl.custom.domain.model.CustomTileDataModel
+import com.android.systemui.qs.tiles.impl.custom.shared.model.QSTileConfigModule
+import com.android.systemui.qs.tiles.impl.custom.ui.model.CustomTileComponent
import com.android.systemui.user.data.repository.UserRepository
import com.android.systemui.util.time.SystemClock
import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelImpl.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/ui/viewmodel/QSTileViewModelImpl.kt
index 3866c17b655f..f8723e422fe3 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/base/viewmodel/QSTileViewModelImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/ui/viewmodel/QSTileViewModelImpl.kt
@@ -14,26 +14,26 @@
* limitations under the License.
*/
-package com.android.systemui.qs.tiles.base.viewmodel
+package com.android.systemui.qs.tiles.base.ui.viewmodel
+import android.annotation.SuppressLint
import android.os.UserHandle
import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.systemui.Dumpable
import com.android.systemui.plugins.FalsingManager
import com.android.systemui.plugins.qs.TileDetailsViewModel
-import com.android.systemui.qs.tiles.base.analytics.QSTileAnalytics
-import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
-import com.android.systemui.qs.tiles.base.interactor.DisabledByPolicyInteractor
-import com.android.systemui.qs.tiles.base.interactor.QSTileDataInteractor
-import com.android.systemui.qs.tiles.base.interactor.QSTileDataToStateMapper
-import com.android.systemui.qs.tiles.base.interactor.QSTileInput
-import com.android.systemui.qs.tiles.base.interactor.QSTileUserActionInteractor
-import com.android.systemui.qs.tiles.base.logging.QSTileLogger
-import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
-import com.android.systemui.qs.tiles.viewmodel.QSTilePolicy
-import com.android.systemui.qs.tiles.viewmodel.QSTileState
-import com.android.systemui.qs.tiles.viewmodel.QSTileUserAction
-import com.android.systemui.qs.tiles.viewmodel.QSTileViewModel
+import com.android.systemui.qs.tiles.base.domain.interactor.DisabledByPolicyInteractor
+import com.android.systemui.qs.tiles.base.domain.interactor.QSTileDataInteractor
+import com.android.systemui.qs.tiles.base.domain.interactor.QSTileUserActionInteractor
+import com.android.systemui.qs.tiles.base.domain.model.DataUpdateTrigger
+import com.android.systemui.qs.tiles.base.domain.model.QSTileInput
+import com.android.systemui.qs.tiles.base.shared.logging.QSTileLogger
+import com.android.systemui.qs.tiles.base.shared.model.QSTileConfig
+import com.android.systemui.qs.tiles.base.shared.model.QSTilePolicy
+import com.android.systemui.qs.tiles.base.shared.model.QSTileState
+import com.android.systemui.qs.tiles.base.shared.model.QSTileUserAction
+import com.android.systemui.qs.tiles.base.ui.analytics.QSTileAnalytics
+import com.android.systemui.qs.tiles.base.ui.model.QSTileDataToStateMapper
import com.android.systemui.user.data.repository.UserRepository
import com.android.systemui.util.kotlin.throttle
import com.android.systemui.util.time.SystemClock
@@ -89,8 +89,10 @@ class QSTileViewModelImpl<DATA_TYPE>(
private val users: MutableStateFlow<UserHandle> =
MutableStateFlow(userRepository.getSelectedUserInfo().userHandle)
+ @SuppressLint("SharedFlowCreation")
private val userInputs: MutableSharedFlow<QSTileUserAction> = MutableSharedFlow()
+ @SuppressLint("SharedFlowCreation")
private val forceUpdates: MutableSharedFlow<Unit> = MutableSharedFlow()
private val spec
get() = config.tileSpec
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/StubQSTileViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/ui/viewmodel/StubQSTileViewModel.kt
index bdd5c73779cf..304b8042aa83 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/viewmodel/StubQSTileViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/base/ui/viewmodel/StubQSTileViewModel.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2024 The Android Open Source Project
+ * Copyright (C) 2025 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.
@@ -14,9 +14,12 @@
* limitations under the License.
*/
-package com.android.systemui.qs.tiles.viewmodel
+package com.android.systemui.qs.tiles.base.ui.viewmodel
import android.os.UserHandle
+import com.android.systemui.qs.tiles.base.shared.model.QSTileConfig
+import com.android.systemui.qs.tiles.base.shared.model.QSTileState
+import com.android.systemui.qs.tiles.base.shared.model.QSTileUserAction
import kotlinx.coroutines.flow.StateFlow
object StubQSTileViewModel : QSTileViewModel {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/CastDetailsContent.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/CastDetailsContent.kt
new file mode 100644
index 000000000000..cd33c964ce1b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/CastDetailsContent.kt
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2025 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.qs.tiles.dialog
+
+import android.view.LayoutInflater
+import androidx.compose.foundation.layout.fillMaxHeight
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.remember
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.viewinterop.AndroidView
+import com.android.internal.R
+import com.android.internal.app.MediaRouteControllerContentManager
+
+@Composable
+fun CastDetailsContent(castDetailsViewModel: CastDetailsViewModel) {
+ if (castDetailsViewModel.shouldShowChooserDialog()) {
+ // TODO(b/378514236): Show the chooser UI here.
+ return
+ }
+
+ val contentManager: MediaRouteControllerContentManager = remember {
+ castDetailsViewModel.createControllerContentManager()
+ }
+
+ AndroidView(
+ modifier = Modifier.fillMaxWidth().fillMaxHeight(),
+ factory = { context ->
+ // Inflate with the existing dialog xml layout
+ val view =
+ LayoutInflater.from(context).inflate(R.layout.media_route_controller_dialog, null)
+ contentManager.bindViews(view)
+ contentManager.onAttachedToWindow()
+
+ view
+ },
+ onRelease = { contentManager.onDetachedFromWindow() },
+ )
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/CastDetailsViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/CastDetailsViewModel.kt
new file mode 100644
index 000000000000..72322eff8bed
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/CastDetailsViewModel.kt
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2025 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.qs.tiles.dialog
+
+import android.content.Context
+import android.content.Intent
+import android.graphics.drawable.Drawable
+import android.provider.Settings
+import com.android.internal.app.MediaRouteControllerContentManager
+import com.android.internal.app.MediaRouteDialogPresenter
+import com.android.systemui.plugins.qs.TileDetailsViewModel
+import com.android.systemui.qs.tiles.base.domain.actions.QSTileIntentUserInputHandler
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
+
+/** The view model used for the screen record details view in the Quick Settings */
+class CastDetailsViewModel
+@AssistedInject
+constructor(
+ private val qsTileIntentUserActionHandler: QSTileIntentUserInputHandler,
+ @Assisted private val context: Context,
+ @Assisted private val routeTypes: Int,
+) : MediaRouteControllerContentManager.Delegate, TileDetailsViewModel {
+ @AssistedFactory
+ fun interface Factory {
+ fun create(context: Context, routeTypes: Int): CastDetailsViewModel
+ }
+
+ fun shouldShowChooserDialog(): Boolean {
+ return MediaRouteDialogPresenter.shouldShowChooserDialog(context, routeTypes)
+ }
+
+ fun createControllerContentManager(): MediaRouteControllerContentManager {
+ return MediaRouteControllerContentManager(context, this)
+ }
+
+ override fun clickOnSettingsButton() {
+ qsTileIntentUserActionHandler.handle(
+ /* expandable= */ null,
+ Intent(Settings.ACTION_CAST_SETTINGS),
+ )
+ }
+
+ // TODO(b/388321032): Replace this string with a string in a translatable xml file,
+ override val title: String
+ get() = "Cast screen to device"
+
+ // TODO(b/388321032): Replace this string with a string in a translatable xml file,
+ override val subTitle: String
+ get() = "Searching for devices..."
+
+ override fun setMediaRouteDeviceTitle(title: CharSequence?) {
+ // TODO(b/378514236): Finish implementing this function.
+ }
+
+ override fun setMediaRouteDeviceIcon(icon: Drawable?) {
+ // TODO(b/378514236): Finish implementing this function.
+ }
+
+ override fun dismissView() {
+ // TODO(b/378514236): Finish implementing this function.
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDetailsContentController.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDetailsContentController.java
index 6b5a22a4fc09..945e051606b9 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDetailsContentController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDetailsContentController.java
@@ -65,7 +65,6 @@ import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import androidx.annotation.WorkerThread;
-import com.android.app.viewcapture.ViewCaptureAwareWindowManager;
import com.android.internal.logging.UiEventLogger;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.KeyguardUpdateMonitorCallback;
@@ -186,7 +185,7 @@ public class InternetDetailsContentController implements AccessPointController.A
private GlobalSettings mGlobalSettings;
private int mDefaultDataSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
private ConnectivityManager.NetworkCallback mConnectivityManagerNetworkCallback;
- private ViewCaptureAwareWindowManager mWindowManager;
+ private WindowManager mWindowManager;
private ToastFactory mToastFactory;
private SignalDrawable mSignalDrawable;
private SignalDrawable mSecondarySignalDrawable; // For the secondary mobile data sub in DSDS
@@ -252,7 +251,7 @@ public class InternetDetailsContentController implements AccessPointController.A
@Main Handler handler, @Main Executor mainExecutor,
BroadcastDispatcher broadcastDispatcher, KeyguardUpdateMonitor keyguardUpdateMonitor,
GlobalSettings globalSettings, KeyguardStateController keyguardStateController,
- ViewCaptureAwareWindowManager viewCaptureAwareWindowManager, ToastFactory toastFactory,
+ @ShadeDisplayAware WindowManager windowManager, ToastFactory toastFactory,
@Background Handler workerHandler,
CarrierConfigTracker carrierConfigTracker,
LocationController locationController,
@@ -284,7 +283,7 @@ public class InternetDetailsContentController implements AccessPointController.A
mAccessPointController = accessPointController;
mWifiIconInjector = new WifiUtils.InternetIconInjector(mContext);
mConnectivityManagerNetworkCallback = new DataConnectivityListener();
- mWindowManager = viewCaptureAwareWindowManager;
+ mWindowManager = windowManager;
mToastFactory = toastFactory;
mSignalDrawable = new SignalDrawable(mContext);
mSecondarySignalDrawable = new SignalDrawable(mContext);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDetailsViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDetailsViewModel.kt
index fb63bea4fb9f..8dc73e351703 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDetailsViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDetailsViewModel.kt
@@ -19,7 +19,7 @@ package com.android.systemui.qs.tiles.dialog
import android.content.Intent
import android.provider.Settings
import com.android.systemui.plugins.qs.TileDetailsViewModel
-import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandler
+import com.android.systemui.qs.tiles.base.domain.actions.QSTileIntentUserInputHandler
import com.android.systemui.statusbar.connectivity.AccessPointController
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/airplane/domain/interactor/AirplaneModeTileDataInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/airplane/domain/interactor/AirplaneModeTileDataInteractor.kt
index 4f01a04fb742..63dd9b31326c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/airplane/domain/interactor/AirplaneModeTileDataInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/airplane/domain/interactor/AirplaneModeTileDataInteractor.kt
@@ -17,8 +17,8 @@
package com.android.systemui.qs.tiles.impl.airplane.domain.interactor
import android.os.UserHandle
-import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
-import com.android.systemui.qs.tiles.base.interactor.QSTileDataInteractor
+import com.android.systemui.qs.tiles.base.domain.interactor.QSTileDataInteractor
+import com.android.systemui.qs.tiles.base.domain.model.DataUpdateTrigger
import com.android.systemui.qs.tiles.impl.airplane.domain.model.AirplaneModeTileModel
import com.android.systemui.statusbar.pipeline.airplane.data.repository.AirplaneModeRepository
import javax.inject.Inject
@@ -29,13 +29,12 @@ import kotlinx.coroutines.flow.map
/** Observes airplane mode state changes providing the [AirplaneModeTileModel]. */
class AirplaneModeTileDataInteractor
@Inject
-constructor(
- private val airplaneModeRepository: AirplaneModeRepository,
-) : QSTileDataInteractor<AirplaneModeTileModel> {
+constructor(private val airplaneModeRepository: AirplaneModeRepository) :
+ QSTileDataInteractor<AirplaneModeTileModel> {
override fun tileData(
user: UserHandle,
- triggers: Flow<DataUpdateTrigger>
+ triggers: Flow<DataUpdateTrigger>,
): Flow<AirplaneModeTileModel> =
airplaneModeRepository.isAirplaneMode.map { AirplaneModeTileModel(it) }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/airplane/domain/interactor/AirplaneModeTileUserActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/airplane/domain/interactor/AirplaneModeTileUserActionInteractor.kt
index 50532918b170..e86d95150139 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/airplane/domain/interactor/AirplaneModeTileUserActionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/airplane/domain/interactor/AirplaneModeTileUserActionInteractor.kt
@@ -19,11 +19,11 @@ package com.android.systemui.qs.tiles.impl.airplane.domain.interactor
import android.content.Intent
import android.provider.Settings
import android.telephony.TelephonyManager
-import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandler
-import com.android.systemui.qs.tiles.base.interactor.QSTileInput
-import com.android.systemui.qs.tiles.base.interactor.QSTileUserActionInteractor
+import com.android.systemui.qs.tiles.base.domain.actions.QSTileIntentUserInputHandler
+import com.android.systemui.qs.tiles.base.domain.interactor.QSTileUserActionInteractor
+import com.android.systemui.qs.tiles.base.domain.model.QSTileInput
+import com.android.systemui.qs.tiles.base.shared.model.QSTileUserAction
import com.android.systemui.qs.tiles.impl.airplane.domain.model.AirplaneModeTileModel
-import com.android.systemui.qs.tiles.viewmodel.QSTileUserAction
import com.android.systemui.statusbar.pipeline.airplane.domain.interactor.AirplaneModeInteractor
import javax.inject.Inject
@@ -54,7 +54,7 @@ constructor(
is QSTileUserAction.LongClick -> {
qsTileIntentUserActionHandler.handle(
action.expandable,
- Intent(Settings.ACTION_AIRPLANE_MODE_SETTINGS)
+ Intent(Settings.ACTION_AIRPLANE_MODE_SETTINGS),
)
}
is QSTileUserAction.ToggleClick -> {}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/airplane/domain/AirplaneModeMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/airplane/ui/mapper/AirplaneModeTileMapper.kt
index 80d429ce2716..b0b7b4e2659b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/airplane/domain/AirplaneModeMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/airplane/ui/mapper/AirplaneModeTileMapper.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2025 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.
@@ -14,21 +14,21 @@
* limitations under the License.
*/
-package com.android.systemui.qs.tiles.impl.airplane.domain
+package com.android.systemui.qs.tiles.impl.airplane.ui.mapper
import android.content.res.Resources
import android.content.res.Resources.Theme
import com.android.systemui.common.shared.model.Icon
-import com.android.systemui.qs.tiles.base.interactor.QSTileDataToStateMapper
+import com.android.systemui.qs.tiles.base.shared.model.QSTileConfig
+import com.android.systemui.qs.tiles.base.shared.model.QSTileState
+import com.android.systemui.qs.tiles.base.ui.model.QSTileDataToStateMapper
import com.android.systemui.qs.tiles.impl.airplane.domain.model.AirplaneModeTileModel
-import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
-import com.android.systemui.qs.tiles.viewmodel.QSTileState
import com.android.systemui.res.R
import com.android.systemui.shade.ShadeDisplayAware
import javax.inject.Inject
/** Maps [AirplaneModeTileModel] to [QSTileState]. */
-class AirplaneModeMapper
+class AirplaneModeTileMapper
@Inject
constructor(@ShadeDisplayAware private val resources: Resources, val theme: Theme) :
QSTileDataToStateMapper<AirplaneModeTileModel> {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/alarm/domain/interactor/AlarmTileDataInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/alarm/domain/interactor/AlarmTileDataInteractor.kt
index 51cd501c0c80..1f113d9bddd4 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/alarm/domain/interactor/AlarmTileDataInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/alarm/domain/interactor/AlarmTileDataInteractor.kt
@@ -18,8 +18,8 @@ package com.android.systemui.qs.tiles.impl.alarm.domain.interactor
import android.os.UserHandle
import com.android.systemui.common.coroutine.ConflatedCallbackFlow
-import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
-import com.android.systemui.qs.tiles.base.interactor.QSTileDataInteractor
+import com.android.systemui.qs.tiles.base.domain.interactor.QSTileDataInteractor
+import com.android.systemui.qs.tiles.base.domain.model.DataUpdateTrigger
import com.android.systemui.qs.tiles.impl.alarm.domain.model.AlarmTileModel
import com.android.systemui.statusbar.policy.NextAlarmController
import com.android.systemui.util.time.DateFormatUtil
@@ -33,12 +33,12 @@ class AlarmTileDataInteractor
@Inject
constructor(
private val alarmController: NextAlarmController,
- private val dateFormatUtil: DateFormatUtil
+ private val dateFormatUtil: DateFormatUtil,
) : QSTileDataInteractor<AlarmTileModel> {
override fun tileData(
user: UserHandle,
- triggers: Flow<DataUpdateTrigger>
+ triggers: Flow<DataUpdateTrigger>,
): Flow<AlarmTileModel> =
ConflatedCallbackFlow.conflatedCallbackFlow {
val alarmCallback =
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/alarm/domain/interactor/AlarmTileUserActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/alarm/domain/interactor/AlarmTileUserActionInteractor.kt
index 79fcd3721877..f6fcf255cc4e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/alarm/domain/interactor/AlarmTileUserActionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/alarm/domain/interactor/AlarmTileUserActionInteractor.kt
@@ -18,19 +18,18 @@ package com.android.systemui.qs.tiles.impl.alarm.domain.interactor
import android.content.Intent
import android.provider.AlarmClock
-import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandler
-import com.android.systemui.qs.tiles.base.interactor.QSTileInput
-import com.android.systemui.qs.tiles.base.interactor.QSTileUserActionInteractor
+import com.android.systemui.qs.tiles.base.domain.actions.QSTileIntentUserInputHandler
+import com.android.systemui.qs.tiles.base.domain.interactor.QSTileUserActionInteractor
+import com.android.systemui.qs.tiles.base.domain.model.QSTileInput
+import com.android.systemui.qs.tiles.base.shared.model.QSTileUserAction
import com.android.systemui.qs.tiles.impl.alarm.domain.model.AlarmTileModel
-import com.android.systemui.qs.tiles.viewmodel.QSTileUserAction
import javax.inject.Inject
/** Handles alarm tile clicks. */
class AlarmTileUserActionInteractor
@Inject
-constructor(
- private val inputHandler: QSTileIntentUserInputHandler,
-) : QSTileUserActionInteractor<AlarmTileModel> {
+constructor(private val inputHandler: QSTileIntentUserInputHandler) :
+ QSTileUserActionInteractor<AlarmTileModel> {
override suspend fun handleInput(input: QSTileInput<AlarmTileModel>): Unit =
with(input) {
when (action) {
@@ -44,7 +43,7 @@ constructor(
} else {
inputHandler.handle(
action.expandable,
- Intent(AlarmClock.ACTION_SHOW_ALARMS)
+ Intent(AlarmClock.ACTION_SHOW_ALARMS),
)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/alarm/domain/AlarmTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/alarm/ui/mapper/AlarmTileMapper.kt
index d56d9944dbb8..8a726adcd695 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/alarm/domain/AlarmTileMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/alarm/ui/mapper/AlarmTileMapper.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2025 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.
@@ -14,15 +14,15 @@
* limitations under the License.
*/
-package com.android.systemui.qs.tiles.impl.alarm.domain
+package com.android.systemui.qs.tiles.impl.alarm.ui.mapper
import android.content.res.Resources
import android.content.res.Resources.Theme
import com.android.systemui.common.shared.model.Icon
-import com.android.systemui.qs.tiles.base.interactor.QSTileDataToStateMapper
+import com.android.systemui.qs.tiles.base.shared.model.QSTileConfig
+import com.android.systemui.qs.tiles.base.shared.model.QSTileState
+import com.android.systemui.qs.tiles.base.ui.model.QSTileDataToStateMapper
import com.android.systemui.qs.tiles.impl.alarm.domain.model.AlarmTileModel
-import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
-import com.android.systemui.qs.tiles.viewmodel.QSTileState
import com.android.systemui.res.R
import com.android.systemui.shade.ShadeDisplayAware
import com.android.systemui.util.time.SystemClock
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/battery/domain/interactor/BatterySaverTileDataInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/battery/domain/interactor/BatterySaverTileDataInteractor.kt
index 22bbbbb62019..ed55449c7a9d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/battery/domain/interactor/BatterySaverTileDataInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/battery/domain/interactor/BatterySaverTileDataInteractor.kt
@@ -18,11 +18,10 @@ package com.android.systemui.qs.tiles.impl.battery.domain.interactor
import android.os.UserHandle
import com.android.systemui.dagger.qualifiers.Background
-import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
-import com.android.systemui.qs.tiles.base.interactor.QSTileDataInteractor
+import com.android.systemui.qs.tiles.base.domain.interactor.QSTileDataInteractor
+import com.android.systemui.qs.tiles.base.domain.model.DataUpdateTrigger
import com.android.systemui.qs.tiles.impl.battery.domain.model.BatterySaverTileModel
import com.android.systemui.statusbar.policy.BatteryController
-import com.android.systemui.util.kotlin.combine
import com.android.systemui.util.kotlin.getBatteryLevel
import com.android.systemui.util.kotlin.isBatteryPowerSaveEnabled
import com.android.systemui.util.kotlin.isDevicePluggedIn
@@ -44,7 +43,7 @@ constructor(
override fun tileData(
user: UserHandle,
- triggers: Flow<DataUpdateTrigger>
+ triggers: Flow<DataUpdateTrigger>,
): Flow<BatterySaverTileModel> =
combine(
batteryController.isDevicePluggedIn().distinctUntilChanged().flowOn(bgCoroutineContext),
@@ -56,8 +55,8 @@ constructor(
) {
isPluggedIn: Boolean,
isPowerSaverEnabled: Boolean,
- _, // we are only interested in battery level change, not the actual level
- ->
+ _ // we are only interested in battery level change, not the actual level
+ ->
BatterySaverTileModel.Standard(isPluggedIn, isPowerSaverEnabled)
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/battery/domain/interactor/BatterySaverTileUserActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/battery/domain/interactor/BatterySaverTileUserActionInteractor.kt
index 3bbb9aa8505c..2eb05a2461f0 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/battery/domain/interactor/BatterySaverTileUserActionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/battery/domain/interactor/BatterySaverTileUserActionInteractor.kt
@@ -18,11 +18,11 @@ package com.android.systemui.qs.tiles.impl.battery.domain.interactor
import android.content.Intent
import android.provider.Settings
-import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandler
-import com.android.systemui.qs.tiles.base.interactor.QSTileInput
-import com.android.systemui.qs.tiles.base.interactor.QSTileUserActionInteractor
+import com.android.systemui.qs.tiles.base.domain.actions.QSTileIntentUserInputHandler
+import com.android.systemui.qs.tiles.base.domain.interactor.QSTileUserActionInteractor
+import com.android.systemui.qs.tiles.base.domain.model.QSTileInput
+import com.android.systemui.qs.tiles.base.shared.model.QSTileUserAction
import com.android.systemui.qs.tiles.impl.battery.domain.model.BatterySaverTileModel
-import com.android.systemui.qs.tiles.viewmodel.QSTileUserAction
import com.android.systemui.statusbar.policy.BatteryController
import javax.inject.Inject
@@ -31,7 +31,7 @@ class BatterySaverTileUserActionInteractor
@Inject
constructor(
private val qsTileIntentUserActionHandler: QSTileIntentUserInputHandler,
- private val batteryController: BatteryController
+ private val batteryController: BatteryController,
) : QSTileUserActionInteractor<BatterySaverTileModel> {
override suspend fun handleInput(input: QSTileInput<BatterySaverTileModel>) =
@@ -45,7 +45,7 @@ constructor(
is QSTileUserAction.LongClick -> {
qsTileIntentUserActionHandler.handle(
action.expandable,
- Intent(Settings.ACTION_BATTERY_SAVER_SETTINGS)
+ Intent(Settings.ACTION_BATTERY_SAVER_SETTINGS),
)
}
is QSTileUserAction.ToggleClick -> {}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/battery/ui/BatterySaverTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/battery/ui/mapper/BatterySaverTileMapper.kt
index 72759c5bb066..7bc3d2bfc809 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/battery/ui/BatterySaverTileMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/battery/ui/mapper/BatterySaverTileMapper.kt
@@ -14,14 +14,14 @@
* limitations under the License.
*/
-package com.android.systemui.qs.tiles.impl.battery.ui
+package com.android.systemui.qs.tiles.impl.battery.ui.mapper
import android.content.res.Resources
import com.android.systemui.common.shared.model.Icon
-import com.android.systemui.qs.tiles.base.interactor.QSTileDataToStateMapper
+import com.android.systemui.qs.tiles.base.shared.model.QSTileConfig
+import com.android.systemui.qs.tiles.base.shared.model.QSTileState
+import com.android.systemui.qs.tiles.base.ui.model.QSTileDataToStateMapper
import com.android.systemui.qs.tiles.impl.battery.domain.model.BatterySaverTileModel
-import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
-import com.android.systemui.qs.tiles.viewmodel.QSTileState
import com.android.systemui.res.R
import com.android.systemui.shade.ShadeDisplayAware
import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/interactor/ColorCorrectionTileDataInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/interactor/ColorCorrectionTileDataInteractor.kt
index cd33d451ba81..1d173fcebfac 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/interactor/ColorCorrectionTileDataInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/interactor/ColorCorrectionTileDataInteractor.kt
@@ -18,8 +18,8 @@ package com.android.systemui.qs.tiles.impl.colorcorrection.domain.interactor
import android.os.UserHandle
import com.android.systemui.accessibility.data.repository.ColorCorrectionRepository
-import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
-import com.android.systemui.qs.tiles.base.interactor.QSTileDataInteractor
+import com.android.systemui.qs.tiles.base.domain.interactor.QSTileDataInteractor
+import com.android.systemui.qs.tiles.base.domain.model.DataUpdateTrigger
import com.android.systemui.qs.tiles.impl.colorcorrection.domain.model.ColorCorrectionTileModel
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
@@ -29,15 +29,15 @@ import kotlinx.coroutines.flow.map
/** Observes color correction state changes providing the [ColorCorrectionTileModel]. */
class ColorCorrectionTileDataInteractor
@Inject
-constructor(
- private val colorCorrectionRepository: ColorCorrectionRepository,
-) : QSTileDataInteractor<ColorCorrectionTileModel> {
+constructor(private val colorCorrectionRepository: ColorCorrectionRepository) :
+ QSTileDataInteractor<ColorCorrectionTileModel> {
override fun tileData(
user: UserHandle,
- triggers: Flow<DataUpdateTrigger>
+ triggers: Flow<DataUpdateTrigger>,
): Flow<ColorCorrectionTileModel> {
return colorCorrectionRepository.isEnabled(user).map { ColorCorrectionTileModel(it) }
}
+
override fun availability(user: UserHandle): Flow<Boolean> = flowOf(true)
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/interactor/ColorCorrectionUserActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/interactor/ColorCorrectionUserActionInteractor.kt
index b774643eb21d..dea1f5ca8c1b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/interactor/ColorCorrectionUserActionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/interactor/ColorCorrectionUserActionInteractor.kt
@@ -20,11 +20,11 @@ import android.content.Intent
import android.provider.Settings
import com.android.systemui.accessibility.data.repository.ColorCorrectionRepository
import com.android.systemui.qs.shared.QSSettingsPackageRepository
-import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandler
-import com.android.systemui.qs.tiles.base.interactor.QSTileInput
-import com.android.systemui.qs.tiles.base.interactor.QSTileUserActionInteractor
+import com.android.systemui.qs.tiles.base.domain.actions.QSTileIntentUserInputHandler
+import com.android.systemui.qs.tiles.base.domain.interactor.QSTileUserActionInteractor
+import com.android.systemui.qs.tiles.base.domain.model.QSTileInput
+import com.android.systemui.qs.tiles.base.shared.model.QSTileUserAction
import com.android.systemui.qs.tiles.impl.colorcorrection.domain.model.ColorCorrectionTileModel
-import com.android.systemui.qs.tiles.viewmodel.QSTileUserAction
import javax.inject.Inject
/** Handles color correction tile clicks. */
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/ColorCorrectionTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/colorcorrection/ui/mapper/ColorCorrectionTileMapper.kt
index e5a0fe8ed048..93d81f0aa508 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/ColorCorrectionTileMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/colorcorrection/ui/mapper/ColorCorrectionTileMapper.kt
@@ -14,14 +14,14 @@
* limitations under the License.
*/
-package com.android.systemui.qs.tiles.impl.colorcorrection.domain
+package com.android.systemui.qs.tiles.impl.colorcorrection.ui.mapper
import android.content.res.Resources
import com.android.systemui.common.shared.model.Icon
-import com.android.systemui.qs.tiles.base.interactor.QSTileDataToStateMapper
+import com.android.systemui.qs.tiles.base.shared.model.QSTileConfig
+import com.android.systemui.qs.tiles.base.shared.model.QSTileState
+import com.android.systemui.qs.tiles.base.ui.model.QSTileDataToStateMapper
import com.android.systemui.qs.tiles.impl.colorcorrection.domain.model.ColorCorrectionTileModel
-import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
-import com.android.systemui.qs.tiles.viewmodel.QSTileState
import com.android.systemui.res.R
import com.android.systemui.shade.ShadeDisplayAware
import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/data/entity/CustomTileDefaults.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/data/model/CustomTileDefaults.kt
index dfeb65b7c5aa..40151063ac9c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/data/entity/CustomTileDefaults.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/data/model/CustomTileDefaults.kt
@@ -14,15 +14,13 @@
* limitations under the License.
*/
-package com.android.systemui.qs.tiles.impl.custom.data.entity
+package com.android.systemui.qs.tiles.impl.custom.data.model
import android.graphics.drawable.Icon
sealed interface CustomTileDefaults {
data object Error : CustomTileDefaults
- data class Result(
- val icon: Icon,
- val label: CharSequence,
- ) : CustomTileDefaults
+
+ data class Result(val icon: Icon, val label: CharSequence) : CustomTileDefaults
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/data/repository/CustomTileDefaultsRepository.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/data/repository/CustomTileDefaultsRepository.kt
index 32fb1d18724d..34894f52a0be 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/data/repository/CustomTileDefaultsRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/data/repository/CustomTileDefaultsRepository.kt
@@ -24,8 +24,8 @@ import android.graphics.drawable.Icon
import android.os.UserHandle
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
-import com.android.systemui.qs.tiles.impl.custom.data.entity.CustomTileDefaults
-import com.android.systemui.qs.tiles.impl.di.QSTileScope
+import com.android.systemui.qs.tiles.base.shared.model.QSTileScope
+import com.android.systemui.qs.tiles.impl.custom.data.model.CustomTileDefaults
import com.android.systemui.shade.ShadeDisplayAware
import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
@@ -58,11 +58,7 @@ interface CustomTileDefaultsRepository {
*
* Listen to [defaults] to get the loaded result
*/
- fun requestNewDefaults(
- user: UserHandle,
- componentName: ComponentName,
- force: Boolean = false,
- )
+ fun requestNewDefaults(user: UserHandle, componentName: ComponentName, force: Boolean = false)
}
@QSTileScope
@@ -77,7 +73,7 @@ constructor(
private val defaultsRequests =
MutableSharedFlow<DefaultsRequest>(
replay = 1,
- onBufferOverflow = BufferOverflow.DROP_OLDEST
+ onBufferOverflow = BufferOverflow.DROP_OLDEST,
)
private val defaults: SharedFlow<DefaultsResult> =
@@ -106,7 +102,7 @@ constructor(
private suspend fun loadDefaults(
user: UserHandle,
- componentName: ComponentName
+ componentName: ComponentName,
): CustomTileDefaults =
withContext(backgroundDispatcher) {
try {
@@ -120,16 +116,14 @@ constructor(
CustomTileDefaults.Result(
Icon.createWithResource(componentName.packageName, iconRes),
- info.loadLabel(userContext.packageManager)
+ info.loadLabel(userContext.packageManager),
)
} catch (e: PackageManager.NameNotFoundException) {
CustomTileDefaults.Error
}
}
- private fun ComponentName.getServiceInfo(
- packageManager: PackageManager,
- ): ServiceInfo {
+ private fun ComponentName.getServiceInfo(packageManager: PackageManager): ServiceInfo {
val isSystemApp = packageManager.getApplicationInfo(packageName, 0).isSystemApp
var flags =
(PackageManager.MATCH_DIRECT_BOOT_UNAWARE or PackageManager.MATCH_DIRECT_BOOT_AWARE)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/data/repository/CustomTilePackageUpdatesRepository.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/data/repository/CustomTilePackageUpdatesRepository.kt
index cd4938f01b63..da811a3c494f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/data/repository/CustomTilePackageUpdatesRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/data/repository/CustomTilePackageUpdatesRepository.kt
@@ -23,12 +23,12 @@ import android.content.Intent
import android.content.IntentFilter
import android.os.UserHandle
import androidx.annotation.GuardedBy
-import com.android.systemui.common.coroutine.ConflatedCallbackFlow
-import com.android.systemui.dagger.qualifiers.Application
+import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.qs.pipeline.shared.TileSpec
-import com.android.systemui.qs.tiles.impl.di.QSTileScope
+import com.android.systemui.qs.tiles.base.shared.model.QSTileScope
import com.android.systemui.shade.ShadeDisplayAware
+import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
import javax.inject.Inject
import kotlin.coroutines.CoroutineContext
import kotlinx.coroutines.CoroutineScope
@@ -40,7 +40,6 @@ import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onCompletion
import kotlinx.coroutines.flow.shareIn
-import com.android.app.tracing.coroutines.launchTraced as launch
interface CustomTilePackageUpdatesRepository {
@@ -79,7 +78,7 @@ constructor(
"RegisterReceiverViaContext",
)
private fun createPackageChangesFlowForUser(user: UserHandle): Flow<Unit> =
- ConflatedCallbackFlow.conflatedCallbackFlow {
+ conflatedCallbackFlow {
val receiver =
object : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/data/repository/CustomTileRepository.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/data/repository/CustomTileRepository.kt
index 0aaea8fc0a50..aabe7a9365ba 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/data/repository/CustomTileRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/data/repository/CustomTileRepository.kt
@@ -27,10 +27,10 @@ import com.android.systemui.qs.external.CustomTileStatePersister
import com.android.systemui.qs.external.PackageManagerAdapter
import com.android.systemui.qs.external.TileServiceKey
import com.android.systemui.qs.pipeline.shared.TileSpec
-import com.android.systemui.qs.tiles.impl.custom.commons.copy
-import com.android.systemui.qs.tiles.impl.custom.commons.setFrom
-import com.android.systemui.qs.tiles.impl.custom.data.entity.CustomTileDefaults
-import com.android.systemui.qs.tiles.impl.di.QSTileScope
+import com.android.systemui.qs.tiles.base.shared.model.QSTileScope
+import com.android.systemui.qs.tiles.impl.custom.data.model.CustomTileDefaults
+import com.android.systemui.qs.tiles.impl.custom.shared.model.copy
+import com.android.systemui.qs.tiles.impl.custom.shared.model.setFrom
import javax.inject.Inject
import kotlin.coroutines.CoroutineContext
import kotlinx.coroutines.channels.BufferOverflow
@@ -78,11 +78,7 @@ interface CustomTileRepository {
* [user] differs from the cached one. [isPersistable] tile will be persisted to be possibly
* loaded when the [restoreForTheUserIfNeeded].
*/
- suspend fun updateWithTile(
- user: UserHandle,
- newTile: Tile,
- isPersistable: Boolean,
- )
+ suspend fun updateWithTile(user: UserHandle, newTile: Tile, isPersistable: Boolean)
/**
* Updates tile with the values from [defaults]. Overwrites the current cache when [user]
@@ -114,11 +110,7 @@ constructor(
if (isPersistable && getCurrentTileWithUser()?.user != user) {
withContext(backgroundContext) {
customTileStatePersister.readState(user.getKey())?.let {
- updateWithTile(
- user,
- it,
- true,
- )
+ updateWithTile(user, it, true)
}
}
}
@@ -137,11 +129,8 @@ constructor(
}
}
- override suspend fun updateWithTile(
- user: UserHandle,
- newTile: Tile,
- isPersistable: Boolean,
- ) = updateTile(user, isPersistable) { setFrom(newTile) }
+ override suspend fun updateWithTile(user: UserHandle, newTile: Tile, isPersistable: Boolean) =
+ updateTile(user, isPersistable) { setFrom(newTile) }
override suspend fun updateWithDefaults(
user: UserHandle,
@@ -182,7 +171,7 @@ constructor(
packageManagerAdapter.getServiceInfo(
tileSpec.componentName,
META_DATA_QUERY_FLAGS,
- getCurrentTileWithUser()?.user?.identifier ?: UserHandle.USER_CURRENT
+ getCurrentTileWithUser()?.user?.identifier ?: UserHandle.USER_CURRENT,
)
info?.metaData?.getBoolean(TileService.META_DATA_TOGGLEABLE_TILE, false) == true
} catch (e: RemoteException) {
@@ -193,7 +182,7 @@ constructor(
private suspend fun updateTile(
user: UserHandle,
isPersistable: Boolean,
- update: Tile.() -> Unit
+ update: Tile.() -> Unit,
): Unit =
tileUpdateMutex.withLock {
val currentTileWithUser = getCurrentTileWithUser()
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileDataInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileDataInteractor.kt
index 0b0f2feaa909..e64675f81257 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileDataInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileDataInteractor.kt
@@ -20,14 +20,14 @@ import android.os.UserHandle
import android.service.quicksettings.Tile
import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.systemui.qs.pipeline.shared.TileSpec
-import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
-import com.android.systemui.qs.tiles.base.interactor.QSTileDataInteractor
-import com.android.systemui.qs.tiles.base.logging.QSTileLogger
-import com.android.systemui.qs.tiles.impl.custom.data.entity.CustomTileDefaults
+import com.android.systemui.qs.tiles.base.domain.interactor.QSTileDataInteractor
+import com.android.systemui.qs.tiles.base.domain.model.DataUpdateTrigger
+import com.android.systemui.qs.tiles.base.shared.logging.QSTileLogger
+import com.android.systemui.qs.tiles.base.shared.model.QSTileScope
+import com.android.systemui.qs.tiles.impl.custom.data.model.CustomTileDefaults
import com.android.systemui.qs.tiles.impl.custom.data.repository.CustomTileDefaultsRepository
import com.android.systemui.qs.tiles.impl.custom.data.repository.CustomTilePackageUpdatesRepository
-import com.android.systemui.qs.tiles.impl.custom.domain.entity.CustomTileDataModel
-import com.android.systemui.qs.tiles.impl.di.QSTileScope
+import com.android.systemui.qs.tiles.impl.custom.domain.model.CustomTileDataModel
import com.android.systemui.user.data.repository.UserRepository
import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileInteractor.kt
index 6f1cb3cc2675..c587e3a11edf 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileInteractor.kt
@@ -18,11 +18,12 @@ package com.android.systemui.qs.tiles.impl.custom.domain.interactor
import android.os.UserHandle
import android.service.quicksettings.Tile
+import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.qs.pipeline.shared.TileSpec
+import com.android.systemui.qs.tiles.base.shared.model.QSTileScope
import com.android.systemui.qs.tiles.impl.custom.data.repository.CustomTileDefaultsRepository
import com.android.systemui.qs.tiles.impl.custom.data.repository.CustomTileRepository
-import com.android.systemui.qs.tiles.impl.di.QSTileScope
import javax.inject.Inject
import kotlin.coroutines.CoroutineContext
import kotlinx.coroutines.CoroutineScope
@@ -34,7 +35,6 @@ import kotlinx.coroutines.flow.firstOrNull
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
-import com.android.app.tracing.coroutines.launchTraced as launch
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
@@ -100,7 +100,7 @@ constructor(
launchUpdates(user)
customTileRepository.restoreForTheUserIfNeeded(
user,
- customTileRepository.isTileActive()
+ customTileRepository.isTileActive(),
)
// Suspend to make sure it gets the tile from one of the sources: restoration, defaults,
// or
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileServiceInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileServiceInteractor.kt
index c0fc93fc914b..56fd325fe6d2 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileServiceInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileServiceInteractor.kt
@@ -31,8 +31,8 @@ import com.android.systemui.qs.external.CustomTileInterface
import com.android.systemui.qs.external.TileServiceManager
import com.android.systemui.qs.external.TileServices
import com.android.systemui.qs.pipeline.shared.TileSpec
-import com.android.systemui.qs.tiles.base.logging.QSTileLogger
-import com.android.systemui.qs.tiles.impl.di.QSTileScope
+import com.android.systemui.qs.tiles.base.shared.logging.QSTileLogger
+import com.android.systemui.qs.tiles.base.shared.model.QSTileScope
import com.android.systemui.user.data.repository.UserRepository
import dagger.Lazy
import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileUserActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileUserActionInteractor.kt
index 1153b5c67261..df2759ab9de1 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileUserActionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/interactor/CustomTileUserActionInteractor.kt
@@ -34,13 +34,13 @@ import androidx.annotation.GuardedBy
import com.android.systemui.animation.Expandable
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.qs.pipeline.shared.TileSpec
-import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandler
-import com.android.systemui.qs.tiles.base.interactor.QSTileInput
-import com.android.systemui.qs.tiles.base.interactor.QSTileUserActionInteractor
-import com.android.systemui.qs.tiles.base.logging.QSTileLogger
-import com.android.systemui.qs.tiles.impl.custom.domain.entity.CustomTileDataModel
-import com.android.systemui.qs.tiles.impl.di.QSTileScope
-import com.android.systemui.qs.tiles.viewmodel.QSTileUserAction
+import com.android.systemui.qs.tiles.base.domain.actions.QSTileIntentUserInputHandler
+import com.android.systemui.qs.tiles.base.domain.interactor.QSTileUserActionInteractor
+import com.android.systemui.qs.tiles.base.domain.model.QSTileInput
+import com.android.systemui.qs.tiles.base.shared.logging.QSTileLogger
+import com.android.systemui.qs.tiles.base.shared.model.QSTileScope
+import com.android.systemui.qs.tiles.base.shared.model.QSTileUserAction
+import com.android.systemui.qs.tiles.impl.custom.domain.model.CustomTileDataModel
import com.android.systemui.settings.DisplayTracker
import com.android.systemui.shade.ShadeDisplayAware
import java.util.concurrent.atomic.AtomicReference
@@ -80,10 +80,7 @@ constructor(
qsTileLogger.logCustomTileUserActionDelivered(tileSpec)
}
- private suspend fun click(
- expandable: Expandable?,
- activityLaunchForClick: PendingIntent?,
- ) {
+ private suspend fun click(expandable: Expandable?, activityLaunchForClick: PendingIntent?) {
grantToken()
try {
// Bind active tile to deliver user action
@@ -133,7 +130,7 @@ constructor(
token,
WindowManager.LayoutParams.TYPE_QS_DIALOG,
displayTracker.defaultDisplayId,
- null /* options */
+ null, /* options */
)
} catch (e: RemoteException) {
qsTileLogger.logError(tileSpec, "Failed to grant a window token", e)
@@ -147,7 +144,7 @@ constructor(
user: UserHandle,
expandable: Expandable?,
componentName: ComponentName,
- state: Int
+ state: Int,
) {
val resolvedIntent: Intent? =
resolveIntent(
@@ -166,7 +163,7 @@ constructor(
Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
.setData(
Uri.fromParts(IntentFilter.SCHEME_PACKAGE, componentName.packageName, null)
- )
+ ),
)
} else {
qsTileIntentUserInputHandler.handle(expandable, resolvedIntent)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/entity/CustomTileDataModel.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/model/CustomTileDataModel.kt
index 5b6ff1e033fb..4393e9e1bb63 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/entity/CustomTileDataModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/model/CustomTileDataModel.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.qs.tiles.impl.custom.domain.entity
+package com.android.systemui.qs.tiles.impl.custom.domain.model
import android.content.ComponentName
import android.graphics.drawable.Icon
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/QSTileConfigModule.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/shared/model/QSTileConfigModule.kt
index 558fb64e3ef8..c9508889ff85 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/QSTileConfigModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/shared/model/QSTileConfigModule.kt
@@ -14,10 +14,10 @@
* limitations under the License.
*/
-package com.android.systemui.qs.tiles.impl.custom.di
+package com.android.systemui.qs.tiles.impl.custom.shared.model
import com.android.systemui.qs.pipeline.shared.TileSpec
-import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
+import com.android.systemui.qs.tiles.base.shared.model.QSTileConfig
import dagger.Module
import dagger.Provides
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/commons/TileExt.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/shared/model/TileExt.kt
index 869f6f321d21..022894e2e5d2 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/commons/TileExt.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/shared/model/TileExt.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.qs.tiles.impl.custom.commons
+package com.android.systemui.qs.tiles.impl.custom.shared.model
import android.service.quicksettings.Tile
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/CustomTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/ui/mapper/CustomTileMapper.kt
index c446865f31af..dfaaea1e7d86 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/domain/CustomTileMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/ui/mapper/CustomTileMapper.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2025 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.qs.tiles.impl.custom.domain
+package com.android.systemui.qs.tiles.impl.custom.ui.mapper
import android.annotation.SuppressLint
import android.app.IUriGrantsManager
@@ -26,10 +26,10 @@ import android.widget.Button
import android.widget.Switch
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.qs.tiles.base.interactor.QSTileDataToStateMapper
-import com.android.systemui.qs.tiles.impl.custom.domain.entity.CustomTileDataModel
-import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
-import com.android.systemui.qs.tiles.viewmodel.QSTileState
+import com.android.systemui.qs.tiles.base.shared.model.QSTileConfig
+import com.android.systemui.qs.tiles.base.shared.model.QSTileState
+import com.android.systemui.qs.tiles.base.ui.model.QSTileDataToStateMapper
+import com.android.systemui.qs.tiles.impl.custom.domain.model.CustomTileDataModel
import com.android.systemui.shade.ShadeDisplayAware
import javax.inject.Inject
@@ -38,9 +38,8 @@ class CustomTileMapper
@Inject
constructor(
@ShadeDisplayAware private val context: Context,
- private val uriGrantsManager: IUriGrantsManager
-) :
- QSTileDataToStateMapper<CustomTileDataModel> {
+ private val uriGrantsManager: IUriGrantsManager,
+) : QSTileDataToStateMapper<CustomTileDataModel> {
override fun map(config: QSTileConfig, data: CustomTileDataModel): QSTileState {
val userContext =
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/CustomTileComponent.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/ui/model/CustomTileComponent.kt
index 7b099c2cb0c6..4f230334c427 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/CustomTileComponent.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/ui/model/CustomTileComponent.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2025 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.
@@ -14,14 +14,15 @@
* limitations under the License.
*/
-package com.android.systemui.qs.tiles.impl.custom.di
+package com.android.systemui.qs.tiles.impl.custom.ui.model
+import com.android.systemui.qs.tiles.base.shared.model.QSTileScope
+import com.android.systemui.qs.tiles.base.ui.model.QSTileComponent
import com.android.systemui.qs.tiles.impl.custom.data.repository.CustomTilePackageUpdatesRepository
-import com.android.systemui.qs.tiles.impl.custom.domain.entity.CustomTileDataModel
import com.android.systemui.qs.tiles.impl.custom.domain.interactor.CustomTileInteractor
import com.android.systemui.qs.tiles.impl.custom.domain.interactor.CustomTileServiceInteractor
-import com.android.systemui.qs.tiles.impl.di.QSTileComponent
-import com.android.systemui.qs.tiles.impl.di.QSTileScope
+import com.android.systemui.qs.tiles.impl.custom.domain.model.CustomTileDataModel
+import com.android.systemui.qs.tiles.impl.custom.shared.model.QSTileConfigModule
import dagger.Subcomponent
@QSTileScope
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/CustomTileModule.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/ui/model/CustomTileModule.kt
index 196fa12cacc9..ee7908af6c84 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/di/CustomTileModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/custom/ui/model/CustomTileModule.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2025 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.
@@ -14,23 +14,23 @@
* limitations under the License.
*/
-package com.android.systemui.qs.tiles.impl.custom.di
+package com.android.systemui.qs.tiles.impl.custom.ui.model
-import com.android.systemui.qs.tiles.base.interactor.QSTileDataInteractor
-import com.android.systemui.qs.tiles.base.interactor.QSTileDataToStateMapper
-import com.android.systemui.qs.tiles.base.interactor.QSTileUserActionInteractor
-import com.android.systemui.qs.tiles.base.viewmodel.QSTileCoroutineScopeFactory
+import com.android.systemui.qs.tiles.base.domain.interactor.QSTileDataInteractor
+import com.android.systemui.qs.tiles.base.domain.interactor.QSTileUserActionInteractor
+import com.android.systemui.qs.tiles.base.shared.model.QSTileCoroutineScopeFactory
+import com.android.systemui.qs.tiles.base.shared.model.QSTileScope
+import com.android.systemui.qs.tiles.base.ui.model.QSTileDataToStateMapper
import com.android.systemui.qs.tiles.impl.custom.data.repository.CustomTileDefaultsRepository
import com.android.systemui.qs.tiles.impl.custom.data.repository.CustomTileDefaultsRepositoryImpl
import com.android.systemui.qs.tiles.impl.custom.data.repository.CustomTilePackageUpdatesRepository
import com.android.systemui.qs.tiles.impl.custom.data.repository.CustomTilePackageUpdatesRepositoryImpl
import com.android.systemui.qs.tiles.impl.custom.data.repository.CustomTileRepository
import com.android.systemui.qs.tiles.impl.custom.data.repository.CustomTileRepositoryImpl
-import com.android.systemui.qs.tiles.impl.custom.domain.CustomTileMapper
-import com.android.systemui.qs.tiles.impl.custom.domain.entity.CustomTileDataModel
import com.android.systemui.qs.tiles.impl.custom.domain.interactor.CustomTileDataInteractor
import com.android.systemui.qs.tiles.impl.custom.domain.interactor.CustomTileUserActionInteractor
-import com.android.systemui.qs.tiles.impl.di.QSTileScope
+import com.android.systemui.qs.tiles.impl.custom.domain.model.CustomTileDataModel
+import com.android.systemui.qs.tiles.impl.custom.ui.mapper.CustomTileMapper
import dagger.Binds
import dagger.Module
import dagger.Provides
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/flashlight/domain/interactor/FlashlightTileDataInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/flashlight/domain/interactor/FlashlightTileDataInteractor.kt
index 38eb5947bd71..9720d1549a4c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/flashlight/domain/interactor/FlashlightTileDataInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/flashlight/domain/interactor/FlashlightTileDataInteractor.kt
@@ -17,11 +17,11 @@
package com.android.systemui.qs.tiles.impl.flashlight.domain.interactor
import android.os.UserHandle
-import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
-import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
-import com.android.systemui.qs.tiles.base.interactor.QSTileDataInteractor
+import com.android.systemui.qs.tiles.base.domain.interactor.QSTileDataInteractor
+import com.android.systemui.qs.tiles.base.domain.model.DataUpdateTrigger
import com.android.systemui.qs.tiles.impl.flashlight.domain.model.FlashlightTileModel
import com.android.systemui.statusbar.policy.FlashlightController
+import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
import javax.inject.Inject
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
@@ -30,22 +30,23 @@ import kotlinx.coroutines.flow.flowOf
/** Observes flashlight state changes providing the [FlashlightTileModel]. */
class FlashlightTileDataInteractor
@Inject
-constructor(
- private val flashlightController: FlashlightController,
-) : QSTileDataInteractor<FlashlightTileModel> {
+constructor(private val flashlightController: FlashlightController) :
+ QSTileDataInteractor<FlashlightTileModel> {
override fun tileData(
user: UserHandle,
- triggers: Flow<DataUpdateTrigger>
+ triggers: Flow<DataUpdateTrigger>,
): Flow<FlashlightTileModel> = conflatedCallbackFlow {
val callback =
object : FlashlightController.FlashlightListener {
override fun onFlashlightChanged(enabled: Boolean) {
trySend(FlashlightTileModel.FlashlightAvailable(enabled))
}
+
override fun onFlashlightError() {
trySend(FlashlightTileModel.FlashlightAvailable(false))
}
+
override fun onFlashlightAvailabilityChanged(available: Boolean) {
trySend(
if (available)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/flashlight/domain/interactor/FlashlightTileUserActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/flashlight/domain/interactor/FlashlightTileUserActionInteractor.kt
index 13afc15d17ef..8a76b2a61677 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/flashlight/domain/interactor/FlashlightTileUserActionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/flashlight/domain/interactor/FlashlightTileUserActionInteractor.kt
@@ -17,19 +17,18 @@
package com.android.systemui.qs.tiles.impl.flashlight.domain.interactor
import android.app.ActivityManager
-import com.android.systemui.qs.tiles.base.interactor.QSTileInput
-import com.android.systemui.qs.tiles.base.interactor.QSTileUserActionInteractor
+import com.android.systemui.qs.tiles.base.domain.interactor.QSTileUserActionInteractor
+import com.android.systemui.qs.tiles.base.domain.model.QSTileInput
+import com.android.systemui.qs.tiles.base.shared.model.QSTileUserAction
import com.android.systemui.qs.tiles.impl.flashlight.domain.model.FlashlightTileModel
-import com.android.systemui.qs.tiles.viewmodel.QSTileUserAction
import com.android.systemui.statusbar.policy.FlashlightController
import javax.inject.Inject
/** Handles flashlight tile clicks. */
class FlashlightTileUserActionInteractor
@Inject
-constructor(
- private val flashlightController: FlashlightController,
-) : QSTileUserActionInteractor<FlashlightTileModel> {
+constructor(private val flashlightController: FlashlightController) :
+ QSTileUserActionInteractor<FlashlightTileModel> {
override suspend fun handleInput(input: QSTileInput<FlashlightTileModel>) =
with(input) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/flashlight/domain/FlashlightMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/flashlight/ui/mapper/FlashlightMapper.kt
index 32ccba6f1fa5..0be4a9fffe1b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/flashlight/domain/FlashlightMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/flashlight/ui/mapper/FlashlightMapper.kt
@@ -14,15 +14,15 @@
* limitations under the License.
*/
-package com.android.systemui.qs.tiles.impl.flashlight.domain
+package com.android.systemui.qs.tiles.impl.flashlight.ui.mapper
import android.content.res.Resources
import android.content.res.Resources.Theme
import com.android.systemui.common.shared.model.Icon
-import com.android.systemui.qs.tiles.base.interactor.QSTileDataToStateMapper
+import com.android.systemui.qs.tiles.base.shared.model.QSTileConfig
+import com.android.systemui.qs.tiles.base.shared.model.QSTileState
+import com.android.systemui.qs.tiles.base.ui.model.QSTileDataToStateMapper
import com.android.systemui.qs.tiles.impl.flashlight.domain.model.FlashlightTileModel
-import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
-import com.android.systemui.qs.tiles.viewmodel.QSTileState
import com.android.systemui.res.R
import com.android.systemui.shade.ShadeDisplayAware
import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/fontscaling/domain/interactor/FontScalingTileDataInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/fontscaling/domain/interactor/FontScalingTileDataInteractor.kt
index 745e6a301689..3464741ffe9c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/fontscaling/domain/interactor/FontScalingTileDataInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/fontscaling/domain/interactor/FontScalingTileDataInteractor.kt
@@ -17,8 +17,8 @@
package com.android.systemui.qs.tiles.impl.fontscaling.domain.interactor
import android.os.UserHandle
-import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
-import com.android.systemui.qs.tiles.base.interactor.QSTileDataInteractor
+import com.android.systemui.qs.tiles.base.domain.interactor.QSTileDataInteractor
+import com.android.systemui.qs.tiles.base.domain.model.DataUpdateTrigger
import com.android.systemui.qs.tiles.impl.fontscaling.domain.model.FontScalingTileModel
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
@@ -29,7 +29,7 @@ class FontScalingTileDataInteractor @Inject constructor() :
QSTileDataInteractor<FontScalingTileModel> {
override fun tileData(
user: UserHandle,
- triggers: Flow<DataUpdateTrigger>
+ triggers: Flow<DataUpdateTrigger>,
): Flow<FontScalingTileModel> = flowOf(FontScalingTileModel)
override fun availability(user: UserHandle): Flow<Boolean> = flowOf(true)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/fontscaling/domain/interactor/FontScalingTileUserActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/fontscaling/domain/interactor/FontScalingTileUserActionInteractor.kt
index 0ebb51e722af..65db0f9fd8b1 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/fontscaling/domain/interactor/FontScalingTileUserActionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/fontscaling/domain/interactor/FontScalingTileUserActionInteractor.kt
@@ -25,11 +25,11 @@ import com.android.systemui.animation.DialogTransitionAnimator
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.qs.shared.QSSettingsPackageRepository
-import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandler
-import com.android.systemui.qs.tiles.base.interactor.QSTileInput
-import com.android.systemui.qs.tiles.base.interactor.QSTileUserActionInteractor
+import com.android.systemui.qs.tiles.base.domain.actions.QSTileIntentUserInputHandler
+import com.android.systemui.qs.tiles.base.domain.interactor.QSTileUserActionInteractor
+import com.android.systemui.qs.tiles.base.domain.model.QSTileInput
+import com.android.systemui.qs.tiles.base.shared.model.QSTileUserAction
import com.android.systemui.qs.tiles.impl.fontscaling.domain.model.FontScalingTileModel
-import com.android.systemui.qs.tiles.viewmodel.QSTileUserAction
import com.android.systemui.statusbar.phone.SystemUIDialog
import com.android.systemui.statusbar.policy.KeyguardStateController
import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/fontscaling/domain/FontScalingTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/fontscaling/ui/mapper/FontScalingTileMapper.kt
index c571b136e18b..659e1fe6a81d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/fontscaling/domain/FontScalingTileMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/fontscaling/ui/mapper/FontScalingTileMapper.kt
@@ -14,14 +14,14 @@
* limitations under the License.
*/
-package com.android.systemui.qs.tiles.impl.fontscaling.domain
+package com.android.systemui.qs.tiles.impl.fontscaling.ui.mapper
import android.content.res.Resources
import com.android.systemui.common.shared.model.Icon
-import com.android.systemui.qs.tiles.base.interactor.QSTileDataToStateMapper
+import com.android.systemui.qs.tiles.base.shared.model.QSTileConfig
+import com.android.systemui.qs.tiles.base.shared.model.QSTileState
+import com.android.systemui.qs.tiles.base.ui.model.QSTileDataToStateMapper
import com.android.systemui.qs.tiles.impl.fontscaling.domain.model.FontScalingTileModel
-import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
-import com.android.systemui.qs.tiles.viewmodel.QSTileState
import com.android.systemui.res.R
import com.android.systemui.shade.ShadeDisplayAware
import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/interactor/HearingDevicesTileDataInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/interactor/HearingDevicesTileDataInteractor.kt
index 33b7feb49b09..50ea30dc8011 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/interactor/HearingDevicesTileDataInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/interactor/HearingDevicesTileDataInteractor.kt
@@ -19,8 +19,8 @@ package com.android.systemui.qs.tiles.impl.hearingdevices.domain.interactor
import android.os.UserHandle
import com.android.systemui.accessibility.hearingaid.HearingDevicesChecker
import com.android.systemui.dagger.qualifiers.Background
-import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
-import com.android.systemui.qs.tiles.base.interactor.QSTileDataInteractor
+import com.android.systemui.qs.tiles.base.domain.interactor.QSTileDataInteractor
+import com.android.systemui.qs.tiles.base.domain.model.DataUpdateTrigger
import com.android.systemui.qs.tiles.impl.hearingdevices.domain.model.HearingDevicesTileModel
import com.android.systemui.statusbar.policy.BluetoothController
import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/interactor/HearingDevicesTileUserActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/interactor/HearingDevicesTileUserActionInteractor.kt
index 268efcef9062..049d0618875e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/interactor/HearingDevicesTileUserActionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/interactor/HearingDevicesTileUserActionInteractor.kt
@@ -22,11 +22,11 @@ import com.android.systemui.accessibility.hearingaid.HearingDevicesDialogManager
import com.android.systemui.accessibility.hearingaid.HearingDevicesUiEventLogger.Companion.LAUNCH_SOURCE_QS_TILE
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.qs.shared.QSSettingsPackageRepository
-import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandler
-import com.android.systemui.qs.tiles.base.interactor.QSTileInput
-import com.android.systemui.qs.tiles.base.interactor.QSTileUserActionInteractor
+import com.android.systemui.qs.tiles.base.domain.actions.QSTileIntentUserInputHandler
+import com.android.systemui.qs.tiles.base.domain.interactor.QSTileUserActionInteractor
+import com.android.systemui.qs.tiles.base.domain.model.QSTileInput
+import com.android.systemui.qs.tiles.base.shared.model.QSTileUserAction
import com.android.systemui.qs.tiles.impl.hearingdevices.domain.model.HearingDevicesTileModel
-import com.android.systemui.qs.tiles.viewmodel.QSTileUserAction
import javax.inject.Inject
import kotlin.coroutines.CoroutineContext
import kotlinx.coroutines.withContext
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/HearingDevicesTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/hearingdevices/ui/mapper/HearingDevicesTileMapper.kt
index 12f71491c7b4..d89c535b2c67 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/HearingDevicesTileMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/hearingdevices/ui/mapper/HearingDevicesTileMapper.kt
@@ -14,14 +14,14 @@
* limitations under the License.
*/
-package com.android.systemui.qs.tiles.impl.hearingdevices.domain
+package com.android.systemui.qs.tiles.impl.hearingdevices.ui.mapper
import android.content.res.Resources
import com.android.systemui.common.shared.model.Icon
-import com.android.systemui.qs.tiles.base.interactor.QSTileDataToStateMapper
+import com.android.systemui.qs.tiles.base.shared.model.QSTileConfig
+import com.android.systemui.qs.tiles.base.shared.model.QSTileState
+import com.android.systemui.qs.tiles.base.ui.model.QSTileDataToStateMapper
import com.android.systemui.qs.tiles.impl.hearingdevices.domain.model.HearingDevicesTileModel
-import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
-import com.android.systemui.qs.tiles.viewmodel.QSTileState
import com.android.systemui.res.R
import com.android.systemui.shade.ShadeDisplayAware
import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/internet/domain/interactor/InternetTileDataInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/internet/domain/interactor/InternetTileDataInteractor.kt
index 871c051722c6..786ac47a0190 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/internet/domain/interactor/InternetTileDataInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/internet/domain/interactor/InternetTileDataInteractor.kt
@@ -24,8 +24,8 @@ import com.android.systemui.common.shared.model.ContentDescription
import com.android.systemui.common.shared.model.ContentDescription.Companion.loadContentDescription
import com.android.systemui.common.shared.model.Text
import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
-import com.android.systemui.qs.tiles.base.interactor.QSTileDataInteractor
+import com.android.systemui.qs.tiles.base.domain.interactor.QSTileDataInteractor
+import com.android.systemui.qs.tiles.base.domain.model.DataUpdateTrigger
import com.android.systemui.qs.tiles.impl.internet.domain.model.InternetTileModel
import com.android.systemui.res.R
import com.android.systemui.shade.ShadeDisplayAware
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/internet/domain/interactor/InternetTileUserActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/internet/domain/interactor/InternetTileUserActionInteractor.kt
index 0431e36fef6a..7fd282c63d84 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/internet/domain/interactor/InternetTileUserActionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/internet/domain/interactor/InternetTileUserActionInteractor.kt
@@ -19,12 +19,12 @@ package com.android.systemui.qs.tiles.impl.internet.domain.interactor
import android.content.Intent
import android.provider.Settings
import com.android.systemui.dagger.qualifiers.Main
-import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandler
-import com.android.systemui.qs.tiles.base.interactor.QSTileInput
-import com.android.systemui.qs.tiles.base.interactor.QSTileUserActionInteractor
+import com.android.systemui.qs.tiles.base.domain.actions.QSTileIntentUserInputHandler
+import com.android.systemui.qs.tiles.base.domain.interactor.QSTileUserActionInteractor
+import com.android.systemui.qs.tiles.base.domain.model.QSTileInput
+import com.android.systemui.qs.tiles.base.shared.model.QSTileUserAction
import com.android.systemui.qs.tiles.dialog.InternetDialogManager
import com.android.systemui.qs.tiles.impl.internet.domain.model.InternetTileModel
-import com.android.systemui.qs.tiles.viewmodel.QSTileUserAction
import com.android.systemui.statusbar.connectivity.AccessPointController
import javax.inject.Inject
import kotlin.coroutines.CoroutineContext
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/internet/domain/InternetTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/internet/ui/mapper/InternetTileMapper.kt
index 8d5880554277..a96ad38921a5 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/internet/domain/InternetTileMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/internet/ui/mapper/InternetTileMapper.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.qs.tiles.impl.internet.domain
+package com.android.systemui.qs.tiles.impl.internet.ui.mapper
import android.content.Context
import android.content.res.Resources
@@ -25,10 +25,10 @@ import com.android.systemui.common.shared.model.ContentDescription.Companion.loa
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.common.shared.model.Text.Companion.loadText
import com.android.systemui.dagger.qualifiers.Main
-import com.android.systemui.qs.tiles.base.interactor.QSTileDataToStateMapper
+import com.android.systemui.qs.tiles.base.shared.model.QSTileConfig
+import com.android.systemui.qs.tiles.base.shared.model.QSTileState
+import com.android.systemui.qs.tiles.base.ui.model.QSTileDataToStateMapper
import com.android.systemui.qs.tiles.impl.internet.domain.model.InternetTileModel
-import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
-import com.android.systemui.qs.tiles.viewmodel.QSTileState
import com.android.systemui.res.R
import com.android.systemui.shade.ShadeDisplayAware
import com.android.systemui.statusbar.pipeline.shared.ui.model.InternetTileIconModel
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/inversion/domain/interactor/ColorInversionTileDataInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/inversion/domain/interactor/ColorInversionTileDataInteractor.kt
index 7f3dd3e17b79..fea9e7eb3bda 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/inversion/domain/interactor/ColorInversionTileDataInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/inversion/domain/interactor/ColorInversionTileDataInteractor.kt
@@ -18,8 +18,8 @@ package com.android.systemui.qs.tiles.impl.inversion.domain.interactor
import android.os.UserHandle
import com.android.systemui.accessibility.data.repository.ColorInversionRepository
-import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
-import com.android.systemui.qs.tiles.base.interactor.QSTileDataInteractor
+import com.android.systemui.qs.tiles.base.domain.interactor.QSTileDataInteractor
+import com.android.systemui.qs.tiles.base.domain.model.DataUpdateTrigger
import com.android.systemui.qs.tiles.impl.inversion.domain.model.ColorInversionTileModel
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
@@ -29,15 +29,15 @@ import kotlinx.coroutines.flow.map
/** Observes color inversion state changes providing the [ColorInversionTileModel]. */
class ColorInversionTileDataInteractor
@Inject
-constructor(
- private val colorInversionRepository: ColorInversionRepository,
-) : QSTileDataInteractor<ColorInversionTileModel> {
+constructor(private val colorInversionRepository: ColorInversionRepository) :
+ QSTileDataInteractor<ColorInversionTileModel> {
override fun tileData(
user: UserHandle,
- triggers: Flow<DataUpdateTrigger>
+ triggers: Flow<DataUpdateTrigger>,
): Flow<ColorInversionTileModel> {
return colorInversionRepository.isEnabled(user).map { ColorInversionTileModel(it) }
}
+
override fun availability(user: UserHandle): Flow<Boolean> = flowOf(true)
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/inversion/domain/interactor/ColorInversionUserActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/inversion/domain/interactor/ColorInversionUserActionInteractor.kt
index f78349718697..12530bc8361c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/inversion/domain/interactor/ColorInversionUserActionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/inversion/domain/interactor/ColorInversionUserActionInteractor.kt
@@ -20,11 +20,11 @@ import android.content.Intent
import android.provider.Settings
import com.android.systemui.accessibility.data.repository.ColorInversionRepository
import com.android.systemui.qs.shared.QSSettingsPackageRepository
-import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandler
-import com.android.systemui.qs.tiles.base.interactor.QSTileInput
-import com.android.systemui.qs.tiles.base.interactor.QSTileUserActionInteractor
+import com.android.systemui.qs.tiles.base.domain.actions.QSTileIntentUserInputHandler
+import com.android.systemui.qs.tiles.base.domain.interactor.QSTileUserActionInteractor
+import com.android.systemui.qs.tiles.base.domain.model.QSTileInput
+import com.android.systemui.qs.tiles.base.shared.model.QSTileUserAction
import com.android.systemui.qs.tiles.impl.inversion.domain.model.ColorInversionTileModel
-import com.android.systemui.qs.tiles.viewmodel.QSTileUserAction
import javax.inject.Inject
/** Handles color inversion tile clicks. */
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/inversion/domain/ColorInversionTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/inversion/ui/mapper/ColorInversionTileMapper.kt
index 05590e803ffa..868abc045ad6 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/inversion/domain/ColorInversionTileMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/inversion/ui/mapper/ColorInversionTileMapper.kt
@@ -14,15 +14,15 @@
* limitations under the License.
*/
-package com.android.systemui.qs.tiles.impl.inversion.domain
+package com.android.systemui.qs.tiles.impl.inversion.ui.mapper
import android.content.res.Resources
import android.content.res.Resources.Theme
import com.android.systemui.common.shared.model.Icon
-import com.android.systemui.qs.tiles.base.interactor.QSTileDataToStateMapper
+import com.android.systemui.qs.tiles.base.shared.model.QSTileConfig
+import com.android.systemui.qs.tiles.base.shared.model.QSTileState
+import com.android.systemui.qs.tiles.base.ui.model.QSTileDataToStateMapper
import com.android.systemui.qs.tiles.impl.inversion.domain.model.ColorInversionTileModel
-import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
-import com.android.systemui.qs.tiles.viewmodel.QSTileState
import com.android.systemui.res.R
import com.android.systemui.shade.ShadeDisplayAware
import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingModel.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/irecording/data/model/IssueRecordingModel.kt
index 260729b6a868..704102b25ce2 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/irecording/data/model/IssueRecordingModel.kt
@@ -14,6 +14,6 @@
* limitations under the License.
*/
-package com.android.systemui.qs.tiles.impl.irecording
+package com.android.systemui.qs.tiles.impl.irecording.data.model
@JvmInline value class IssueRecordingModel(val isRecording: Boolean)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingDataInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/irecording/domain/interactor/IssueRecordingDataInteractor.kt
index 09a6ce8e5ec0..95fe191aad3f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingDataInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/irecording/domain/interactor/IssueRecordingDataInteractor.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2024 The Android Open Source Project
+ * Copyright (C) 2025 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.
@@ -14,13 +14,14 @@
* limitations under the License.
*/
-package com.android.systemui.qs.tiles.impl.irecording
+package com.android.systemui.qs.tiles.impl.irecording.domain.interactor
import android.os.UserHandle
import com.android.systemui.Flags
import com.android.systemui.dagger.qualifiers.Background
-import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
-import com.android.systemui.qs.tiles.base.interactor.QSTileDataInteractor
+import com.android.systemui.qs.tiles.base.domain.interactor.QSTileDataInteractor
+import com.android.systemui.qs.tiles.base.domain.model.DataUpdateTrigger
+import com.android.systemui.qs.tiles.impl.irecording.data.model.IssueRecordingModel
import com.android.systemui.recordissue.IssueRecordingState
import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingUserActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/irecording/domain/interactor/IssueRecordingUserActionInteractor.kt
index fceee5a3379e..7182a37b7064 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingUserActionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/irecording/domain/interactor/IssueRecordingUserActionInteractor.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.qs.tiles.impl.irecording
+package com.android.systemui.qs.tiles.impl.irecording.domain.interactor
import android.app.AlertDialog
import android.app.BroadcastOptions
@@ -30,9 +30,10 @@ import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.qs.pipeline.domain.interactor.PanelInteractor
import com.android.systemui.qs.tiles.DELAY_MS
import com.android.systemui.qs.tiles.INTERVAL_MS
-import com.android.systemui.qs.tiles.base.interactor.QSTileInput
-import com.android.systemui.qs.tiles.base.interactor.QSTileUserActionInteractor
-import com.android.systemui.qs.tiles.viewmodel.QSTileUserAction
+import com.android.systemui.qs.tiles.base.domain.interactor.QSTileUserActionInteractor
+import com.android.systemui.qs.tiles.base.domain.model.QSTileInput
+import com.android.systemui.qs.tiles.base.shared.model.QSTileUserAction
+import com.android.systemui.qs.tiles.impl.irecording.data.model.IssueRecordingModel
import com.android.systemui.recordissue.IssueRecordingService.Companion.getStartIntent
import com.android.systemui.recordissue.IssueRecordingService.Companion.getStopIntent
import com.android.systemui.recordissue.IssueRecordingState
@@ -47,8 +48,6 @@ import javax.inject.Inject
import kotlin.coroutines.CoroutineContext
import kotlinx.coroutines.withContext
-private const val TAG = "IssueRecordingActionInteractor"
-
class IssueRecordingUserActionInteractor
@Inject
constructor(
@@ -128,4 +127,8 @@ constructor(
action,
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE,
)
+
+ companion object {
+ private const val TAG = "IssueRecordingUserActionInteractor"
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/irecording/ui/mapper/IssueRecordingMapper.kt
index afb137e1e92f..bd51bbeed4b9 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/irecording/ui/mapper/IssueRecordingMapper.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2024 The Android Open Source Project
+ * Copyright (C) 2025 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.
@@ -14,14 +14,15 @@
* limitations under the License.
*/
-package com.android.systemui.qs.tiles.impl.irecording
+package com.android.systemui.qs.tiles.impl.irecording.ui.mapper
import android.content.res.Resources
import android.content.res.Resources.Theme
import com.android.systemui.common.shared.model.Icon
-import com.android.systemui.qs.tiles.base.interactor.QSTileDataToStateMapper
-import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
-import com.android.systemui.qs.tiles.viewmodel.QSTileState
+import com.android.systemui.qs.tiles.base.shared.model.QSTileConfig
+import com.android.systemui.qs.tiles.base.shared.model.QSTileState
+import com.android.systemui.qs.tiles.base.ui.model.QSTileDataToStateMapper
+import com.android.systemui.qs.tiles.impl.irecording.data.model.IssueRecordingModel
import com.android.systemui.res.R
import com.android.systemui.shade.ShadeDisplayAware
import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/location/domain/interactor/LocationTileDataInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/location/domain/interactor/LocationTileDataInteractor.kt
index bd2f2c987ccf..052d062364cf 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/location/domain/interactor/LocationTileDataInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/location/domain/interactor/LocationTileDataInteractor.kt
@@ -17,8 +17,8 @@
package com.android.systemui.qs.tiles.impl.location.domain.interactor
import android.os.UserHandle
-import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
-import com.android.systemui.qs.tiles.base.interactor.QSTileDataInteractor
+import com.android.systemui.qs.tiles.base.domain.interactor.QSTileDataInteractor
+import com.android.systemui.qs.tiles.base.domain.model.DataUpdateTrigger
import com.android.systemui.qs.tiles.impl.location.domain.model.LocationTileModel
import com.android.systemui.statusbar.policy.LocationController
import com.android.systemui.util.kotlin.isLocationEnabledFlow
@@ -30,13 +30,12 @@ import kotlinx.coroutines.flow.map
/** Observes location state changes providing the [LocationTileModel]. */
class LocationTileDataInteractor
@Inject
-constructor(
- private val locationController: LocationController,
-) : QSTileDataInteractor<LocationTileModel> {
+constructor(private val locationController: LocationController) :
+ QSTileDataInteractor<LocationTileModel> {
override fun tileData(
user: UserHandle,
- triggers: Flow<DataUpdateTrigger>
+ triggers: Flow<DataUpdateTrigger>,
): Flow<LocationTileModel> =
locationController.isLocationEnabledFlow().map { LocationTileModel(it) }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/location/domain/interactor/LocationTileUserActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/location/domain/interactor/LocationTileUserActionInteractor.kt
index d46bcfc3b947..f5d1400c9c7a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/location/domain/interactor/LocationTileUserActionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/location/domain/interactor/LocationTileUserActionInteractor.kt
@@ -18,21 +18,21 @@ package com.android.systemui.qs.tiles.impl.location.domain.interactor
import android.content.Intent
import android.provider.Settings
+import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.systemui.coroutines.newTracingContext
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.plugins.ActivityStarter
-import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandler
-import com.android.systemui.qs.tiles.base.interactor.QSTileInput
-import com.android.systemui.qs.tiles.base.interactor.QSTileUserActionInteractor
+import com.android.systemui.qs.tiles.base.domain.actions.QSTileIntentUserInputHandler
+import com.android.systemui.qs.tiles.base.domain.interactor.QSTileUserActionInteractor
+import com.android.systemui.qs.tiles.base.domain.model.QSTileInput
+import com.android.systemui.qs.tiles.base.shared.model.QSTileUserAction
import com.android.systemui.qs.tiles.impl.location.domain.model.LocationTileModel
-import com.android.systemui.qs.tiles.viewmodel.QSTileUserAction
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.statusbar.policy.LocationController
import javax.inject.Inject
import kotlin.coroutines.CoroutineContext
import kotlinx.coroutines.CoroutineScope
-import com.android.app.tracing.coroutines.launchTraced as launch
import kotlinx.coroutines.withContext
/** Handles location tile clicks. */
@@ -68,7 +68,7 @@ constructor(
is QSTileUserAction.LongClick -> {
qsTileIntentUserActionHandler.handle(
action.expandable,
- Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS)
+ Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS),
)
}
is QSTileUserAction.ToggleClick -> {}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/location/domain/LocationTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/location/ui/mapper/LocationTileMapper.kt
index ced5a4f099a2..d2c9116b0f24 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/location/domain/LocationTileMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/location/ui/mapper/LocationTileMapper.kt
@@ -14,15 +14,15 @@
* limitations under the License.
*/
-package com.android.systemui.qs.tiles.impl.location.domain
+package com.android.systemui.qs.tiles.impl.location.ui.mapper
import android.content.res.Resources
import android.content.res.Resources.Theme
import com.android.systemui.common.shared.model.Icon
-import com.android.systemui.qs.tiles.base.interactor.QSTileDataToStateMapper
+import com.android.systemui.qs.tiles.base.shared.model.QSTileConfig
+import com.android.systemui.qs.tiles.base.shared.model.QSTileState
+import com.android.systemui.qs.tiles.base.ui.model.QSTileDataToStateMapper
import com.android.systemui.qs.tiles.impl.location.domain.model.LocationTileModel
-import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
-import com.android.systemui.qs.tiles.viewmodel.QSTileState
import com.android.systemui.res.R
import com.android.systemui.shade.ShadeDisplayAware
import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesDndTileDataInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesDndTileDataInteractor.kt
index e47ae690bc97..c3e7bea4a466 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesDndTileDataInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesDndTileDataInteractor.kt
@@ -24,8 +24,8 @@ import com.android.settingslib.notification.modes.ZenMode
import com.android.settingslib.notification.modes.ZenModeDescriptions
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.modes.shared.ModesUi
-import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
-import com.android.systemui.qs.tiles.base.interactor.QSTileDataInteractor
+import com.android.systemui.qs.tiles.base.domain.interactor.QSTileDataInteractor
+import com.android.systemui.qs.tiles.base.domain.model.DataUpdateTrigger
import com.android.systemui.qs.tiles.impl.modes.domain.model.ModesDndTileModel
import com.android.systemui.shade.ShadeDisplayAware
import com.android.systemui.statusbar.policy.domain.interactor.ZenModeInteractor
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesDndTileUserActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesDndTileUserActionInteractor.kt
index e8fcea070ede..012ae036cf47 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesDndTileUserActionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesDndTileUserActionInteractor.kt
@@ -24,11 +24,11 @@ import com.android.systemui.animation.Expandable
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.qs.shared.QSSettingsPackageRepository
-import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandler
-import com.android.systemui.qs.tiles.base.interactor.QSTileInput
-import com.android.systemui.qs.tiles.base.interactor.QSTileUserActionInteractor
+import com.android.systemui.qs.tiles.base.domain.actions.QSTileIntentUserInputHandler
+import com.android.systemui.qs.tiles.base.domain.interactor.QSTileUserActionInteractor
+import com.android.systemui.qs.tiles.base.domain.model.QSTileInput
+import com.android.systemui.qs.tiles.base.shared.model.QSTileUserAction
import com.android.systemui.qs.tiles.impl.modes.domain.model.ModesDndTileModel
-import com.android.systemui.qs.tiles.viewmodel.QSTileUserAction
import com.android.systemui.statusbar.policy.domain.interactor.ZenModeInteractor
import com.android.systemui.statusbar.policy.ui.dialog.ModesDialogDelegate
import com.android.systemui.statusbar.policy.ui.dialog.ModesDialogEventLogger
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileDataInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileDataInteractor.kt
index 479f61823912..e97985c6ab78 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileDataInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileDataInteractor.kt
@@ -25,8 +25,8 @@ import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.modes.shared.ModesUi
import com.android.systemui.modes.shared.ModesUiIcons
import com.android.systemui.qs.tiles.ModesTile
-import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
-import com.android.systemui.qs.tiles.base.interactor.QSTileDataInteractor
+import com.android.systemui.qs.tiles.base.domain.interactor.QSTileDataInteractor
+import com.android.systemui.qs.tiles.base.domain.model.DataUpdateTrigger
import com.android.systemui.qs.tiles.impl.modes.domain.model.ModesTileModel
import com.android.systemui.shade.ShadeDisplayAware
import com.android.systemui.statusbar.policy.domain.interactor.ZenModeInteractor
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileUserActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileUserActionInteractor.kt
index ab1326a8bafb..5240a1809b76 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileUserActionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileUserActionInteractor.kt
@@ -23,11 +23,11 @@ import com.android.systemui.animation.Expandable
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.qs.flags.QSComposeFragment
-import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandler
-import com.android.systemui.qs.tiles.base.interactor.QSTileInput
-import com.android.systemui.qs.tiles.base.interactor.QSTileUserActionInteractor
+import com.android.systemui.qs.tiles.base.domain.actions.QSTileIntentUserInputHandler
+import com.android.systemui.qs.tiles.base.domain.interactor.QSTileUserActionInteractor
+import com.android.systemui.qs.tiles.base.domain.model.QSTileInput
+import com.android.systemui.qs.tiles.base.shared.model.QSTileUserAction
import com.android.systemui.qs.tiles.impl.modes.domain.model.ModesTileModel
-import com.android.systemui.qs.tiles.viewmodel.QSTileUserAction
import com.android.systemui.statusbar.policy.domain.interactor.ZenModeInteractor
import com.android.systemui.statusbar.policy.ui.dialog.ModesDialogDelegate
import com.android.systemui.statusbar.policy.ui.dialog.ModesDialogEventLogger
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/ui/ModesDndTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/ui/ModesDndTileMapper.kt
index 73a8766ef45e..632c572455e4 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/ui/ModesDndTileMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/ui/ModesDndTileMapper.kt
@@ -19,10 +19,10 @@ package com.android.systemui.qs.tiles.impl.modes.ui
import android.content.res.Resources
import android.widget.Switch
import com.android.systemui.common.shared.model.Icon
-import com.android.systemui.qs.tiles.base.interactor.QSTileDataToStateMapper
+import com.android.systemui.qs.tiles.base.shared.model.QSTileConfig
+import com.android.systemui.qs.tiles.base.shared.model.QSTileState
+import com.android.systemui.qs.tiles.base.ui.model.QSTileDataToStateMapper
import com.android.systemui.qs.tiles.impl.modes.domain.model.ModesDndTileModel
-import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
-import com.android.systemui.qs.tiles.viewmodel.QSTileState
import com.android.systemui.res.R
import com.android.systemui.shade.ShadeDisplayAware
import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/ui/ModesTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/ui/mapper/ModesTileMapper.kt
index 99ae3b8db709..4668350131a6 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/ui/ModesTileMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/ui/mapper/ModesTileMapper.kt
@@ -14,15 +14,15 @@
* limitations under the License.
*/
-package com.android.systemui.qs.tiles.impl.modes.ui
+package com.android.systemui.qs.tiles.impl.modes.ui.mapper
import android.content.res.Resources
import android.icu.text.MessageFormat
import android.widget.Button
-import com.android.systemui.qs.tiles.base.interactor.QSTileDataToStateMapper
+import com.android.systemui.qs.tiles.base.shared.model.QSTileConfig
+import com.android.systemui.qs.tiles.base.shared.model.QSTileState
+import com.android.systemui.qs.tiles.base.ui.model.QSTileDataToStateMapper
import com.android.systemui.qs.tiles.impl.modes.domain.model.ModesTileModel
-import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
-import com.android.systemui.qs.tiles.viewmodel.QSTileState
import com.android.systemui.res.R
import com.android.systemui.shade.ShadeDisplayAware
import java.util.Locale
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/night/domain/interactor/NightDisplayTileDataInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/night/domain/interactor/NightDisplayTileDataInteractor.kt
index e8e43e8fc749..f6f267fbce0e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/night/domain/interactor/NightDisplayTileDataInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/night/domain/interactor/NightDisplayTileDataInteractor.kt
@@ -20,8 +20,8 @@ import android.content.Context
import android.hardware.display.ColorDisplayManager
import android.os.UserHandle
import com.android.systemui.accessibility.data.repository.NightDisplayRepository
-import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
-import com.android.systemui.qs.tiles.base.interactor.QSTileDataInteractor
+import com.android.systemui.qs.tiles.base.domain.interactor.QSTileDataInteractor
+import com.android.systemui.qs.tiles.base.domain.model.DataUpdateTrigger
import com.android.systemui.qs.tiles.impl.night.domain.model.NightDisplayTileModel
import com.android.systemui.shade.ShadeDisplayAware
import com.android.systemui.util.time.DateFormatUtil
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/night/domain/interactor/NightDisplayTileUserActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/night/domain/interactor/NightDisplayTileUserActionInteractor.kt
index 7076a8f757fe..41739422ecb1 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/night/domain/interactor/NightDisplayTileUserActionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/night/domain/interactor/NightDisplayTileUserActionInteractor.kt
@@ -22,12 +22,12 @@ import android.provider.Settings
import com.android.systemui.accessibility.data.repository.NightDisplayRepository
import com.android.systemui.accessibility.qs.QSAccessibilityModule
import com.android.systemui.qs.pipeline.shared.TileSpec
-import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandler
-import com.android.systemui.qs.tiles.base.interactor.QSTileInput
-import com.android.systemui.qs.tiles.base.interactor.QSTileUserActionInteractor
-import com.android.systemui.qs.tiles.base.logging.QSTileLogger
+import com.android.systemui.qs.tiles.base.domain.actions.QSTileIntentUserInputHandler
+import com.android.systemui.qs.tiles.base.domain.interactor.QSTileUserActionInteractor
+import com.android.systemui.qs.tiles.base.domain.model.QSTileInput
+import com.android.systemui.qs.tiles.base.shared.logging.QSTileLogger
+import com.android.systemui.qs.tiles.base.shared.model.QSTileUserAction
import com.android.systemui.qs.tiles.impl.night.domain.model.NightDisplayTileModel
-import com.android.systemui.qs.tiles.viewmodel.QSTileUserAction
import javax.inject.Inject
/** Handles night display tile clicks. */
@@ -52,7 +52,7 @@ constructor(
is QSTileUserAction.LongClick -> {
qsTileIntentUserActionHandler.handle(
action.expandable,
- Intent(Settings.ACTION_NIGHT_DISPLAY_SETTINGS)
+ Intent(Settings.ACTION_NIGHT_DISPLAY_SETTINGS),
)
}
is QSTileUserAction.ToggleClick -> {}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/night/ui/NightDisplayTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/night/ui/mapper/NightDisplayTileMapper.kt
index 16b36289ad95..3e478754d261 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/night/ui/NightDisplayTileMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/night/ui/mapper/NightDisplayTileMapper.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2024 The Android Open Source Project
+ * Copyright (C) 2025 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.qs.tiles.impl.night.ui
+package com.android.systemui.qs.tiles.impl.night.ui.mapper
import android.content.res.Resources
import android.service.quicksettings.Tile
@@ -23,11 +23,11 @@ import androidx.annotation.StringRes
import com.android.systemui.accessibility.qs.QSAccessibilityModule
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.qs.pipeline.shared.TileSpec
-import com.android.systemui.qs.tiles.base.interactor.QSTileDataToStateMapper
-import com.android.systemui.qs.tiles.base.logging.QSTileLogger
+import com.android.systemui.qs.tiles.base.shared.logging.QSTileLogger
+import com.android.systemui.qs.tiles.base.shared.model.QSTileConfig
+import com.android.systemui.qs.tiles.base.shared.model.QSTileState
+import com.android.systemui.qs.tiles.base.ui.model.QSTileDataToStateMapper
import com.android.systemui.qs.tiles.impl.night.domain.model.NightDisplayTileModel
-import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
-import com.android.systemui.qs.tiles.viewmodel.QSTileState
import com.android.systemui.res.R
import com.android.systemui.shade.ShadeDisplayAware
import java.time.DateTimeException
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/notes/domain/interactor/NotesTileDataInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/notes/domain/interactor/NotesTileDataInteractor.kt
index a501b8561330..157da8a5f321 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/notes/domain/interactor/NotesTileDataInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/notes/domain/interactor/NotesTileDataInteractor.kt
@@ -19,8 +19,8 @@ package com.android.systemui.qs.tiles.impl.notes.domain.interactor
import android.os.UserHandle
import com.android.systemui.Flags
import com.android.systemui.notetask.NoteTaskEnabledKey
-import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
-import com.android.systemui.qs.tiles.base.interactor.QSTileDataInteractor
+import com.android.systemui.qs.tiles.base.domain.interactor.QSTileDataInteractor
+import com.android.systemui.qs.tiles.base.domain.model.DataUpdateTrigger
import com.android.systemui.qs.tiles.impl.notes.domain.model.NotesTileModel
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/notes/domain/interactor/NotesTileUserActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/notes/domain/interactor/NotesTileUserActionInteractor.kt
index df01d99df0df..527f9e841ea9 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/notes/domain/interactor/NotesTileUserActionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/notes/domain/interactor/NotesTileUserActionInteractor.kt
@@ -20,15 +20,16 @@ import com.android.systemui.animation.Expandable
import com.android.systemui.notetask.NoteTaskController
import com.android.systemui.notetask.NoteTaskEntryPoint
import com.android.systemui.qs.pipeline.domain.interactor.PanelInteractor
-import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandler
-import com.android.systemui.qs.tiles.base.interactor.QSTileInput
-import com.android.systemui.qs.tiles.base.interactor.QSTileUserActionInteractor
+import com.android.systemui.qs.tiles.base.domain.actions.QSTileIntentUserInputHandler
+import com.android.systemui.qs.tiles.base.domain.interactor.QSTileUserActionInteractor
+import com.android.systemui.qs.tiles.base.domain.model.QSTileInput
+import com.android.systemui.qs.tiles.base.shared.model.QSTileUserAction
import com.android.systemui.qs.tiles.impl.notes.domain.model.NotesTileModel
-import com.android.systemui.qs.tiles.viewmodel.QSTileUserAction
import javax.inject.Inject
class NotesTileUserActionInteractor
-@Inject constructor(
+@Inject
+constructor(
private val qsTileIntentUserInputHandler: QSTileIntentUserInputHandler,
private val panelInteractor: PanelInteractor,
private val noteTaskController: NoteTaskController,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/notes/domain/NotesTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/notes/ui/mapper/NotesTileMapper.kt
index ecdd71170cda..36b28dfac7c7 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/notes/domain/NotesTileMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/notes/ui/mapper/NotesTileMapper.kt
@@ -14,15 +14,15 @@
* limitations under the License.
*/
-package com.android.systemui.qs.tiles.impl.notes.domain
+package com.android.systemui.qs.tiles.impl.notes.ui.mapper
import android.content.res.Resources
import android.widget.Button
import com.android.systemui.common.shared.model.Icon
-import com.android.systemui.qs.tiles.base.interactor.QSTileDataToStateMapper
+import com.android.systemui.qs.tiles.base.shared.model.QSTileConfig
+import com.android.systemui.qs.tiles.base.shared.model.QSTileState
+import com.android.systemui.qs.tiles.base.ui.model.QSTileDataToStateMapper
import com.android.systemui.qs.tiles.impl.notes.domain.model.NotesTileModel
-import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
-import com.android.systemui.qs.tiles.viewmodel.QSTileState
import com.android.systemui.res.R
import com.android.systemui.shade.ShadeDisplayAware
import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/onehanded/domain/OneHandedModeTileDataInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/onehanded/domain/OneHandedModeTileDataInteractor.kt
index 8c0fd2cd672a..39f39766fee6 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/onehanded/domain/OneHandedModeTileDataInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/onehanded/domain/OneHandedModeTileDataInteractor.kt
@@ -18,8 +18,8 @@ package com.android.systemui.qs.tiles.impl.onehanded.domain
import android.os.UserHandle
import com.android.systemui.accessibility.data.repository.OneHandedModeRepository
-import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
-import com.android.systemui.qs.tiles.base.interactor.QSTileDataInteractor
+import com.android.systemui.qs.tiles.base.domain.interactor.QSTileDataInteractor
+import com.android.systemui.qs.tiles.base.domain.model.DataUpdateTrigger
import com.android.systemui.qs.tiles.impl.onehanded.domain.model.OneHandedModeTileModel
import com.android.wm.shell.onehanded.OneHanded
import javax.inject.Inject
@@ -30,16 +30,16 @@ import kotlinx.coroutines.flow.map
/** Observes one handed mode state changes providing the [OneHandedModeTileModel]. */
class OneHandedModeTileDataInteractor
@Inject
-constructor(
- private val oneHandedModeRepository: OneHandedModeRepository,
-) : QSTileDataInteractor<OneHandedModeTileModel> {
+constructor(private val oneHandedModeRepository: OneHandedModeRepository) :
+ QSTileDataInteractor<OneHandedModeTileModel> {
override fun tileData(
user: UserHandle,
- triggers: Flow<DataUpdateTrigger>
+ triggers: Flow<DataUpdateTrigger>,
): Flow<OneHandedModeTileModel> {
return oneHandedModeRepository.isEnabled(user).map { OneHandedModeTileModel(it) }
}
+
override fun availability(user: UserHandle): Flow<Boolean> =
flowOf(OneHanded.sIsSupportOneHandedMode)
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/onehanded/domain/OneHandedModeTileUserActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/onehanded/domain/OneHandedModeTileUserActionInteractor.kt
index 0a0f0a668079..88f9d840012e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/onehanded/domain/OneHandedModeTileUserActionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/onehanded/domain/OneHandedModeTileUserActionInteractor.kt
@@ -19,11 +19,11 @@ package com.android.systemui.qs.tiles.impl.onehanded.domain
import android.content.Intent
import android.provider.Settings
import com.android.systemui.accessibility.data.repository.OneHandedModeRepository
-import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandler
-import com.android.systemui.qs.tiles.base.interactor.QSTileInput
-import com.android.systemui.qs.tiles.base.interactor.QSTileUserActionInteractor
+import com.android.systemui.qs.tiles.base.domain.actions.QSTileIntentUserInputHandler
+import com.android.systemui.qs.tiles.base.domain.interactor.QSTileUserActionInteractor
+import com.android.systemui.qs.tiles.base.domain.model.QSTileInput
+import com.android.systemui.qs.tiles.base.shared.model.QSTileUserAction
import com.android.systemui.qs.tiles.impl.onehanded.domain.model.OneHandedModeTileModel
-import com.android.systemui.qs.tiles.viewmodel.QSTileUserAction
import javax.inject.Inject
/** Handles one handed mode tile clicks. */
@@ -38,15 +38,12 @@ constructor(
with(input) {
when (action) {
is QSTileUserAction.Click -> {
- oneHandedModeRepository.setIsEnabled(
- !data.isEnabled,
- user,
- )
+ oneHandedModeRepository.setIsEnabled(!data.isEnabled, user)
}
is QSTileUserAction.LongClick -> {
qsTileIntentUserActionHandler.handle(
action.expandable,
- Intent(Settings.ACTION_ONE_HANDED_SETTINGS)
+ Intent(Settings.ACTION_ONE_HANDED_SETTINGS),
)
}
is QSTileUserAction.ToggleClick -> {}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/onehanded/ui/OneHandedModeTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/onehanded/ui/mapper/OneHandedModeTileMapper.kt
index 5b3ea93ab1ae..ace36ce586de 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/onehanded/ui/OneHandedModeTileMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/onehanded/ui/mapper/OneHandedModeTileMapper.kt
@@ -14,14 +14,14 @@
* limitations under the License.
*/
-package com.android.systemui.qs.tiles.impl.onehanded.ui
+package com.android.systemui.qs.tiles.impl.onehanded.ui.mapper
import android.content.res.Resources
import com.android.systemui.common.shared.model.Icon
-import com.android.systemui.qs.tiles.base.interactor.QSTileDataToStateMapper
+import com.android.systemui.qs.tiles.base.shared.model.QSTileConfig
+import com.android.systemui.qs.tiles.base.shared.model.QSTileState
+import com.android.systemui.qs.tiles.base.ui.model.QSTileDataToStateMapper
import com.android.systemui.qs.tiles.impl.onehanded.domain.model.OneHandedModeTileModel
-import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
-import com.android.systemui.qs.tiles.viewmodel.QSTileState
import com.android.systemui.res.R
import com.android.systemui.shade.ShadeDisplayAware
import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/qr/domain/interactor/QRCodeScannerTileDataInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/qr/domain/interactor/QRCodeScannerTileDataInteractor.kt
index 233e913a27aa..0b4752bcaac4 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/qr/domain/interactor/QRCodeScannerTileDataInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/qr/domain/interactor/QRCodeScannerTileDataInteractor.kt
@@ -21,8 +21,8 @@ import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.qrcodescanner.controller.QRCodeScannerController
import com.android.systemui.qrcodescanner.controller.QRCodeScannerController.DEFAULT_QR_CODE_SCANNER_CHANGE
-import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
-import com.android.systemui.qs.tiles.base.interactor.QSTileDataInteractor
+import com.android.systemui.qs.tiles.base.domain.interactor.QSTileDataInteractor
+import com.android.systemui.qs.tiles.base.domain.model.DataUpdateTrigger
import com.android.systemui.qs.tiles.impl.qr.domain.model.QRCodeScannerTileModel
import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
import javax.inject.Inject
@@ -44,7 +44,7 @@ constructor(
) : QSTileDataInteractor<QRCodeScannerTileModel> {
override fun tileData(
user: UserHandle,
- triggers: Flow<DataUpdateTrigger>
+ triggers: Flow<DataUpdateTrigger>,
): Flow<QRCodeScannerTileModel> =
conflatedCallbackFlow {
qrController.registerQRCodeScannerChangeObservers(DEFAULT_QR_CODE_SCANNER_CHANGE)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/qr/domain/interactor/QRCodeScannerTileUserActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/qr/domain/interactor/QRCodeScannerTileUserActionInteractor.kt
index bb5df022dd1b..eb47d0b8fe91 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/qr/domain/interactor/QRCodeScannerTileUserActionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/qr/domain/interactor/QRCodeScannerTileUserActionInteractor.kt
@@ -16,19 +16,18 @@
package com.android.systemui.qs.tiles.impl.qr.domain.interactor
-import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandler
-import com.android.systemui.qs.tiles.base.interactor.QSTileInput
-import com.android.systemui.qs.tiles.base.interactor.QSTileUserActionInteractor
+import com.android.systemui.qs.tiles.base.domain.actions.QSTileIntentUserInputHandler
+import com.android.systemui.qs.tiles.base.domain.interactor.QSTileUserActionInteractor
+import com.android.systemui.qs.tiles.base.domain.model.QSTileInput
+import com.android.systemui.qs.tiles.base.shared.model.QSTileUserAction
import com.android.systemui.qs.tiles.impl.qr.domain.model.QRCodeScannerTileModel
-import com.android.systemui.qs.tiles.viewmodel.QSTileUserAction
import javax.inject.Inject
/** Handles qr tile clicks. */
class QRCodeScannerTileUserActionInteractor
@Inject
-constructor(
- private val qsTileIntentUserActionHandler: QSTileIntentUserInputHandler,
-) : QSTileUserActionInteractor<QRCodeScannerTileModel> {
+constructor(private val qsTileIntentUserActionHandler: QSTileIntentUserInputHandler) :
+ QSTileUserActionInteractor<QRCodeScannerTileModel> {
override suspend fun handleInput(input: QSTileInput<QRCodeScannerTileModel>): Unit =
with(input) {
@@ -39,7 +38,7 @@ constructor(
qsTileIntentUserActionHandler.handle(
action.expandable,
data.intent,
- true
+ true,
)
is QRCodeScannerTileModel.TemporarilyUnavailable -> {} // no-op
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/qr/ui/QRCodeScannerTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/qr/ui/mapper/QRCodeScannerTileMapper.kt
index 21e92d3a1972..120961b76493 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/qr/ui/QRCodeScannerTileMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/qr/ui/mapper/QRCodeScannerTileMapper.kt
@@ -14,14 +14,14 @@
* limitations under the License.
*/
-package com.android.systemui.qs.tiles.impl.qr.ui
+package com.android.systemui.qs.tiles.impl.qr.ui.mapper
import android.content.res.Resources
import com.android.systemui.common.shared.model.Icon
-import com.android.systemui.qs.tiles.base.interactor.QSTileDataToStateMapper
+import com.android.systemui.qs.tiles.base.shared.model.QSTileConfig
+import com.android.systemui.qs.tiles.base.shared.model.QSTileState
+import com.android.systemui.qs.tiles.base.ui.model.QSTileDataToStateMapper
import com.android.systemui.qs.tiles.impl.qr.domain.model.QRCodeScannerTileModel
-import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
-import com.android.systemui.qs.tiles.viewmodel.QSTileState
import com.android.systemui.res.R
import com.android.systemui.shade.ShadeDisplayAware
import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/systemui/qrcodescanner/dagger/QRCodeScannerModule.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/qr/ui/model/QRCodeScannerModule.kt
index ef1f8341cb15..c3c6be3f7410 100644
--- a/packages/SystemUI/src/com/android/systemui/qrcodescanner/dagger/QRCodeScannerModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/qr/ui/model/QRCodeScannerModule.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2022 The Android Open Source Project
+ * Copyright (C) 2025 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.qrcodescanner.dagger
+package com.android.systemui.qs.tiles.impl.qr.ui.model
import com.android.systemui.Flags
import com.android.systemui.qs.QsEventLogger
@@ -22,16 +22,16 @@ import com.android.systemui.qs.pipeline.shared.TileSpec
import com.android.systemui.qs.shared.model.TileCategory
import com.android.systemui.qs.tileimpl.QSTileImpl
import com.android.systemui.qs.tiles.QRCodeScannerTile
-import com.android.systemui.qs.tiles.base.interactor.QSTileAvailabilityInteractor
-import com.android.systemui.qs.tiles.base.viewmodel.QSTileViewModelFactory
+import com.android.systemui.qs.tiles.base.domain.interactor.QSTileAvailabilityInteractor
+import com.android.systemui.qs.tiles.base.shared.model.QSTileConfig
+import com.android.systemui.qs.tiles.base.shared.model.QSTileUIConfig
+import com.android.systemui.qs.tiles.base.ui.viewmodel.QSTileViewModel
+import com.android.systemui.qs.tiles.base.ui.viewmodel.QSTileViewModelFactory
+import com.android.systemui.qs.tiles.base.ui.viewmodel.StubQSTileViewModel
import com.android.systemui.qs.tiles.impl.qr.domain.interactor.QRCodeScannerTileDataInteractor
import com.android.systemui.qs.tiles.impl.qr.domain.interactor.QRCodeScannerTileUserActionInteractor
import com.android.systemui.qs.tiles.impl.qr.domain.model.QRCodeScannerTileModel
-import com.android.systemui.qs.tiles.impl.qr.ui.QRCodeScannerTileMapper
-import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
-import com.android.systemui.qs.tiles.viewmodel.QSTileUIConfig
-import com.android.systemui.qs.tiles.viewmodel.QSTileViewModel
-import com.android.systemui.qs.tiles.viewmodel.StubQSTileViewModel
+import com.android.systemui.qs.tiles.impl.qr.ui.mapper.QRCodeScannerTileMapper
import com.android.systemui.res.R
import dagger.Binds
import dagger.Module
@@ -81,7 +81,7 @@ interface QRCodeScannerModule {
factory: QSTileViewModelFactory.Static<QRCodeScannerTileModel>,
mapper: QRCodeScannerTileMapper,
stateInteractor: QRCodeScannerTileDataInteractor,
- userActionInteractor: QRCodeScannerTileUserActionInteractor
+ userActionInteractor: QRCodeScannerTileUserActionInteractor,
): QSTileViewModel =
if (Flags.qsNewTilesFuture())
factory.create(
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/reducebrightness/domain/interactor/ReduceBrightColorsTileDataInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/reducebrightness/domain/interactor/ReduceBrightColorsTileDataInteractor.kt
index 536c5f1d17cf..46ed2ffd0996 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/reducebrightness/domain/interactor/ReduceBrightColorsTileDataInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/reducebrightness/domain/interactor/ReduceBrightColorsTileDataInteractor.kt
@@ -20,8 +20,8 @@ import android.os.UserHandle
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.qs.ReduceBrightColorsController
import com.android.systemui.qs.dagger.QSFlagsModule.RBC_AVAILABLE
-import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
-import com.android.systemui.qs.tiles.base.interactor.QSTileDataInteractor
+import com.android.systemui.qs.tiles.base.domain.interactor.QSTileDataInteractor
+import com.android.systemui.qs.tiles.base.domain.model.DataUpdateTrigger
import com.android.systemui.qs.tiles.impl.reducebrightness.domain.model.ReduceBrightColorsTileModel
import com.android.systemui.util.kotlin.isEnabled
import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/reducebrightness/domain/interactor/ReduceBrightColorsTileUserActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/reducebrightness/domain/interactor/ReduceBrightColorsTileUserActionInteractor.kt
index eff5f8f949c2..0bc3dec77853 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/reducebrightness/domain/interactor/ReduceBrightColorsTileUserActionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/reducebrightness/domain/interactor/ReduceBrightColorsTileUserActionInteractor.kt
@@ -21,11 +21,11 @@ import android.content.res.Resources
import android.provider.Settings
import com.android.systemui.accessibility.extradim.ExtraDimDialogManager
import com.android.systemui.qs.ReduceBrightColorsController
-import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandler
-import com.android.systemui.qs.tiles.base.interactor.QSTileInput
-import com.android.systemui.qs.tiles.base.interactor.QSTileUserActionInteractor
+import com.android.systemui.qs.tiles.base.domain.actions.QSTileIntentUserInputHandler
+import com.android.systemui.qs.tiles.base.domain.interactor.QSTileUserActionInteractor
+import com.android.systemui.qs.tiles.base.domain.model.QSTileInput
+import com.android.systemui.qs.tiles.base.shared.model.QSTileUserAction
import com.android.systemui.qs.tiles.impl.reducebrightness.domain.model.ReduceBrightColorsTileModel
-import com.android.systemui.qs.tiles.viewmodel.QSTileUserAction
import com.android.systemui.shade.ShadeDisplayAware
import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/reducebrightness/ui/ReduceBrightColorsTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/reducebrightness/ui/mapper/ReduceBrightColorsTileMapper.kt
index 66759cdfd1a6..2e74399c008f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/reducebrightness/ui/ReduceBrightColorsTileMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/reducebrightness/ui/mapper/ReduceBrightColorsTileMapper.kt
@@ -14,15 +14,15 @@
* limitations under the License.
*/
-package com.android.systemui.qs.tiles.impl.reducebrightness.ui
+package com.android.systemui.qs.tiles.impl.reducebrightness.ui.mapper
import android.content.res.Resources
import android.service.quicksettings.Tile
import com.android.systemui.common.shared.model.Icon
-import com.android.systemui.qs.tiles.base.interactor.QSTileDataToStateMapper
+import com.android.systemui.qs.tiles.base.shared.model.QSTileConfig
+import com.android.systemui.qs.tiles.base.shared.model.QSTileState
+import com.android.systemui.qs.tiles.base.ui.model.QSTileDataToStateMapper
import com.android.systemui.qs.tiles.impl.reducebrightness.domain.model.ReduceBrightColorsTileModel
-import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
-import com.android.systemui.qs.tiles.viewmodel.QSTileState
import com.android.systemui.res.R
import com.android.systemui.shade.ShadeDisplayAware
import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/rotation/domain/interactor/RotationLockTileDataInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/rotation/domain/interactor/RotationLockTileDataInteractor.kt
index 7f17a3a7481a..f89154d9428a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/rotation/domain/interactor/RotationLockTileDataInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/rotation/domain/interactor/RotationLockTileDataInteractor.kt
@@ -22,8 +22,8 @@ import android.content.res.Resources
import android.os.UserHandle
import com.android.systemui.camera.data.repository.CameraAutoRotateRepository
import com.android.systemui.camera.data.repository.CameraSensorPrivacyRepository
-import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
-import com.android.systemui.qs.tiles.base.interactor.QSTileDataInteractor
+import com.android.systemui.qs.tiles.base.domain.interactor.QSTileDataInteractor
+import com.android.systemui.qs.tiles.base.domain.model.DataUpdateTrigger
import com.android.systemui.qs.tiles.impl.rotation.domain.model.RotationLockTileModel
import com.android.systemui.shade.ShadeDisplayAware
import com.android.systemui.statusbar.policy.BatteryController
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/rotation/domain/interactor/RotationLockTileUserActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/rotation/domain/interactor/RotationLockTileUserActionInteractor.kt
index 65712c75516f..71aaa93db5ff 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/rotation/domain/interactor/RotationLockTileUserActionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/rotation/domain/interactor/RotationLockTileUserActionInteractor.kt
@@ -18,11 +18,11 @@ package com.android.systemui.qs.tiles.impl.rotation.domain.interactor
import android.content.Intent
import android.provider.Settings
-import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandler
-import com.android.systemui.qs.tiles.base.interactor.QSTileInput
-import com.android.systemui.qs.tiles.base.interactor.QSTileUserActionInteractor
+import com.android.systemui.qs.tiles.base.domain.actions.QSTileIntentUserInputHandler
+import com.android.systemui.qs.tiles.base.domain.interactor.QSTileUserActionInteractor
+import com.android.systemui.qs.tiles.base.domain.model.QSTileInput
+import com.android.systemui.qs.tiles.base.shared.model.QSTileUserAction
import com.android.systemui.qs.tiles.impl.rotation.domain.model.RotationLockTileModel
-import com.android.systemui.qs.tiles.viewmodel.QSTileUserAction
import com.android.systemui.statusbar.policy.RotationLockController
import javax.inject.Inject
@@ -43,7 +43,7 @@ constructor(
is QSTileUserAction.LongClick -> {
qsTileIntentUserActionHandler.handle(
action.expandable,
- Intent(Settings.ACTION_AUTO_ROTATE_SETTINGS)
+ Intent(Settings.ACTION_AUTO_ROTATE_SETTINGS),
)
}
is QSTileUserAction.ToggleClick -> {}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/rotation/ui/mapper/RotationLockTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/rotation/ui/mapper/RotationLockTileMapper.kt
index 000c7025e32b..d1db49929c0b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/rotation/ui/mapper/RotationLockTileMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/rotation/ui/mapper/RotationLockTileMapper.kt
@@ -19,10 +19,10 @@ package com.android.systemui.qs.tiles.impl.rotation.ui.mapper
import android.content.res.Resources
import android.hardware.devicestate.DeviceStateManager
import com.android.systemui.common.shared.model.Icon
-import com.android.systemui.qs.tiles.base.interactor.QSTileDataToStateMapper
+import com.android.systemui.qs.tiles.base.shared.model.QSTileConfig
+import com.android.systemui.qs.tiles.base.shared.model.QSTileState
+import com.android.systemui.qs.tiles.base.ui.model.QSTileDataToStateMapper
import com.android.systemui.qs.tiles.impl.rotation.domain.model.RotationLockTileModel
-import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
-import com.android.systemui.qs.tiles.viewmodel.QSTileState
import com.android.systemui.res.R
import com.android.systemui.shade.ShadeDisplayAware
import com.android.systemui.statusbar.policy.DevicePostureController
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/saver/domain/interactor/DataSaverTileDataInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/saver/domain/interactor/DataSaverTileDataInteractor.kt
index 91e049b68c06..1c19444a93f9 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/saver/domain/interactor/DataSaverTileDataInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/saver/domain/interactor/DataSaverTileDataInteractor.kt
@@ -18,8 +18,8 @@ package com.android.systemui.qs.tiles.impl.saver.domain.interactor
import android.os.UserHandle
import com.android.systemui.common.coroutine.ConflatedCallbackFlow
-import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
-import com.android.systemui.qs.tiles.base.interactor.QSTileDataInteractor
+import com.android.systemui.qs.tiles.base.domain.interactor.QSTileDataInteractor
+import com.android.systemui.qs.tiles.base.domain.model.DataUpdateTrigger
import com.android.systemui.qs.tiles.impl.saver.domain.model.DataSaverTileModel
import com.android.systemui.statusbar.policy.DataSaverController
import javax.inject.Inject
@@ -30,13 +30,12 @@ import kotlinx.coroutines.flow.flowOf
/** Observes data saver state changes providing the [DataSaverTileModel]. */
class DataSaverTileDataInteractor
@Inject
-constructor(
- private val dataSaverController: DataSaverController,
-) : QSTileDataInteractor<DataSaverTileModel> {
+constructor(private val dataSaverController: DataSaverController) :
+ QSTileDataInteractor<DataSaverTileModel> {
override fun tileData(
user: UserHandle,
- triggers: Flow<DataUpdateTrigger>
+ triggers: Flow<DataUpdateTrigger>,
): Flow<DataSaverTileModel> =
ConflatedCallbackFlow.conflatedCallbackFlow {
val initialValue = dataSaverController.isDataSaverEnabled
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/saver/domain/interactor/DataSaverTileUserActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/saver/domain/interactor/DataSaverTileUserActionInteractor.kt
index 63a9f594b05c..16fb9f7aeb72 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/saver/domain/interactor/DataSaverTileUserActionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/saver/domain/interactor/DataSaverTileUserActionInteractor.kt
@@ -24,12 +24,12 @@ import com.android.systemui.animation.DialogCuj
import com.android.systemui.animation.DialogTransitionAnimator
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
-import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandler
-import com.android.systemui.qs.tiles.base.interactor.QSTileInput
-import com.android.systemui.qs.tiles.base.interactor.QSTileUserActionInteractor
+import com.android.systemui.qs.tiles.base.domain.actions.QSTileIntentUserInputHandler
+import com.android.systemui.qs.tiles.base.domain.interactor.QSTileUserActionInteractor
+import com.android.systemui.qs.tiles.base.domain.model.QSTileInput
+import com.android.systemui.qs.tiles.base.shared.model.QSTileUserAction
import com.android.systemui.qs.tiles.impl.saver.domain.DataSaverDialogDelegate
import com.android.systemui.qs.tiles.impl.saver.domain.model.DataSaverTileModel
-import com.android.systemui.qs.tiles.viewmodel.QSTileUserAction
import com.android.systemui.settings.UserFileManager
import com.android.systemui.shade.ShadeDisplayAware
import com.android.systemui.shade.domain.interactor.ShadeDialogContextInteractor
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/saver/domain/DataSaverTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/saver/ui/mapper/DataSaverTileMapper.kt
index 1d5cf29f2462..e15c098451cb 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/saver/domain/DataSaverTileMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/saver/ui/mapper/DataSaverTileMapper.kt
@@ -14,14 +14,14 @@
* limitations under the License.
*/
-package com.android.systemui.qs.tiles.impl.saver.domain
+package com.android.systemui.qs.tiles.impl.saver.ui.mapper
import android.content.res.Resources
import com.android.systemui.common.shared.model.Icon
-import com.android.systemui.qs.tiles.base.interactor.QSTileDataToStateMapper
+import com.android.systemui.qs.tiles.base.shared.model.QSTileConfig
+import com.android.systemui.qs.tiles.base.shared.model.QSTileState
+import com.android.systemui.qs.tiles.base.ui.model.QSTileDataToStateMapper
import com.android.systemui.qs.tiles.impl.saver.domain.model.DataSaverTileModel
-import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
-import com.android.systemui.qs.tiles.viewmodel.QSTileState
import com.android.systemui.res.R
import com.android.systemui.shade.ShadeDisplayAware
import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/interactor/ScreenRecordTileDataInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/interactor/ScreenRecordTileDataInteractor.kt
index 597825c7611c..7e2f1bd52a52 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/interactor/ScreenRecordTileDataInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/interactor/ScreenRecordTileDataInteractor.kt
@@ -17,8 +17,8 @@
package com.android.systemui.qs.tiles.impl.screenrecord.domain.interactor
import android.os.UserHandle
-import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
-import com.android.systemui.qs.tiles.base.interactor.QSTileDataInteractor
+import com.android.systemui.qs.tiles.base.domain.interactor.QSTileDataInteractor
+import com.android.systemui.qs.tiles.base.domain.model.DataUpdateTrigger
import com.android.systemui.screenrecord.data.model.ScreenRecordModel
import com.android.systemui.screenrecord.data.repository.ScreenRecordRepository
import javax.inject.Inject
@@ -28,13 +28,12 @@ import kotlinx.coroutines.flow.flowOf
/** Observes screen record state changes providing the [ScreenRecordModel]. */
class ScreenRecordTileDataInteractor
@Inject
-constructor(
- private val screenRecordRepository: ScreenRecordRepository,
-) : QSTileDataInteractor<ScreenRecordModel> {
+constructor(private val screenRecordRepository: ScreenRecordRepository) :
+ QSTileDataInteractor<ScreenRecordModel> {
override fun tileData(
user: UserHandle,
- triggers: Flow<DataUpdateTrigger>
+ triggers: Flow<DataUpdateTrigger>,
): Flow<ScreenRecordModel> = screenRecordRepository.screenRecordState
override fun availability(user: UserHandle): Flow<Boolean> = flowOf(true)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/interactor/ScreenRecordTileUserActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/interactor/ScreenRecordTileUserActionInteractor.kt
index 94534479db57..b7dc63286454 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/interactor/ScreenRecordTileUserActionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/interactor/ScreenRecordTileUserActionInteractor.kt
@@ -28,9 +28,9 @@ import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.mediaprojection.MediaProjectionMetricsLogger
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.qs.pipeline.domain.interactor.PanelInteractor
-import com.android.systemui.qs.tiles.base.interactor.QSTileInput
-import com.android.systemui.qs.tiles.base.interactor.QSTileUserActionInteractor
-import com.android.systemui.qs.tiles.viewmodel.QSTileUserAction
+import com.android.systemui.qs.tiles.base.domain.interactor.QSTileUserActionInteractor
+import com.android.systemui.qs.tiles.base.domain.model.QSTileInput
+import com.android.systemui.qs.tiles.base.shared.model.QSTileUserAction
import com.android.systemui.screenrecord.RecordingController
import com.android.systemui.screenrecord.data.model.ScreenRecordModel
import com.android.systemui.screenrecord.data.repository.ScreenRecordRepository
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/ui/ScreenRecordTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/ui/mapper/ScreenRecordTileMapper.kt
index 0a61e3cbe616..b0c9d1224ce5 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/ui/ScreenRecordTileMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/ui/mapper/ScreenRecordTileMapper.kt
@@ -14,14 +14,14 @@
* limitations under the License.
*/
-package com.android.systemui.qs.tiles.impl.screenrecord.domain.ui
+package com.android.systemui.qs.tiles.impl.screenrecord.domain.ui.mapper
import android.content.res.Resources
import android.text.TextUtils
import com.android.systemui.common.shared.model.Icon
-import com.android.systemui.qs.tiles.base.interactor.QSTileDataToStateMapper
-import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
-import com.android.systemui.qs.tiles.viewmodel.QSTileState
+import com.android.systemui.qs.tiles.base.shared.model.QSTileConfig
+import com.android.systemui.qs.tiles.base.shared.model.QSTileState
+import com.android.systemui.qs.tiles.base.ui.model.QSTileDataToStateMapper
import com.android.systemui.res.R
import com.android.systemui.screenrecord.data.model.ScreenRecordModel
import com.android.systemui.shade.ShadeDisplayAware
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/sensorprivacy/SensorPrivacyToggleTileDataInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/sensorprivacy/domain/interactor/SensorPrivacyToggleTileDataInteractor.kt
index a8e9c5663f39..f033e6ac0be4 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/sensorprivacy/SensorPrivacyToggleTileDataInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/sensorprivacy/domain/interactor/SensorPrivacyToggleTileDataInteractor.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2024 The Android Open Source Project
+ * Copyright (C) 2025 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.qs.tiles.impl.sensorprivacy
+package com.android.systemui.qs.tiles.impl.sensorprivacy.domain.interactor
import android.hardware.SensorPrivacyManager.Sensors.CAMERA
import android.hardware.SensorPrivacyManager.Sensors.MICROPHONE
@@ -22,12 +22,12 @@ import android.hardware.SensorPrivacyManager.Sensors.Sensor
import android.os.UserHandle
import android.provider.DeviceConfig
import android.util.Log
-import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
import com.android.systemui.dagger.qualifiers.Background
-import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
-import com.android.systemui.qs.tiles.base.interactor.QSTileDataInteractor
+import com.android.systemui.qs.tiles.base.domain.interactor.QSTileDataInteractor
+import com.android.systemui.qs.tiles.base.domain.model.DataUpdateTrigger
import com.android.systemui.qs.tiles.impl.sensorprivacy.domain.model.SensorPrivacyToggleTileModel
import com.android.systemui.statusbar.policy.IndividualSensorPrivacyController
+import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
@@ -55,7 +55,7 @@ constructor(
override fun tileData(
user: UserHandle,
- triggers: Flow<DataUpdateTrigger>
+ triggers: Flow<DataUpdateTrigger>,
): Flow<SensorPrivacyToggleTileModel> =
conflatedCallbackFlow {
val callback =
@@ -85,14 +85,14 @@ constructor(
return@withContext DeviceConfig.getBoolean(
DeviceConfig.NAMESPACE_PRIVACY,
deviceConfigName,
- true
+ true,
)
} catch (exception: IllegalArgumentException) {
Log.w(
TAG,
"isDeviceConfigSet for sensorId $sensorId: " +
"Defaulting to true due to exception. ",
- exception
+ exception,
)
return@withContext true
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/sensorprivacy/domain/SensorPrivacyToggleTileUserActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/sensorprivacy/domain/interactor/SensorPrivacyToggleTileUserActionInteractor.kt
index d7f64d11a4fd..decf432169b0 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/sensorprivacy/domain/SensorPrivacyToggleTileUserActionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/sensorprivacy/domain/interactor/SensorPrivacyToggleTileUserActionInteractor.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2024 The Android Open Source Project
+ * Copyright (C) 2025 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.qs.tiles.impl.sensorprivacy.domain
+package com.android.systemui.qs.tiles.impl.sensorprivacy.domain.interactor
import android.content.Intent
import android.hardware.SensorPrivacyManager.Sensors.Sensor
@@ -23,11 +23,11 @@ import android.provider.Settings
import android.safetycenter.SafetyCenterManager
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.plugins.ActivityStarter
-import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandler
-import com.android.systemui.qs.tiles.base.interactor.QSTileInput
-import com.android.systemui.qs.tiles.base.interactor.QSTileUserActionInteractor
+import com.android.systemui.qs.tiles.base.domain.actions.QSTileIntentUserInputHandler
+import com.android.systemui.qs.tiles.base.domain.interactor.QSTileUserActionInteractor
+import com.android.systemui.qs.tiles.base.domain.model.QSTileInput
+import com.android.systemui.qs.tiles.base.shared.model.QSTileUserAction
import com.android.systemui.qs.tiles.impl.sensorprivacy.domain.model.SensorPrivacyToggleTileModel
-import com.android.systemui.qs.tiles.viewmodel.QSTileUserAction
import com.android.systemui.statusbar.policy.IndividualSensorPrivacyController
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/sensorprivacy/ui/SensorPrivacyToggleTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/sensorprivacy/ui/mapper/SensorPrivacyToggleTileMapper.kt
index f54f46c01dee..e9d5f528dc27 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/sensorprivacy/ui/SensorPrivacyToggleTileMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/sensorprivacy/ui/mapper/SensorPrivacyToggleTileMapper.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2024 The Android Open Source Project
+ * Copyright (C) 2025 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.
@@ -14,14 +14,15 @@
* limitations under the License.
*/
-package com.android.systemui.qs.tiles.impl.sensorprivacy.ui
+package com.android.systemui.qs.tiles.impl.sensorprivacy.ui.mapper
import android.content.res.Resources
import com.android.systemui.common.shared.model.Icon
-import com.android.systemui.qs.tiles.base.interactor.QSTileDataToStateMapper
+import com.android.systemui.qs.tiles.base.shared.model.QSTileConfig
+import com.android.systemui.qs.tiles.base.shared.model.QSTileState
+import com.android.systemui.qs.tiles.base.ui.model.QSTileDataToStateMapper
import com.android.systemui.qs.tiles.impl.sensorprivacy.domain.model.SensorPrivacyToggleTileModel
-import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
-import com.android.systemui.qs.tiles.viewmodel.QSTileState
+import com.android.systemui.qs.tiles.impl.sensorprivacy.ui.model.SensorPrivacyTileResources
import com.android.systemui.res.R
import com.android.systemui.shade.ShadeDisplayAware
import dagger.assisted.Assisted
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/sensorprivacy/ui/SensorPrivacyTileResources.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/sensorprivacy/ui/model/SensorPrivacyTileResources.kt
index 2a9fd07a67cb..e65ae00f6938 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/sensorprivacy/ui/SensorPrivacyTileResources.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/sensorprivacy/ui/model/SensorPrivacyTileResources.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2024 The Android Open Source Project
+ * Copyright (C) 2025 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.
@@ -14,12 +14,13 @@
* limitations under the License.
*/
-package com.android.systemui.qs.tiles.impl.sensorprivacy.ui
+package com.android.systemui.qs.tiles.impl.sensorprivacy.ui.model
import com.android.systemui.res.R
sealed interface SensorPrivacyTileResources {
fun getIconRes(isBlocked: Boolean): Int
+
fun getTileLabelRes(): Int
data object CameraPrivacyTileResources : SensorPrivacyTileResources {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/uimodenight/domain/interactor/UiModeNightTileDataInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/uimodenight/domain/interactor/UiModeNightTileDataInteractor.kt
index 925b91326a25..17c2f83b6f70 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/uimodenight/domain/interactor/UiModeNightTileDataInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/uimodenight/domain/interactor/UiModeNightTileDataInteractor.kt
@@ -21,9 +21,9 @@ import android.content.Context
import android.content.res.Configuration
import android.os.UserHandle
import com.android.systemui.common.coroutine.ConflatedCallbackFlow
-import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
-import com.android.systemui.qs.tiles.base.interactor.QSTileDataInteractor
-import com.android.systemui.qs.tiles.impl.uimodenight.domain.model.UiModeNightTileModel
+import com.android.systemui.qs.tiles.base.domain.interactor.QSTileDataInteractor
+import com.android.systemui.qs.tiles.base.domain.model.DataUpdateTrigger
+import com.android.systemui.qs.tiles.impl.uimodenight.domain.interactor.model.UiModeNightTileModel
import com.android.systemui.shade.ShadeDisplayAware
import com.android.systemui.statusbar.policy.BatteryController
import com.android.systemui.statusbar.policy.ConfigurationController
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/uimodenight/domain/interactor/UiModeNightTileUserActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/uimodenight/domain/interactor/UiModeNightTileUserActionInteractor.kt
index 889782831b70..8af5fedff46a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/uimodenight/domain/interactor/UiModeNightTileUserActionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/uimodenight/domain/interactor/UiModeNightTileUserActionInteractor.kt
@@ -20,11 +20,11 @@ import android.app.UiModeManager
import android.content.Intent
import android.provider.Settings
import com.android.systemui.dagger.qualifiers.Background
-import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandler
-import com.android.systemui.qs.tiles.base.interactor.QSTileInput
-import com.android.systemui.qs.tiles.base.interactor.QSTileUserActionInteractor
-import com.android.systemui.qs.tiles.impl.uimodenight.domain.model.UiModeNightTileModel
-import com.android.systemui.qs.tiles.viewmodel.QSTileUserAction
+import com.android.systemui.qs.tiles.base.domain.actions.QSTileIntentUserInputHandler
+import com.android.systemui.qs.tiles.base.domain.interactor.QSTileUserActionInteractor
+import com.android.systemui.qs.tiles.base.domain.model.QSTileInput
+import com.android.systemui.qs.tiles.base.shared.model.QSTileUserAction
+import com.android.systemui.qs.tiles.impl.uimodenight.domain.interactor.model.UiModeNightTileModel
import javax.inject.Inject
import kotlin.coroutines.CoroutineContext
import kotlinx.coroutines.withContext
@@ -51,7 +51,7 @@ constructor(
is QSTileUserAction.LongClick -> {
qsTileIntentUserActionHandler.handle(
action.expandable,
- Intent(Settings.ACTION_DARK_THEME_SETTINGS)
+ Intent(Settings.ACTION_DARK_THEME_SETTINGS),
)
}
is QSTileUserAction.ToggleClick -> {}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/uimodenight/domain/model/UiModeNightTileModel.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/uimodenight/domain/interactor/model/UiModeNightTileModel.kt
index 4fa1306d988d..edb899be5c57 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/uimodenight/domain/model/UiModeNightTileModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/uimodenight/domain/interactor/model/UiModeNightTileModel.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.qs.tiles.impl.uimodenight.domain.model
+package com.android.systemui.qs.tiles.impl.uimodenight.domain.interactor.model
import java.time.LocalTime
@@ -31,5 +31,5 @@ data class UiModeNightTileModel(
val nightModeCustomType: Int,
val is24HourFormat: Boolean,
val customNightModeEnd: LocalTime,
- val customNightModeStart: LocalTime
+ val customNightModeStart: LocalTime,
)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/uimodenight/domain/UiModeNightTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/uimodenight/ui/mapper/UiModeNightTileMapper.kt
index 5933d65bc61f..91c045101182 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/uimodenight/domain/UiModeNightTileMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/uimodenight/ui/mapper/UiModeNightTileMapper.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2025 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.
@@ -14,17 +14,17 @@
* limitations under the License.
*/
-package com.android.systemui.qs.tiles.impl.uimodenight.domain
+package com.android.systemui.qs.tiles.impl.uimodenight.ui.mapper
import android.app.UiModeManager
import android.content.res.Resources
import android.content.res.Resources.Theme
import android.text.TextUtils
import com.android.systemui.common.shared.model.Icon
-import com.android.systemui.qs.tiles.base.interactor.QSTileDataToStateMapper
-import com.android.systemui.qs.tiles.impl.uimodenight.domain.model.UiModeNightTileModel
-import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
-import com.android.systemui.qs.tiles.viewmodel.QSTileState
+import com.android.systemui.qs.tiles.base.shared.model.QSTileConfig
+import com.android.systemui.qs.tiles.base.shared.model.QSTileState
+import com.android.systemui.qs.tiles.base.ui.model.QSTileDataToStateMapper
+import com.android.systemui.qs.tiles.impl.uimodenight.domain.interactor.model.UiModeNightTileModel
import com.android.systemui.res.R
import com.android.systemui.shade.ShadeDisplayAware
import java.time.LocalTime
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/work/domain/interactor/WorkModeTileDataInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/work/domain/interactor/WorkModeTileDataInteractor.kt
index a2a9e87a5981..eabeb5d348f7 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/work/domain/interactor/WorkModeTileDataInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/work/domain/interactor/WorkModeTileDataInteractor.kt
@@ -17,8 +17,8 @@
package com.android.systemui.qs.tiles.impl.work.domain.interactor
import android.os.UserHandle
-import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
-import com.android.systemui.qs.tiles.base.interactor.QSTileDataInteractor
+import com.android.systemui.qs.tiles.base.domain.interactor.QSTileDataInteractor
+import com.android.systemui.qs.tiles.base.domain.model.DataUpdateTrigger
import com.android.systemui.qs.tiles.impl.work.domain.model.WorkModeTileModel
import com.android.systemui.statusbar.phone.ManagedProfileController
import com.android.systemui.util.kotlin.hasActiveWorkProfile
@@ -29,12 +29,11 @@ import kotlinx.coroutines.flow.map
/** Observes data saver state changes providing the [WorkModeTileModel]. */
class WorkModeTileDataInteractor
@Inject
-constructor(
- private val profileController: ManagedProfileController,
-) : QSTileDataInteractor<WorkModeTileModel> {
+constructor(private val profileController: ManagedProfileController) :
+ QSTileDataInteractor<WorkModeTileModel> {
override fun tileData(
user: UserHandle,
- triggers: Flow<DataUpdateTrigger>
+ triggers: Flow<DataUpdateTrigger>,
): Flow<WorkModeTileModel> =
profileController.hasActiveWorkProfile.map { hasActiveWorkProfile: Boolean ->
if (hasActiveWorkProfile) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/work/domain/interactor/WorkModeTileUserActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/work/domain/interactor/WorkModeTileUserActionInteractor.kt
index 45ae09eaef92..42cc996fc722 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/work/domain/interactor/WorkModeTileUserActionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/work/domain/interactor/WorkModeTileUserActionInteractor.kt
@@ -18,11 +18,11 @@ package com.android.systemui.qs.tiles.impl.work.domain.interactor
import android.content.Intent
import android.provider.Settings
-import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandler
-import com.android.systemui.qs.tiles.base.interactor.QSTileInput
-import com.android.systemui.qs.tiles.base.interactor.QSTileUserActionInteractor
+import com.android.systemui.qs.tiles.base.domain.actions.QSTileIntentUserInputHandler
+import com.android.systemui.qs.tiles.base.domain.interactor.QSTileUserActionInteractor
+import com.android.systemui.qs.tiles.base.domain.model.QSTileInput
+import com.android.systemui.qs.tiles.base.shared.model.QSTileUserAction
import com.android.systemui.qs.tiles.impl.work.domain.model.WorkModeTileModel
-import com.android.systemui.qs.tiles.viewmodel.QSTileUserAction
import com.android.systemui.statusbar.phone.ManagedProfileController
import javax.inject.Inject
@@ -45,7 +45,7 @@ constructor(
if (data is WorkModeTileModel.HasActiveProfile) {
qsTileIntentUserActionHandler.handle(
action.expandable,
- Intent(Settings.ACTION_MANAGED_PROFILE_SETTINGS)
+ Intent(Settings.ACTION_MANAGED_PROFILE_SETTINGS),
)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/work/ui/WorkModeTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/work/ui/mapper/WorkModeTileMapper.kt
index 5b462ba074ec..a67c76d4fb78 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/work/ui/WorkModeTileMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/work/ui/mapper/WorkModeTileMapper.kt
@@ -14,17 +14,17 @@
* limitations under the License.
*/
-package com.android.systemui.qs.tiles.impl.work.ui
+package com.android.systemui.qs.tiles.impl.work.ui.mapper
import android.app.admin.DevicePolicyManager
import android.app.admin.DevicePolicyResources.Strings.SystemUi.QS_WORK_PROFILE_LABEL
import android.content.res.Resources
import android.service.quicksettings.Tile
import com.android.systemui.common.shared.model.Icon
-import com.android.systemui.qs.tiles.base.interactor.QSTileDataToStateMapper
+import com.android.systemui.qs.tiles.base.shared.model.QSTileConfig
+import com.android.systemui.qs.tiles.base.shared.model.QSTileState
+import com.android.systemui.qs.tiles.base.ui.model.QSTileDataToStateMapper
import com.android.systemui.qs.tiles.impl.work.domain.model.WorkModeTileModel
-import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
-import com.android.systemui.qs.tiles.viewmodel.QSTileState
import com.android.systemui.res.R
import com.android.systemui.shade.ShadeDisplayAware
import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsContainerViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsContainerViewModel.kt
index f9b1a36621b2..11622be3d417 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsContainerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsContainerViewModel.kt
@@ -77,7 +77,7 @@ constructor(
val showMedia: Boolean by
hydrator.hydratedStateOf(
traceName = "showMedia",
- source = mediaCarouselInteractor.hasActiveMediaOrRecommendation,
+ source = mediaCarouselInteractor.hasAnyMediaOrRecommendation,
)
override suspend fun onActivated(): Nothing {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayActionsViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayActionsViewModel.kt
index 52c4e2fac6d5..2f7bc0992411 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayActionsViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeOverlayActionsViewModel.kt
@@ -21,8 +21,7 @@ import com.android.compose.animation.scene.Swipe
import com.android.compose.animation.scene.UserAction
import com.android.compose.animation.scene.UserActionResult
import com.android.compose.animation.scene.UserActionResult.HideOverlay
-import com.android.compose.animation.scene.UserActionResult.ShowOverlay
-import com.android.compose.animation.scene.UserActionResult.ShowOverlay.HideCurrentOverlays
+import com.android.compose.animation.scene.UserActionResult.ReplaceByOverlay
import com.android.systemui.qs.panels.ui.viewmodel.EditModeViewModel
import com.android.systemui.scene.shared.model.Overlays
import com.android.systemui.scene.ui.viewmodel.SceneContainerArea
@@ -47,12 +46,8 @@ constructor(private val editModeViewModel: EditModeViewModel) : UserActionsViewM
put(Back, HideOverlay(Overlays.QuickSettingsShade))
}
put(
- Swipe.Down(fromSource = SceneContainerArea.StartHalf),
- ShowOverlay(
- Overlays.NotificationsShade,
- hideCurrentOverlays =
- HideCurrentOverlays.Some(Overlays.QuickSettingsShade),
- ),
+ Swipe.Down(fromSource = SceneContainerArea.TopEdgeStartHalf),
+ ReplaceByOverlay(Overlays.NotificationsShade),
)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/reardisplay/RearDisplayCoreStartable.kt b/packages/SystemUI/src/com/android/systemui/reardisplay/RearDisplayCoreStartable.kt
index 263ef09ea767..5d4a774d77f9 100644
--- a/packages/SystemUI/src/com/android/systemui/reardisplay/RearDisplayCoreStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/reardisplay/RearDisplayCoreStartable.kt
@@ -19,14 +19,18 @@ package com.android.systemui.reardisplay
import android.content.Context
import android.hardware.devicestate.DeviceStateManager
import android.hardware.devicestate.feature.flags.Flags
+import android.os.Handler
+import android.view.accessibility.AccessibilityManager
import androidx.annotation.VisibleForTesting
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.keyguard.KeyguardUpdateMonitorCallback
import com.android.systemui.CoreStartable
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.display.domain.interactor.RearDisplayStateInteractor
import com.android.systemui.statusbar.phone.SystemUIDialog
+import java.util.concurrent.atomic.AtomicBoolean
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
@@ -52,6 +56,8 @@ internal constructor(
private val rearDisplayInnerDialogDelegateFactory: RearDisplayInnerDialogDelegate.Factory,
@Application private val scope: CoroutineScope,
private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
+ private val accessibilityManager: AccessibilityManager,
+ @Background private val handler: Handler,
) : CoreStartable, AutoCloseable {
companion object {
@@ -77,6 +83,12 @@ internal constructor(
override fun start() {
if (Flags.deviceStateRdmV2()) {
var dialog: SystemUIDialog? = null
+ var touchExplorationEnabled = AtomicBoolean(false)
+
+ accessibilityManager.addTouchExplorationStateChangeListener(
+ { enabled -> touchExplorationEnabled.set(enabled) },
+ handler,
+ )
keyguardUpdateMonitor.registerCallback(keyguardCallback)
@@ -99,6 +111,7 @@ internal constructor(
rearDisplayInnerDialogDelegateFactory.create(
rearDisplayContext,
deviceStateManager::cancelStateRequest,
+ touchExplorationEnabled.get(),
)
dialog = delegate.createDialog().apply { show() }
}
diff --git a/packages/SystemUI/src/com/android/systemui/reardisplay/RearDisplayInnerDialogDelegate.kt b/packages/SystemUI/src/com/android/systemui/reardisplay/RearDisplayInnerDialogDelegate.kt
index f5facf42ee67..96f1bd270239 100644
--- a/packages/SystemUI/src/com/android/systemui/reardisplay/RearDisplayInnerDialogDelegate.kt
+++ b/packages/SystemUI/src/com/android/systemui/reardisplay/RearDisplayInnerDialogDelegate.kt
@@ -20,7 +20,10 @@ import android.annotation.SuppressLint
import android.content.Context
import android.os.Bundle
import android.view.MotionEvent
+import android.view.View
+import android.widget.Button
import android.widget.SeekBar
+import android.widget.TextView
import com.android.systemui.haptics.slider.HapticSlider
import com.android.systemui.haptics.slider.HapticSliderPlugin
import com.android.systemui.haptics.slider.HapticSliderViewBinder
@@ -45,6 +48,7 @@ class RearDisplayInnerDialogDelegate
internal constructor(
private val systemUIDialogFactory: SystemUIDialog.Factory,
@Assisted private val rearDisplayContext: Context,
+ @Assisted private val touchExplorationEnabled: Boolean,
private val vibratorHelper: VibratorHelper,
private val msdlPlayer: MSDLPlayer,
private val systemClock: SystemClock,
@@ -82,6 +86,7 @@ internal constructor(
fun create(
rearDisplayContext: Context,
onCanceledRunnable: Runnable,
+ touchExplorationEnabled: Boolean,
): RearDisplayInnerDialogDelegate
}
@@ -95,11 +100,32 @@ internal constructor(
@SuppressLint("ClickableViewAccessibility")
override fun onCreate(dialog: SystemUIDialog, savedInstanceState: Bundle?) {
+
dialog.apply {
setContentView(R.layout.activity_rear_display_enabled)
setCanceledOnTouchOutside(false)
+ requireViewById<Button>(R.id.cancel_button).let { it ->
+ if (!touchExplorationEnabled) {
+ return@let
+ }
+
+ it.visibility = View.VISIBLE
+ it.setOnClickListener { onCanceledRunnable.run() }
+ }
+
+ requireViewById<TextView>(R.id.seekbar_instructions).let { it ->
+ if (touchExplorationEnabled) {
+ it.visibility = View.GONE
+ }
+ }
+
requireViewById<SeekBar>(R.id.seekbar).let { it ->
+ if (touchExplorationEnabled) {
+ it.visibility = View.GONE
+ return@let
+ }
+
// Create and bind the HapticSliderPlugin
val hapticSliderPlugin =
HapticSliderPlugin(
diff --git a/packages/SystemUI/src/com/android/systemui/recents/LauncherProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/LauncherProxyService.java
index d2639654d206..5b9717535ab5 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/LauncherProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/LauncherProxyService.java
@@ -78,6 +78,7 @@ import android.view.inputmethod.InputMethodManager;
import androidx.annotation.NonNull;
+import com.android.app.displaylib.PerDisplayRepository;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.AssistUtils;
import com.android.internal.app.IVoiceInteractionSessionListener;
@@ -90,7 +91,6 @@ import com.android.systemui.contextualeducation.GestureType;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.display.data.repository.DisplayRepository;
-import com.android.systemui.display.data.repository.PerDisplayRepository;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
import com.android.systemui.keyguard.KeyguardWmStateRefactor;
@@ -126,8 +126,6 @@ import com.android.wm.shell.back.BackAnimation;
import com.android.wm.shell.shared.desktopmode.DesktopModeStatus;
import com.android.wm.shell.sysui.ShellInterface;
-import dagger.Lazy;
-
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
@@ -139,6 +137,8 @@ import java.util.function.Supplier;
import javax.inject.Inject;
import javax.inject.Provider;
+import dagger.Lazy;
+
/**
* Class to send information from SysUI to Launcher with a binder.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java b/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java
index 432a35a1a3dd..a1281ec23f92 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java
@@ -16,9 +16,7 @@
package com.android.systemui.recents;
-import static com.android.systemui.Flags.enableViewCaptureTracing;
import static com.android.systemui.shared.recents.utilities.Utilities.isLargeScreen;
-import static com.android.systemui.util.ConvenienceExtensionsKt.toKotlinLazy;
import static com.android.systemui.util.leak.RotationUtils.ROTATION_LANDSCAPE;
import static com.android.systemui.util.leak.RotationUtils.ROTATION_NONE;
import static com.android.systemui.util.leak.RotationUtils.ROTATION_SEASCAPE;
@@ -55,8 +53,6 @@ import android.widget.TextView;
import androidx.annotation.NonNull;
-import com.android.app.viewcapture.ViewCapture;
-import com.android.app.viewcapture.ViewCaptureAwareWindowManager;
import com.android.systemui.CoreStartable;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dagger.SysUISingleton;
@@ -87,7 +83,6 @@ public class ScreenPinningRequest implements
private final Lazy<NavigationBarController> mNavigationBarControllerLazy;
private final AccessibilityManager mAccessibilityService;
private final WindowManager mWindowManager;
- private final ViewCaptureAwareWindowManager mViewCaptureAwareWindowManager;
private final BroadcastDispatcher mBroadcastDispatcher;
private final UserTracker mUserTracker;
@@ -112,15 +107,12 @@ public class ScreenPinningRequest implements
Lazy<NavigationBarController> navigationBarControllerLazy,
BroadcastDispatcher broadcastDispatcher,
UserTracker userTracker,
- Lazy<ViewCapture> daggerLazyViewCapture) {
+ WindowManager windowManager) {
mContext = context;
mNavigationBarControllerLazy = navigationBarControllerLazy;
mAccessibilityService = (AccessibilityManager)
mContext.getSystemService(Context.ACCESSIBILITY_SERVICE);
- mWindowManager = (WindowManager)
- mContext.getSystemService(Context.WINDOW_SERVICE);
- mViewCaptureAwareWindowManager = new ViewCaptureAwareWindowManager(mWindowManager,
- toKotlinLazy(daggerLazyViewCapture), enableViewCaptureTracing());
+ mWindowManager = windowManager;
mNavBarMode = navigationModeController.addListener(this);
mBroadcastDispatcher = broadcastDispatcher;
mUserTracker = userTracker;
@@ -131,7 +123,7 @@ public class ScreenPinningRequest implements
public void clearPrompt() {
if (mRequestWindow != null) {
- mViewCaptureAwareWindowManager.removeView(mRequestWindow);
+ mWindowManager.removeView(mRequestWindow);
mRequestWindow = null;
}
}
@@ -152,7 +144,7 @@ public class ScreenPinningRequest implements
// show the confirmation
WindowManager.LayoutParams lp = getWindowLayoutParams();
- mViewCaptureAwareWindowManager.addView(mRequestWindow, lp);
+ mWindowManager.addView(mRequestWindow, lp);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/recordissue/RecordIssueModule.kt b/packages/SystemUI/src/com/android/systemui/recordissue/RecordIssueModule.kt
index c092c2f86799..9023c62347d2 100644
--- a/packages/SystemUI/src/com/android/systemui/recordissue/RecordIssueModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/recordissue/RecordIssueModule.kt
@@ -22,15 +22,15 @@ import com.android.systemui.qs.pipeline.shared.TileSpec
import com.android.systemui.qs.shared.model.TileCategory
import com.android.systemui.qs.tileimpl.QSTileImpl
import com.android.systemui.qs.tiles.RecordIssueTile
-import com.android.systemui.qs.tiles.base.viewmodel.QSTileViewModelFactory
-import com.android.systemui.qs.tiles.impl.irecording.IssueRecordingDataInteractor
-import com.android.systemui.qs.tiles.impl.irecording.IssueRecordingMapper
-import com.android.systemui.qs.tiles.impl.irecording.IssueRecordingModel
-import com.android.systemui.qs.tiles.impl.irecording.IssueRecordingUserActionInteractor
-import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
-import com.android.systemui.qs.tiles.viewmodel.QSTileUIConfig
-import com.android.systemui.qs.tiles.viewmodel.QSTileViewModel
-import com.android.systemui.qs.tiles.viewmodel.StubQSTileViewModel
+import com.android.systemui.qs.tiles.base.shared.model.QSTileConfig
+import com.android.systemui.qs.tiles.base.shared.model.QSTileUIConfig
+import com.android.systemui.qs.tiles.base.ui.viewmodel.QSTileViewModel
+import com.android.systemui.qs.tiles.base.ui.viewmodel.QSTileViewModelFactory
+import com.android.systemui.qs.tiles.base.ui.viewmodel.StubQSTileViewModel
+import com.android.systemui.qs.tiles.impl.irecording.data.model.IssueRecordingModel
+import com.android.systemui.qs.tiles.impl.irecording.domain.interactor.IssueRecordingDataInteractor
+import com.android.systemui.qs.tiles.impl.irecording.domain.interactor.IssueRecordingUserActionInteractor
+import com.android.systemui.qs.tiles.impl.irecording.ui.mapper.IssueRecordingMapper
import com.android.systemui.res.R
import dagger.Binds
import dagger.Module
@@ -59,7 +59,7 @@ interface RecordIssueModule {
uiConfig =
QSTileUIConfig.Resource(
iconRes = R.drawable.qs_record_issue_icon_off,
- labelRes = R.string.qs_record_issue_label
+ labelRes = R.string.qs_record_issue_label,
),
instanceId = uiEventLogger.getNewInstanceId(),
category = TileCategory.UTILITIES,
@@ -73,7 +73,7 @@ interface RecordIssueModule {
factory: QSTileViewModelFactory.Static<IssueRecordingModel>,
mapper: IssueRecordingMapper,
stateInteractor: IssueRecordingDataInteractor,
- userActionInteractor: IssueRecordingUserActionInteractor
+ userActionInteractor: IssueRecordingUserActionInteractor,
): QSTileViewModel =
if (Flags.qsNewTilesFuture())
factory.create(
diff --git a/packages/SystemUI/src/com/android/systemui/rotationlock/RotationLockNewModule.kt b/packages/SystemUI/src/com/android/systemui/rotationlock/RotationLockNewModule.kt
index c9712fcdaa27..ba5b4ff59971 100644
--- a/packages/SystemUI/src/com/android/systemui/rotationlock/RotationLockNewModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/rotationlock/RotationLockNewModule.kt
@@ -20,15 +20,15 @@ import com.android.systemui.camera.CameraRotationModule
import com.android.systemui.qs.QsEventLogger
import com.android.systemui.qs.pipeline.shared.TileSpec
import com.android.systemui.qs.shared.model.TileCategory
-import com.android.systemui.qs.tiles.base.interactor.QSTileAvailabilityInteractor
-import com.android.systemui.qs.tiles.base.viewmodel.QSTileViewModelFactory
+import com.android.systemui.qs.tiles.base.domain.interactor.QSTileAvailabilityInteractor
+import com.android.systemui.qs.tiles.base.shared.model.QSTileConfig
+import com.android.systemui.qs.tiles.base.shared.model.QSTileUIConfig
+import com.android.systemui.qs.tiles.base.ui.viewmodel.QSTileViewModel
+import com.android.systemui.qs.tiles.base.ui.viewmodel.QSTileViewModelFactory
import com.android.systemui.qs.tiles.impl.rotation.domain.interactor.RotationLockTileDataInteractor
import com.android.systemui.qs.tiles.impl.rotation.domain.interactor.RotationLockTileUserActionInteractor
import com.android.systemui.qs.tiles.impl.rotation.domain.model.RotationLockTileModel
import com.android.systemui.qs.tiles.impl.rotation.ui.mapper.RotationLockTileMapper
-import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
-import com.android.systemui.qs.tiles.viewmodel.QSTileUIConfig
-import com.android.systemui.qs.tiles.viewmodel.QSTileViewModel
import com.android.systemui.res.R
import dagger.Binds
import dagger.Module
@@ -73,7 +73,7 @@ interface RotationLockNewModule {
factory: QSTileViewModelFactory.Static<RotationLockTileModel>,
mapper: RotationLockTileMapper,
stateInteractor: RotationLockTileDataInteractor,
- userActionInteractor: RotationLockTileUserActionInteractor
+ userActionInteractor: RotationLockTileUserActionInteractor,
): QSTileViewModel =
factory.create(
TileSpec.create(ROTATION_TILE_SPEC),
diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt
index 3ad0867192d3..06fc8610c97b 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt
@@ -33,10 +33,8 @@ import com.android.systemui.bouncer.domain.interactor.SimBouncerInteractor
import com.android.systemui.bouncer.shared.logging.BouncerUiEvent
import com.android.systemui.classifier.FalsingCollector
import com.android.systemui.classifier.FalsingCollectorActual
-import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.dagger.qualifiers.DisplayId
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryHapticsInteractor
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor
@@ -82,6 +80,7 @@ import com.android.systemui.util.kotlin.pairwise
import com.android.systemui.util.kotlin.sample
import com.android.systemui.util.printSection
import com.android.systemui.util.println
+import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
import com.google.android.msdl.data.model.MSDLToken
import com.google.android.msdl.domain.MSDLPlayer
import dagger.Lazy
@@ -123,7 +122,6 @@ constructor(
private val bouncerInteractor: BouncerInteractor,
private val keyguardInteractor: KeyguardInteractor,
private val sysUiState: SysUiState,
- @DisplayId private val displayId: Int,
private val sceneLogger: SceneLogger,
@FalsingCollectorActual private val falsingCollector: FalsingCollector,
private val falsingManager: FalsingManager,
@@ -197,7 +195,8 @@ constructor(
return
}
- printSection("Scene state") {
+ printSection("Framework state") {
+ println("isVisible", sceneInteractor.isVisible.value)
println("currentScene", sceneInteractor.currentScene.value.debugName)
println(
"currentOverlays",
@@ -732,21 +731,26 @@ constructor(
sceneInteractor.transitionState
.mapNotNull { it as? ObservableTransitionState.Idle }
.distinctUntilChanged(),
+ sceneInteractor.isVisible,
occlusionInteractor.invisibleDueToOcclusion,
- ) { idleState, invisibleDueToOcclusion ->
+ ) { idleState, isVisible, invisibleDueToOcclusion ->
SceneContainerPlugin.SceneContainerPluginState(
scene = idleState.currentScene,
overlays = idleState.currentOverlays,
+ isVisible = isVisible,
invisibleDueToOcclusion = invisibleDueToOcclusion,
)
}
- .collect { sceneContainerPluginState ->
+ .map { sceneContainerPluginState ->
+ SceneContainerPlugin.EvaluatorByFlag.map { (flag, evaluator) ->
+ flag to evaluator(sceneContainerPluginState)
+ }
+ .toMap()
+ }
+ .distinctUntilChanged()
+ .collect { flags ->
sysUiState.updateFlags(
- displayId,
- *SceneContainerPlugin.EvaluatorByFlag.map { (flag, evaluator) ->
- flag to evaluator.invoke(sceneContainerPluginState)
- }
- .toTypedArray(),
+ *(flags.entries.map { (key, value) -> key to value }).toTypedArray()
)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerSwipeDetector.kt b/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerSwipeDetector.kt
index ede453dbe6b3..760b97e6245b 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerSwipeDetector.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerSwipeDetector.kt
@@ -58,6 +58,22 @@ sealed class SceneContainerArea(private val resolveArea: (LayoutDirection) -> Re
}
)
+ data object TopEdgeStartHalf :
+ SceneContainerArea(
+ resolveArea = {
+ if (it == LayoutDirection.Ltr) Resolved.TopEdgeLeftHalf
+ else Resolved.TopEdgeRightHalf
+ }
+ )
+
+ data object TopEdgeEndHalf :
+ SceneContainerArea(
+ resolveArea = {
+ if (it == LayoutDirection.Ltr) Resolved.TopEdgeRightHalf
+ else Resolved.TopEdgeLeftHalf
+ }
+ )
+
override fun resolve(layoutDirection: LayoutDirection): Resolved {
return resolveArea(layoutDirection)
}
@@ -72,6 +88,12 @@ sealed class SceneContainerArea(private val resolveArea: (LayoutDirection) -> Re
data object RightEdge : Resolved
data object RightHalf : Resolved
+
+ /** The left half of the top edge of the display. */
+ data object TopEdgeLeftHalf : Resolved
+
+ /** The right half of the top edge of the display. */
+ data object TopEdgeRightHalf : Resolved
}
}
@@ -108,9 +130,14 @@ class SceneContainerSwipeDetector(val edgeSize: Dp) : SwipeSourceDetector {
Edge.Resolved.Left -> SceneContainerArea.Resolved.LeftEdge
Edge.Resolved.Bottom -> SceneContainerArea.Resolved.BottomEdge
Edge.Resolved.Right -> SceneContainerArea.Resolved.RightEdge
- else -> {
- // Note: This intentionally includes Edge.Resolved.Top. At the moment, we don't need
- // to detect swipes on the top edge, and consider them part of the right/left half.
+ Edge.Resolved.Top -> {
+ if (position.x < layoutSize.width * 0.5f) {
+ SceneContainerArea.Resolved.TopEdgeLeftHalf
+ } else {
+ SceneContainerArea.Resolved.TopEdgeRightHalf
+ }
+ }
+ null -> {
if (position.x < layoutSize.width * 0.5f) {
SceneContainerArea.Resolved.LeftHalf
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordModule.kt b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordModule.kt
index 9a9c576c5af6..b4cc05505f23 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordModule.kt
@@ -24,14 +24,14 @@ import com.android.systemui.qs.pipeline.shared.TileSpec
import com.android.systemui.qs.shared.model.TileCategory
import com.android.systemui.qs.tileimpl.QSTileImpl
import com.android.systemui.qs.tiles.ScreenRecordTile
-import com.android.systemui.qs.tiles.base.interactor.QSTileAvailabilityInteractor
-import com.android.systemui.qs.tiles.base.viewmodel.QSTileViewModelFactory
+import com.android.systemui.qs.tiles.base.domain.interactor.QSTileAvailabilityInteractor
+import com.android.systemui.qs.tiles.base.shared.model.QSTileConfig
+import com.android.systemui.qs.tiles.base.shared.model.QSTileUIConfig
+import com.android.systemui.qs.tiles.base.ui.viewmodel.QSTileViewModel
+import com.android.systemui.qs.tiles.base.ui.viewmodel.QSTileViewModelFactory
import com.android.systemui.qs.tiles.impl.screenrecord.domain.interactor.ScreenRecordTileDataInteractor
import com.android.systemui.qs.tiles.impl.screenrecord.domain.interactor.ScreenRecordTileUserActionInteractor
-import com.android.systemui.qs.tiles.impl.screenrecord.domain.ui.ScreenRecordTileMapper
-import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
-import com.android.systemui.qs.tiles.viewmodel.QSTileUIConfig
-import com.android.systemui.qs.tiles.viewmodel.QSTileViewModel
+import com.android.systemui.qs.tiles.impl.screenrecord.domain.ui.mapper.ScreenRecordTileMapper
import com.android.systemui.res.R
import com.android.systemui.screenrecord.data.model.ScreenRecordModel
import com.android.systemui.screenrecord.data.repository.ScreenRecordRepository
@@ -86,7 +86,7 @@ interface ScreenRecordModule {
factory: QSTileViewModelFactory.Static<ScreenRecordModel>,
mapper: ScreenRecordTileMapper,
stateInteractor: ScreenRecordTileDataInteractor,
- userActionInteractor: ScreenRecordTileUserActionInteractor
+ userActionInteractor: ScreenRecordTileUserActionInteractor,
): QSTileViewModel =
factory.create(
TileSpec.create(SCREEN_RECORD_TILE_SPEC),
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotWindow.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotWindow.kt
index c4fe7a428084..644e12cba6fc 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotWindow.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotWindow.kt
@@ -31,7 +31,6 @@ import android.view.Window
import android.view.WindowInsets
import android.view.WindowManager
import android.window.WindowContext
-import com.android.app.viewcapture.ViewCaptureAwareWindowManager
import com.android.internal.policy.PhoneWindow
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
@@ -42,7 +41,6 @@ class ScreenshotWindow
@AssistedInject
constructor(
private val windowManager: WindowManager,
- private val viewCaptureAwareWindowManager: ViewCaptureAwareWindowManager,
private val context: Context,
@Assisted private val display: Display,
) {
@@ -97,7 +95,7 @@ constructor(
Log.d(TAG, "attachWindow")
}
attachRequested = true
- viewCaptureAwareWindowManager.addView(decorView, params)
+ windowManager.addView(decorView, params)
decorView.requestApplyInsets()
decorView.requireViewById<ViewGroup>(R.id.content).apply {
@@ -135,7 +133,7 @@ constructor(
if (LogConfig.DEBUG_WINDOW) {
Log.d(TAG, "Removing screenshot window")
}
- viewCaptureAwareWindowManager.removeViewImmediate(decorView)
+ windowManager.removeViewImmediate(decorView)
detachRequested = false
}
if (attachRequested && !detachRequested) {
diff --git a/packages/SystemUI/src/com/android/systemui/scrim/ScrimView.java b/packages/SystemUI/src/com/android/systemui/scrim/ScrimView.java
index 917869a66ca4..3e6f234a3663 100644
--- a/packages/SystemUI/src/com/android/systemui/scrim/ScrimView.java
+++ b/packages/SystemUI/src/com/android/systemui/scrim/ScrimView.java
@@ -30,8 +30,10 @@ import android.graphics.Rect;
import android.graphics.RenderEffect;
import android.graphics.Shader;
import android.graphics.drawable.Drawable;
+import android.os.Build;
import android.os.Looper;
import android.util.AttributeSet;
+import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
@@ -52,6 +54,9 @@ import java.util.concurrent.Executor;
* need to be careful to synchronize when necessary.
*/
public class ScrimView extends View {
+ private static final String TAG = "ScrimView";
+ private static final boolean isDebugLoggable = Build.isDebuggable() || Log.isLoggable(TAG,
+ Log.DEBUG);
private final Object mColorLock = new Object();
@@ -381,12 +386,21 @@ public class ScrimView extends View {
*/
public void setBlurRadius(float blurRadius) {
if (blurRadius > 0) {
+ debugLog("Apply blur RenderEffect to ScrimView " + mScrimName + " for radius "
+ + blurRadius);
setRenderEffect(RenderEffect.createBlurEffect(
blurRadius,
blurRadius,
Shader.TileMode.CLAMP));
} else {
+ debugLog("Resetting blur RenderEffect to ScrimView " + mScrimName);
setRenderEffect(null);
}
}
+
+ private void debugLog(String logMsg) {
+ if (isDebugLoggable) {
+ Log.d(TAG, logMsg);
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index 24e7976011f4..fcfce1281722 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -206,6 +206,7 @@ import com.google.android.msdl.data.model.MSDLToken;
import com.google.android.msdl.domain.MSDLPlayer;
import dagger.Lazy;
+
import kotlin.Unit;
import kotlinx.coroutines.CoroutineDispatcher;
@@ -988,8 +989,10 @@ public final class NotificationPanelViewController implements
mBlurConfig.getMaxBlurRadiusPx(),
Shader.TileMode.CLAMP);
}
+ debugLog("Applying blur RenderEffect to shade.");
mView.setRenderEffect(mBlurRenderEffect);
} else {
+ debugLog("Resetting blur RenderEffect on shade.");
mView.setRenderEffect(null);
}
}
@@ -4267,7 +4270,8 @@ public final class NotificationPanelViewController implements
== AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_FORWARD.getId()
|| action
== AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_UP.getId()) {
- mStatusBarKeyguardViewManager.showPrimaryBouncer(true);
+ mStatusBarKeyguardViewManager.showPrimaryBouncer(true,
+ "NotificationPanelViewController#performAccessibilityAction");
return true;
}
return super.performAccessibilityAction(host, action, args);
diff --git a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsControllerImpl.java b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsControllerImpl.java
index c671f7d9db14..4ad4ab3410d6 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsControllerImpl.java
@@ -102,6 +102,7 @@ import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.policy.SplitShadeStateController;
import com.android.systemui.util.LargeScreenUtils;
import com.android.systemui.util.kotlin.JavaAdapter;
+import com.android.systemui.utils.windowmanager.WindowManagerProvider;
import dalvik.annotation.optimization.NeverCompile;
@@ -299,6 +300,8 @@ public class QuickSettingsControllerImpl implements QuickSettingsController, Dum
private final Runnable mQsCollapseExpandAction = this::collapseOrExpandQs;
private final QS.ScrollListener mQsScrollListener = this::onScroll;
+ private final WindowManagerProvider mWindowManagerProvider;
+
@Inject
public QuickSettingsControllerImpl(
Lazy<NotificationPanelViewController> panelViewControllerLazy,
@@ -336,7 +339,8 @@ public class QuickSettingsControllerImpl implements QuickSettingsController, Dum
CastController castController,
SplitShadeStateController splitShadeStateController,
Lazy<CommunalTransitionViewModel> communalTransitionViewModelLazy,
- Lazy<LargeScreenHeaderHelper> largeScreenHeaderHelperLazy
+ Lazy<LargeScreenHeaderHelper> largeScreenHeaderHelperLazy,
+ WindowManagerProvider windowManagerProvider
) {
SceneContainerFlag.assertInLegacyMode();
mPanelViewControllerLazy = panelViewControllerLazy;
@@ -387,6 +391,8 @@ public class QuickSettingsControllerImpl implements QuickSettingsController, Dum
mLockscreenShadeTransitionController.addCallback(new LockscreenShadeTransitionCallback());
dumpManager.registerDumpable(this);
+
+ mWindowManagerProvider = windowManagerProvider;
}
@VisibleForTesting
@@ -532,7 +538,7 @@ public class QuickSettingsControllerImpl implements QuickSettingsController, Dum
* on ACTION_DOWN, and safely queried repeatedly thereafter during ACTION_MOVE events.
*/
void updateGestureInsetsCache() {
- WindowManager wm = this.mPanelView.getContext().getSystemService(WindowManager.class);
+ WindowManager wm = mWindowManagerProvider.getWindowManager(this.mPanelView.getContext());
WindowMetrics windowMetrics = wm.getCurrentWindowMetrics();
mCachedGestureInsets = windowMetrics.getWindowInsets().getInsets(
WindowInsets.Type.systemGestures());
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerSceneImpl.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerSceneImpl.kt
index e6834ad6cdda..b211f0729318 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerSceneImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerSceneImpl.kt
@@ -97,6 +97,11 @@ constructor(
loggingReason = "ShadeControllerSceneImpl.instantCollapseShade",
transitionKey = Instant,
)
+
+ shadeInteractor.collapseQuickSettingsShade(
+ loggingReason = "ShadeControllerSceneImpl.instantCollapseShade",
+ transitionKey = Instant,
+ )
}
override fun animateCollapseShade(
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeDisplayAwareModule.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeDisplayAwareModule.kt
index cd224735cc62..446d4b450edc 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeDisplayAwareModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeDisplayAwareModule.kt
@@ -49,6 +49,8 @@ import com.android.systemui.statusbar.notification.stack.NotificationStackRebind
import com.android.systemui.statusbar.phone.ConfigurationControllerImpl
import com.android.systemui.statusbar.phone.ConfigurationForwarder
import com.android.systemui.statusbar.policy.ConfigurationController
+import com.android.systemui.utils.windowmanager.WindowManagerProvider
+import com.android.systemui.utils.windowmanager.WindowManagerUtils
import dagger.Module
import dagger.Provides
import dagger.multibindings.ClassKey
@@ -111,9 +113,10 @@ object ShadeDisplayAwareModule {
fun provideShadeWindowManager(
defaultWindowManager: WindowManager,
@ShadeDisplayAware context: Context,
+ windowManagerProvider: WindowManagerProvider
): WindowManager {
return if (ShadeWindowGoesAround.isEnabled) {
- context.getSystemService(WindowManager::class.java) as WindowManager
+ windowManagerProvider.getWindowManager(context)
} else {
defaultWindowManager
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/carrier/ShadeCarrierGroupController.java b/packages/SystemUI/src/com/android/systemui/shade/carrier/ShadeCarrierGroupController.java
index 4b8cc00e1c28..421b5ea23278 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/carrier/ShadeCarrierGroupController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/carrier/ShadeCarrierGroupController.java
@@ -39,9 +39,13 @@ import androidx.annotation.VisibleForTesting;
import com.android.keyguard.CarrierTextManager;
import com.android.settingslib.AccessibilityContentDescriptions;
import com.android.settingslib.mobile.TelephonyIcons;
+import com.android.systemui.Flags;
import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.dagger.qualifiers.Application;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.kairos.ExperimentalKairosApi;
+import com.android.systemui.kairos.KairosNetwork;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.res.R;
import com.android.systemui.shade.ShadeDisplayAware;
@@ -52,17 +56,30 @@ import com.android.systemui.statusbar.connectivity.ui.MobileContextProvider;
import com.android.systemui.statusbar.phone.StatusBarLocation;
import com.android.systemui.statusbar.pipeline.StatusBarPipelineFlags;
import com.android.systemui.statusbar.pipeline.mobile.ui.MobileUiAdapter;
+import com.android.systemui.statusbar.pipeline.mobile.ui.MobileUiAdapterKairos;
import com.android.systemui.statusbar.pipeline.mobile.ui.binder.MobileIconsBinder;
import com.android.systemui.statusbar.pipeline.mobile.ui.view.ModernShadeCarrierGroupMobileView;
import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.MobileIconsViewModel;
+import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.MobileIconsViewModelKairos;
import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.ShadeCarrierGroupMobileIconViewModel;
import com.android.systemui.util.CarrierConfigTracker;
+import dagger.Lazy;
+
+import kotlin.OptIn;
+import kotlin.Pair;
+
+import kotlinx.coroutines.CoroutineScope;
+import kotlinx.coroutines.Job;
+
+import java.util.ArrayList;
import java.util.List;
+import java.util.concurrent.CancellationException;
import java.util.function.Consumer;
import javax.inject.Inject;
+@OptIn(markerClass = ExperimentalKairosApi.class)
public class ShadeCarrierGroupController {
private static final String TAG = "ShadeCarrierGroup";
@@ -100,38 +117,43 @@ public class ShadeCarrierGroupController {
private final ShadeCarrierGroupControllerLogger mLogger;
+ private final Lazy<MobileUiAdapterKairos> mMobileUiAdapterKairos;
+ private final CoroutineScope mAppScope;
+ private final KairosNetwork mKairosNetwork;
+
private final SignalCallback mSignalCallback = new SignalCallback() {
- @Override
- public void setMobileDataIndicators(@NonNull MobileDataIndicators indicators) {
- int slotIndex = getSlotIndex(indicators.subId);
- if (slotIndex >= SIM_SLOTS) {
- Log.w(TAG, "setMobileDataIndicators - slot: " + slotIndex);
- return;
- }
- if (slotIndex == INVALID_SIM_SLOT_INDEX) {
- Log.e(TAG, "Invalid SIM slot index for subscription: " + indicators.subId);
- return;
- }
- mInfos[slotIndex] = new CellSignalState(
- indicators.statusIcon.visible,
- indicators.statusIcon.icon,
- indicators.statusIcon.contentDescription,
- indicators.typeContentDescription.toString(),
- indicators.roaming
- );
- mMainHandler.obtainMessage(H.MSG_UPDATE_STATE).sendToTarget();
- }
+ @Override
+ public void setMobileDataIndicators(@NonNull MobileDataIndicators indicators) {
+ int slotIndex = getSlotIndex(indicators.subId);
+ if (slotIndex >= SIM_SLOTS) {
+ Log.w(TAG, "setMobileDataIndicators - slot: " + slotIndex);
+ return;
+ }
+ if (slotIndex == INVALID_SIM_SLOT_INDEX) {
+ Log.e(TAG, "Invalid SIM slot index for subscription: " + indicators.subId);
+ return;
+ }
+ mInfos[slotIndex] = new CellSignalState(
+ indicators.statusIcon.visible,
+ indicators.statusIcon.icon,
+ indicators.statusIcon.contentDescription,
+ indicators.typeContentDescription.toString(),
+ indicators.roaming
+ );
+ mMainHandler.obtainMessage(H.MSG_UPDATE_STATE).sendToTarget();
+ }
- @Override
- public void setNoSims(boolean hasNoSims, boolean simDetected) {
- if (hasNoSims) {
- for (int i = 0; i < SIM_SLOTS; i++) {
- mInfos[i] = mInfos[i].changeVisibility(false);
- }
- }
- mMainHandler.obtainMessage(H.MSG_UPDATE_STATE).sendToTarget();
+ @Override
+ public void setNoSims(boolean hasNoSims, boolean simDetected) {
+ if (hasNoSims) {
+ for (int i = 0; i < SIM_SLOTS; i++) {
+ mInfos[i] = mInfos[i].changeVisibility(false);
}
- };
+ }
+ mMainHandler.obtainMessage(H.MSG_UPDATE_STATE).sendToTarget();
+ }
+ };
+ private final ArrayList<Job> mBindingJobs = new ArrayList<>();
private static class Callback implements CarrierTextManager.CarrierTextCallback {
private H mHandler;
@@ -159,7 +181,10 @@ public class ShadeCarrierGroupController {
SlotIndexResolver slotIndexResolver,
MobileUiAdapter mobileUiAdapter,
MobileContextProvider mobileContextProvider,
- StatusBarPipelineFlags statusBarPipelineFlags
+ StatusBarPipelineFlags statusBarPipelineFlags,
+ Lazy<MobileUiAdapterKairos> mobileUiAdapterKairos,
+ CoroutineScope appScope,
+ KairosNetwork kairosNetwork
) {
mContext = context;
mActivityStarter = activityStarter;
@@ -174,6 +199,9 @@ public class ShadeCarrierGroupController {
.build();
mCarrierConfigTracker = carrierConfigTracker;
mSlotIndexResolver = slotIndexResolver;
+ mMobileUiAdapterKairos = mobileUiAdapterKairos;
+ mAppScope = appScope;
+ mKairosNetwork = kairosNetwork;
View.OnClickListener onClickListener = v -> {
if (!v.isVisibleToUser()) {
return;
@@ -195,8 +223,12 @@ public class ShadeCarrierGroupController {
mMobileIconsViewModel = mobileUiAdapter.getMobileIconsViewModel();
if (mStatusBarPipelineFlags.useNewShadeCarrierGroupMobileIcons()) {
- mobileUiAdapter.setShadeCarrierGroupController(this);
- MobileIconsBinder.bind(view, mMobileIconsViewModel);
+ if (Flags.statusBarMobileIconKairos()) {
+ mobileUiAdapterKairos.get().setShadeCarrierGroupController(this);
+ } else {
+ mobileUiAdapter.setShadeCarrierGroupController(this);
+ MobileIconsBinder.bind(view, mMobileIconsViewModel);
+ }
}
mCarrierDividers[0] = view.getCarrierDivider1();
@@ -243,21 +275,52 @@ public class ShadeCarrierGroupController {
List<IconData> iconDataList = processSubIdList(subIds);
- for (IconData iconData : iconDataList) {
- ShadeCarrier carrier = mCarrierGroups[iconData.slotIndex];
-
- Context mobileContext =
- mMobileContextProvider.getMobileContextForSub(iconData.subId, mContext);
- ModernShadeCarrierGroupMobileView modernMobileView = ModernShadeCarrierGroupMobileView
- .constructAndBind(
- mobileContext,
- mMobileIconsViewModel.getLogger(),
- "mobile_carrier_shade_group",
- (ShadeCarrierGroupMobileIconViewModel) mMobileIconsViewModel
- .viewModelForSub(iconData.subId,
- StatusBarLocation.SHADE_CARRIER_GROUP)
- );
- carrier.addModernMobileView(modernMobileView);
+ if (Flags.statusBarMobileIconKairos()) {
+ for (Job job : mBindingJobs) {
+ job.cancel(new CancellationException());
+ }
+ mBindingJobs.clear();
+ MobileIconsViewModelKairos mobileIconsViewModel =
+ mMobileUiAdapterKairos.get().getMobileIconsViewModel();
+ for (IconData iconData : iconDataList) {
+ ShadeCarrier carrier = mCarrierGroups[iconData.slotIndex];
+
+ Context mobileContext =
+ mMobileContextProvider.getMobileContextForSub(iconData.subId, mContext);
+
+ Pair<ModernShadeCarrierGroupMobileView, Job> viewAndJob =
+ ModernShadeCarrierGroupMobileView.constructAndBind(
+ mobileContext,
+ mobileIconsViewModel.getLogger(),
+ "mobile_carrier_shade_group",
+ mobileIconsViewModel.shadeCarrierGroupIcon(iconData.subId),
+ mAppScope,
+ iconData.subId,
+ StatusBarLocation.SHADE_CARRIER_GROUP,
+ mKairosNetwork
+ );
+ mBindingJobs.add(viewAndJob.getSecond());
+ carrier.addModernMobileView(viewAndJob.getFirst());
+ }
+ } else {
+ for (IconData iconData : iconDataList) {
+ ShadeCarrier carrier = mCarrierGroups[iconData.slotIndex];
+
+ Context mobileContext =
+ mMobileContextProvider.getMobileContextForSub(iconData.subId, mContext);
+
+ ModernShadeCarrierGroupMobileView modernMobileView =
+ ModernShadeCarrierGroupMobileView
+ .constructAndBind(
+ mobileContext,
+ mMobileIconsViewModel.getLogger(),
+ "mobile_carrier_shade_group",
+ (ShadeCarrierGroupMobileIconViewModel) mMobileIconsViewModel
+ .viewModelForSub(iconData.subId,
+ StatusBarLocation.SHADE_CARRIER_GROUP)
+ );
+ carrier.addModernMobileView(modernMobileView);
+ }
}
}
@@ -288,7 +351,6 @@ public class ShadeCarrierGroupController {
* Sets a {@link OnSingleCarrierChangedListener}.
*
* This will get notified when the number of carriers changes between 1 and "not one".
- * @param listener
*/
public void setOnSingleCarrierChangedListener(
@Nullable OnSingleCarrierChangedListener listener) {
@@ -489,6 +551,9 @@ public class ShadeCarrierGroupController {
private final MobileUiAdapter mMobileUiAdapter;
private final MobileContextProvider mMobileContextProvider;
private final StatusBarPipelineFlags mStatusBarPipelineFlags;
+ private final CoroutineScope mAppScope;
+ private final KairosNetwork mKairosNetwork;
+ private final Lazy<MobileUiAdapterKairos> mMobileUiAdapterKairos;
@Inject
public Builder(
@@ -503,7 +568,10 @@ public class ShadeCarrierGroupController {
SlotIndexResolver slotIndexResolver,
MobileUiAdapter mobileUiAdapter,
MobileContextProvider mobileContextProvider,
- StatusBarPipelineFlags statusBarPipelineFlags
+ StatusBarPipelineFlags statusBarPipelineFlags,
+ @Application CoroutineScope appScope,
+ KairosNetwork kairosNetwork,
+ Lazy<MobileUiAdapterKairos> mobileUiAdapterKairos
) {
mActivityStarter = activityStarter;
mHandler = handler;
@@ -517,6 +585,9 @@ public class ShadeCarrierGroupController {
mMobileUiAdapter = mobileUiAdapter;
mMobileContextProvider = mobileContextProvider;
mStatusBarPipelineFlags = statusBarPipelineFlags;
+ mAppScope = appScope;
+ mKairosNetwork = kairosNetwork;
+ mMobileUiAdapterKairos = mobileUiAdapterKairos;
}
public Builder setShadeCarrierGroup(ShadeCarrierGroup view) {
@@ -538,8 +609,10 @@ public class ShadeCarrierGroupController {
mSlotIndexResolver,
mMobileUiAdapter,
mMobileContextProvider,
- mStatusBarPipelineFlags
- );
+ mStatusBarPipelineFlags,
+ mMobileUiAdapterKairos,
+ mAppScope,
+ mKairosNetwork);
}
}
@@ -571,7 +644,8 @@ public class ShadeCarrierGroupController {
public static class SubscriptionManagerSlotIndexResolver implements SlotIndexResolver {
@Inject
- public SubscriptionManagerSlotIndexResolver() {}
+ public SubscriptionManagerSlotIndexResolver() {
+ }
@Override
public int getSlotIndex(int subscriptionId) {
diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImpl.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImpl.kt
index 52de0abf7d3c..043742245227 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractorSceneContainerImpl.kt
@@ -187,12 +187,18 @@ constructor(
override fun collapseNotificationsShade(loggingReason: String, transitionKey: TransitionKey?) {
if (shadeModeInteractor.isDualShade) {
- // TODO(b/356596436): Hide without animation if transitionKey is Instant.
- sceneInteractor.hideOverlay(
- overlay = Overlays.NotificationsShade,
- loggingReason = loggingReason,
- transitionKey = transitionKey,
- )
+ if (transitionKey == Instant) {
+ sceneInteractor.instantlyHideOverlay(
+ overlay = Overlays.NotificationsShade,
+ loggingReason = loggingReason,
+ )
+ } else {
+ sceneInteractor.hideOverlay(
+ overlay = Overlays.NotificationsShade,
+ loggingReason = loggingReason,
+ transitionKey = transitionKey,
+ )
+ }
} else if (transitionKey == Instant) {
// TODO(b/356596436): Define instant transition instead of snapToScene().
sceneInteractor.snapToScene(
@@ -215,12 +221,18 @@ constructor(
bypassNotificationsShade: Boolean,
) {
if (shadeModeInteractor.isDualShade) {
- // TODO(b/356596436): Hide without animation if transitionKey is Instant.
- sceneInteractor.hideOverlay(
- overlay = Overlays.QuickSettingsShade,
- loggingReason = loggingReason,
- transitionKey = transitionKey,
- )
+ if (transitionKey == Instant) {
+ sceneInteractor.instantlyHideOverlay(
+ overlay = Overlays.QuickSettingsShade,
+ loggingReason = loggingReason,
+ )
+ } else {
+ sceneInteractor.hideOverlay(
+ overlay = Overlays.QuickSettingsShade,
+ loggingReason = loggingReason,
+ transitionKey = transitionKey,
+ )
+ }
return
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModel.kt b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModel.kt
index 5609326362fc..c264e3525026 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModel.kt
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+@file:OptIn(ExperimentalKairosApi::class)
+
package com.android.systemui.shade.ui.viewmodel
import android.content.Context
@@ -28,6 +30,8 @@ import androidx.compose.ui.graphics.Color
import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.compose.animation.scene.OverlayKey
import com.android.systemui.battery.BatteryMeterViewController
+import com.android.systemui.kairos.ExperimentalKairosApi
+import com.android.systemui.kairos.KairosNetwork
import com.android.systemui.lifecycle.ExclusiveActivatable
import com.android.systemui.lifecycle.Hydrator
import com.android.systemui.plugins.ActivityStarter
@@ -47,6 +51,7 @@ import com.android.systemui.statusbar.phone.ui.StatusBarIconController
import com.android.systemui.statusbar.phone.ui.TintedIconManager
import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconsInteractor
import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.MobileIconsViewModel
+import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.MobileIconsViewModelKairos
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import java.util.Locale
@@ -76,6 +81,8 @@ constructor(
private val tintedIconManagerFactory: TintedIconManager.Factory,
private val batteryMeterViewControllerFactory: BatteryMeterViewController.Factory,
val statusBarIconController: StatusBarIconController,
+ val kairosNetwork: KairosNetwork,
+ val mobileIconsViewModelKairos: dagger.Lazy<MobileIconsViewModelKairos>,
) : ExclusiveActivatable() {
private val hydrator = Hydrator("ShadeHeaderViewModel.hydrator")
@@ -269,15 +276,17 @@ constructor(
data object Weak : HeaderChipHighlight {
override fun backgroundColor(colorScheme: ColorScheme): Color =
- colorScheme.primary.copy(alpha = 0.1f)
+ colorScheme.surface.copy(alpha = 0.1f)
- override fun foregroundColor(colorScheme: ColorScheme): Color = colorScheme.primary
+ override fun foregroundColor(colorScheme: ColorScheme): Color = colorScheme.onSurface
}
data object Strong : HeaderChipHighlight {
- override fun backgroundColor(colorScheme: ColorScheme): Color = colorScheme.secondary
+ override fun backgroundColor(colorScheme: ColorScheme): Color =
+ colorScheme.primaryContainer
- override fun foregroundColor(colorScheme: ColorScheme): Color = colorScheme.onSecondary
+ override fun foregroundColor(colorScheme: ColorScheme): Color =
+ colorScheme.onPrimaryContainer
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeUserActions.kt b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeUserActions.kt
index cf3b08c041be..8d2d9efb56eb 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeUserActions.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeUserActions.kt
@@ -71,6 +71,8 @@ fun dualShadeActions(): Array<Pair<UserAction, UserActionResult>> {
Swipe.Down to ShowOverlay(Overlays.NotificationsShade),
Swipe.Down(fromSource = SceneContainerArea.EndHalf) to
ShowOverlay(Overlays.QuickSettingsShade),
+ Swipe.Down(fromSource = SceneContainerArea.TopEdgeEndHalf) to
+ ShowOverlay(Overlays.QuickSettingsShade),
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BlurUtils.kt b/packages/SystemUI/src/com/android/systemui/statusbar/BlurUtils.kt
index 2bacee12db8a..9940ae523b60 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BlurUtils.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BlurUtils.kt
@@ -16,12 +16,15 @@
package com.android.systemui.statusbar
+import android.annotation.SuppressLint
import android.app.ActivityManager
import android.content.res.Resources
+import android.os.Build
import android.os.SystemProperties
import android.os.Trace
import android.os.Trace.TRACE_TAG_APP
import android.util.IndentingPrintWriter
+import android.util.Log
import android.util.MathUtils
import android.view.CrossWindowBlurListeners
import android.view.CrossWindowBlurListeners.CROSS_WINDOW_BLUR_SUPPORTED
@@ -45,7 +48,7 @@ open class BlurUtils @Inject constructor(
private val crossWindowBlurListeners: CrossWindowBlurListeners,
dumpManager: DumpManager
) : Dumpable {
- val minBlurRadius = resources.getDimensionPixelSize(R.dimen.min_window_blur_radius).toFloat();
+ val minBlurRadius = resources.getDimensionPixelSize(R.dimen.min_window_blur_radius).toFloat()
val maxBlurRadius = if (Flags.notificationShadeBlur()) {
blurConfig.maxBlurRadiusPx
} else {
@@ -55,6 +58,9 @@ open class BlurUtils @Inject constructor(
private var lastAppliedBlur = 0
private var earlyWakeupEnabled = false
+ /** When this is true, early wakeup flag is not reset on surface flinger when blur drops to 0 */
+ private var persistentEarlyWakeupRequired = false
+
init {
dumpManager.registerDumpable(this)
}
@@ -91,11 +97,8 @@ open class BlurUtils @Inject constructor(
return
}
if (lastAppliedBlur == 0 && radius != 0) {
- Trace.asyncTraceForTrackBegin(
- TRACE_TAG_APP, TRACK_NAME, "eEarlyWakeup (prepareBlur)", 0)
- earlyWakeupEnabled = true
createTransaction().use {
- it.setEarlyWakeupStart()
+ earlyWakeupStart(it, "eEarlyWakeup (prepareBlur)")
it.apply()
}
}
@@ -116,19 +119,15 @@ open class BlurUtils @Inject constructor(
if (shouldBlur(radius)) {
it.setBackgroundBlurRadius(viewRootImpl.surfaceControl, radius)
if (!earlyWakeupEnabled && lastAppliedBlur == 0 && radius != 0) {
- Trace.asyncTraceForTrackBegin(
- TRACE_TAG_APP,
- TRACK_NAME,
- "eEarlyWakeup (applyBlur)",
- 0
- )
- it.setEarlyWakeupStart()
- earlyWakeupEnabled = true
+ earlyWakeupStart(it, "eEarlyWakeup (applyBlur)")
}
- if (earlyWakeupEnabled && lastAppliedBlur != 0 && radius == 0) {
- it.setEarlyWakeupEnd()
- Trace.asyncTraceForTrackEnd(TRACE_TAG_APP, TRACK_NAME, 0)
- earlyWakeupEnabled = false
+ if (
+ earlyWakeupEnabled &&
+ lastAppliedBlur != 0 &&
+ radius == 0 &&
+ !persistentEarlyWakeupRequired
+ ) {
+ earlyWakeupEnd(it, "applyBlur")
}
lastAppliedBlur = radius
}
@@ -137,6 +136,26 @@ open class BlurUtils @Inject constructor(
}
}
+ private fun v(verboseLog: String) {
+ if (isLoggable) Log.v(TAG, verboseLog)
+ }
+
+ @SuppressLint("MissingPermission")
+ private fun earlyWakeupStart(transaction: SurfaceControl.Transaction, traceMethodName: String) {
+ v("earlyWakeupStart from $traceMethodName")
+ Trace.asyncTraceForTrackBegin(TRACE_TAG_APP, TRACK_NAME, traceMethodName, 0)
+ transaction.setEarlyWakeupStart()
+ earlyWakeupEnabled = true
+ }
+
+ @SuppressLint("MissingPermission")
+ private fun earlyWakeupEnd(transaction: SurfaceControl.Transaction, loggingContext: String) {
+ v("earlyWakeupEnd from $loggingContext")
+ transaction.setEarlyWakeupEnd()
+ Trace.asyncTraceForTrackEnd(TRACE_TAG_APP, TRACK_NAME, 0)
+ earlyWakeupEnabled = false
+ }
+
@VisibleForTesting
open fun createTransaction(): SurfaceControl.Transaction {
return SurfaceControl.Transaction()
@@ -177,7 +196,39 @@ open class BlurUtils @Inject constructor(
}
}
+ /**
+ * Enables/disables the early wakeup flag on surface flinger. Keeps the early wakeup flag on
+ * until it reset by passing false to this method.
+ */
+ fun setPersistentEarlyWakeup(persistentWakeup: Boolean, viewRootImpl: ViewRootImpl?) {
+ persistentEarlyWakeupRequired = persistentWakeup
+ if (viewRootImpl == null || !supportsBlursOnWindows()) return
+ if (persistentEarlyWakeupRequired) {
+ if (earlyWakeupEnabled) return
+ createTransaction().use {
+ earlyWakeupStart(it, "setEarlyWakeup")
+ it.apply()
+ }
+ } else {
+ if (!earlyWakeupEnabled) return
+ if (lastAppliedBlur > 0) {
+ Log.w(
+ TAG,
+ "resetEarlyWakeup invoked when lastAppliedBlur $lastAppliedBlur is " +
+ "non-zero, this means that the early wakeup signal was reset while blur" +
+ " was still active",
+ )
+ }
+ createTransaction().use {
+ earlyWakeupEnd(it, "resetEarlyWakeup")
+ it.apply()
+ }
+ }
+ }
+
companion object {
const val TRACK_NAME = "BlurUtils"
+ private const val TAG = "BlurUtils"
+ private val isLoggable = Log.isLoggable(TAG, Log.VERBOSE) || Build.isDebuggable()
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index e44701dba87c..4daf61a895c7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -64,6 +64,7 @@ import androidx.annotation.VisibleForTesting;
import com.android.internal.annotations.KeepForWeakReference;
import com.android.internal.os.SomeArgs;
+import com.android.internal.statusbar.DisableStates;
import com.android.internal.statusbar.IAddTileResultCallback;
import com.android.internal.statusbar.IStatusBar;
import com.android.internal.statusbar.IUndoMediaTransferCallback;
@@ -85,6 +86,7 @@ import java.io.FileOutputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.Map;
/**
* This class takes the functions from IStatusBar that come in on
@@ -184,6 +186,8 @@ public class CommandQueue extends IStatusBar.Stub implements
private static final int MSG_TOGGLE_QUICK_SETTINGS_PANEL = 82 << MSG_SHIFT;
private static final int MSG_WALLET_ACTION_LAUNCH_GESTURE = 83 << MSG_SHIFT;
private static final int MSG_DISPLAY_REMOVE_SYSTEM_DECORATIONS = 85 << MSG_SHIFT;
+ private static final int MSG_DISABLE_ALL = 86 << MSG_SHIFT;
+
public static final int FLAG_EXCLUDE_NONE = 0;
public static final int FLAG_EXCLUDE_SEARCH_PANEL = 1 << 0;
public static final int FLAG_EXCLUDE_RECENTS_PANEL = 1 << 1;
@@ -654,7 +658,8 @@ public class CommandQueue extends IStatusBar.Stub implements
/**
* Called to notify that disable flags are updated.
- * @see Callbacks#disable(int, int, int, boolean).
+ * @see Callbacks#disable(int, int, int, boolean)
+ * @see Callbacks#disableForAllDisplays(DisableStates)
*/
public void disable(int displayId, @DisableFlags int state1, @Disable2Flags int state2,
boolean animate) {
@@ -682,6 +687,27 @@ public class CommandQueue extends IStatusBar.Stub implements
disable(displayId, state1, state2, true);
}
+ @Override
+ public void disableForAllDisplays(DisableStates disableStates) throws RemoteException {
+ synchronized (mLock) {
+ for (Map.Entry<Integer, Pair<Integer, Integer>> displaysWithStates :
+ disableStates.displaysWithStates.entrySet()) {
+ int displayId = displaysWithStates.getKey();
+ Pair<Integer, Integer> states = displaysWithStates.getValue();
+ setDisabled(displayId, states.first, states.second);
+ }
+ mHandler.removeMessages(MSG_DISABLE_ALL);
+ Message msg = mHandler.obtainMessage(MSG_DISABLE_ALL, disableStates);
+ if (Looper.myLooper() == mHandler.getLooper()) {
+ // If its the right looper execute immediately so hides can be handled quickly.
+ mHandler.handleMessage(msg);
+ msg.recycle();
+ } else {
+ msg.sendToTarget();
+ }
+ }
+ }
+
/**
* Apply current disable flags by {@link CommandQueue#disable(int, int, int, boolean)}.
*
@@ -1552,6 +1578,21 @@ public class CommandQueue extends IStatusBar.Stub implements
args.argi4 != 0 /* animate */);
}
break;
+ case MSG_DISABLE_ALL:
+ DisableStates disableStates = (DisableStates) msg.obj;
+ boolean animate = disableStates.animate;
+ Map<Integer, Pair<Integer, Integer>> displaysWithDisableStates =
+ disableStates.displaysWithStates;
+ for (Map.Entry<Integer, Pair<Integer, Integer>> displayWithDisableStates :
+ displaysWithDisableStates.entrySet()) {
+ int displayId = displayWithDisableStates.getKey();
+ Pair<Integer, Integer> states = displayWithDisableStates.getValue();
+ for (int i = 0; i < mCallbacks.size(); i++) {
+ mCallbacks.get(i).disable(displayId, states.first, states.second,
+ animate);
+ }
+ }
+ break;
case MSG_EXPAND_NOTIFICATIONS:
for (int i = 0; i < mCallbacks.size(); i++) {
mCallbacks.get(i).animateExpandNotificationsPanel();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ImmersiveModeConfirmation.java b/packages/SystemUI/src/com/android/systemui/statusbar/ImmersiveModeConfirmation.java
index 97e62d79b374..2a9a47d83dd4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ImmersiveModeConfirmation.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ImmersiveModeConfirmation.java
@@ -28,9 +28,6 @@ import static android.view.WindowManager.LayoutParams.TYPE_PRIVATE_PRESENTATION;
import static android.window.DisplayAreaOrganizer.FEATURE_UNDEFINED;
import static android.window.DisplayAreaOrganizer.KEY_ROOT_DISPLAY_AREA_ID;
-import static com.android.systemui.Flags.enableViewCaptureTracing;
-import static com.android.systemui.util.ConvenienceExtensionsKt.toKotlinLazy;
-
import android.animation.ArgbEvaluator;
import android.animation.ValueAnimator;
import android.annotation.NonNull;
@@ -76,16 +73,13 @@ import android.widget.Button;
import android.widget.FrameLayout;
import android.widget.RelativeLayout;
-import com.android.app.viewcapture.ViewCapture;
-import com.android.app.viewcapture.ViewCaptureAwareWindowManager;
import com.android.systemui.CoreStartable;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.res.R;
import com.android.systemui.shared.system.TaskStackChangeListener;
import com.android.systemui.shared.system.TaskStackChangeListeners;
import com.android.systemui.util.settings.SecureSettings;
-
-import kotlin.Lazy;
+import com.android.systemui.utils.windowmanager.WindowManagerProvider;
import javax.inject.Inject;
@@ -112,13 +106,14 @@ public class ImmersiveModeConfirmation implements CoreStartable, CommandQueue.Ca
private long mShowDelayMs = 0L;
private final IBinder mWindowToken = new Binder();
private final CommandQueue mCommandQueue;
+ private final WindowManagerProvider mWindowManagerProvider;
private ClingWindowView mClingWindow;
/** The wrapper on the last {@link WindowManager} used to add the confirmation window. */
@Nullable
- private ViewCaptureAwareWindowManager mViewCaptureAwareWindowManager;
+ private WindowManager mWindowManager;
/**
- * The WindowContext that is registered with {@link #mViewCaptureAwareWindowManager} with
+ * The WindowContext that is registered with {@link #mWindowManager} with
* options to specify the {@link RootDisplayArea} to attach the confirmation window.
*/
@Nullable
@@ -136,21 +131,18 @@ public class ImmersiveModeConfirmation implements CoreStartable, CommandQueue.Ca
private ContentObserver mContentObserver;
- private Lazy<ViewCapture> mLazyViewCapture;
-
@Inject
public ImmersiveModeConfirmation(Context context, CommandQueue commandQueue,
- SecureSettings secureSettings,
- dagger.Lazy<ViewCapture> daggerLazyViewCapture,
- @Background Handler backgroundHandler) {
+ SecureSettings secureSettings, @Background Handler backgroundHandler,
+ WindowManagerProvider windowManagerProvider) {
mSysUiContext = context;
final Display display = mSysUiContext.getDisplay();
mDisplayContext = display.getDisplayId() == DEFAULT_DISPLAY
? mSysUiContext : mSysUiContext.createDisplayContext(display);
mCommandQueue = commandQueue;
mSecureSettings = secureSettings;
- mLazyViewCapture = toKotlinLazy(daggerLazyViewCapture);
mBackgroundHandler = backgroundHandler;
+ mWindowManagerProvider = windowManagerProvider;
}
boolean loadSetting(int currentUserId) {
@@ -257,14 +249,14 @@ public class ImmersiveModeConfirmation implements CoreStartable, CommandQueue.Ca
private void handleHide() {
if (mClingWindow != null) {
if (DEBUG) Log.d(TAG, "Hiding immersive mode confirmation");
- if (mViewCaptureAwareWindowManager != null) {
+ if (mWindowManager != null) {
try {
- mViewCaptureAwareWindowManager.removeView(mClingWindow);
+ mWindowManager.removeView(mClingWindow);
} catch (WindowManager.InvalidDisplayException e) {
Log.w(TAG, "Fail to hide the immersive confirmation window because of "
+ e);
}
- mViewCaptureAwareWindowManager = null;
+ mWindowManager = null;
mWindowContext = null;
}
mClingWindow = null;
@@ -525,8 +517,8 @@ public class ImmersiveModeConfirmation implements CoreStartable, CommandQueue.Ca
* confirmation window.
*/
@NonNull
- private ViewCaptureAwareWindowManager createWindowManager(int rootDisplayAreaId) {
- if (mViewCaptureAwareWindowManager != null) {
+ private WindowManager createWindowManager(int rootDisplayAreaId) {
+ if (mWindowManager != null) {
throw new IllegalStateException(
"Must not create a new WindowManager while there is an existing one");
}
@@ -535,10 +527,8 @@ public class ImmersiveModeConfirmation implements CoreStartable, CommandQueue.Ca
mWindowContextRootDisplayAreaId = rootDisplayAreaId;
mWindowContext = mDisplayContext.createWindowContext(
IMMERSIVE_MODE_CONFIRMATION_WINDOW_TYPE, options);
- WindowManager wm = mWindowContext.getSystemService(WindowManager.class);
- mViewCaptureAwareWindowManager = new ViewCaptureAwareWindowManager(wm, mLazyViewCapture,
- enableViewCaptureTracing());
- return mViewCaptureAwareWindowManager;
+ mWindowManager = mWindowManagerProvider.getWindowManager(mWindowContext);
+ return mWindowManager;
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutListSearch.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutListSearch.java
index ef0660fbcd1c..3d7d08910502 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutListSearch.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutListSearch.java
@@ -79,6 +79,7 @@ import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto;
import com.android.systemui.res.R;
import com.android.systemui.statusbar.phone.CentralSurfaces;
+import com.android.systemui.utils.windowmanager.WindowManagerProvider;
import com.google.android.material.bottomsheet.BottomSheetBehavior;
import com.google.android.material.bottomsheet.BottomSheetDialog;
@@ -148,43 +149,44 @@ public final class KeyboardShortcutListSearch {
private KeyCharacterMap mBackupKeyCharacterMap;
@VisibleForTesting
- KeyboardShortcutListSearch(Context context, WindowManager windowManager, int deviceId) {
+ KeyboardShortcutListSearch(Context context, @NonNull WindowManager windowManager,
+ int deviceId) {
this.mContext = new ContextThemeWrapper(
context, R.style.KeyboardShortcutHelper);
this.mPackageManager = AppGlobals.getPackageManager();
- if (windowManager != null) {
- this.mWindowManager = windowManager;
- } else {
- this.mWindowManager = mContext.getSystemService(WindowManager.class);
- }
+ this.mWindowManager = windowManager;
loadResources(this.mContext);
createHardcodedShortcuts(deviceId);
}
- private static KeyboardShortcutListSearch getInstance(Context context, int deviceId) {
+ private static KeyboardShortcutListSearch getInstance(Context context, int deviceId,
+ WindowManagerProvider windowManagerProvider) {
if (sInstance == null) {
- sInstance = new KeyboardShortcutListSearch(context, null, deviceId);
+ WindowManager windowManager = windowManagerProvider.getWindowManager(context);
+ sInstance = new KeyboardShortcutListSearch(context, windowManager, deviceId);
}
return sInstance;
}
- public static void show(Context context, int deviceId) {
+ public static void show(Context context, int deviceId,
+ WindowManagerProvider windowManagerProvider) {
MetricsLogger.visible(context,
MetricsProto.MetricsEvent.KEYBOARD_SHORTCUTS_HELPER);
synchronized (sLock) {
if (sInstance != null && !sInstance.mContext.equals(context)) {
dismiss();
}
- getInstance(context, deviceId).showKeyboardShortcuts(deviceId);
+ getInstance(context, deviceId, windowManagerProvider).showKeyboardShortcuts(deviceId);
}
}
- public static void toggle(Context context, int deviceId) {
+ public static void toggle(Context context, int deviceId,
+ WindowManagerProvider windowManagerProvider) {
synchronized (sLock) {
if (isShowing()) {
dismiss();
} else {
- show(context, deviceId);
+ show(context, deviceId, windowManagerProvider);
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java
index 2157d754ce87..bd6006c8faa6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java
@@ -70,6 +70,7 @@ import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto;
import com.android.settingslib.Utils;
import com.android.systemui.res.R;
+import com.android.systemui.utils.windowmanager.WindowManagerProvider;
import java.util.ArrayList;
import java.util.Collections;
@@ -141,38 +142,38 @@ public final class KeyboardShortcuts {
this.mContext = new ContextThemeWrapper(
context, android.R.style.Theme_DeviceDefault_Settings);
this.mPackageManager = AppGlobals.getPackageManager();
- if (windowManager != null) {
- this.mWindowManager = windowManager;
- } else {
- this.mWindowManager = mContext.getSystemService(WindowManager.class);
- }
+ this.mWindowManager = windowManager;
loadResources(context);
}
- private static KeyboardShortcuts getInstance(Context context) {
+ private static KeyboardShortcuts getInstance(Context context,
+ WindowManagerProvider windowManagerProvider) {
if (sInstance == null) {
- sInstance = new KeyboardShortcuts(context, null);
+ WindowManager windowManager = windowManagerProvider.getWindowManager(context);
+ sInstance = new KeyboardShortcuts(context, windowManager);
}
return sInstance;
}
- public static void show(Context context, int deviceId) {
+ public static void show(Context context, int deviceId,
+ WindowManagerProvider windowManagerProvider) {
MetricsLogger.visible(context,
MetricsProto.MetricsEvent.KEYBOARD_SHORTCUTS_HELPER);
synchronized (sLock) {
if (sInstance != null && !sInstance.mContext.equals(context)) {
dismiss();
}
- getInstance(context).showKeyboardShortcuts(deviceId);
+ getInstance(context, windowManagerProvider).showKeyboardShortcuts(deviceId);
}
}
- public static void toggle(Context context, int deviceId) {
+ public static void toggle(Context context, int deviceId,
+ WindowManagerProvider windowManagerProvider) {
synchronized (sLock) {
if (isShowing()) {
dismiss();
} else {
- show(context, deviceId);
+ show(context, deviceId, windowManagerProvider);
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutsReceiver.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutsReceiver.java
index 815f1fcfdec6..54c84aa139cc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutsReceiver.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutsReceiver.java
@@ -24,6 +24,7 @@ import android.content.Intent;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
import com.android.systemui.shared.recents.utilities.Utilities;
+import com.android.systemui.utils.windowmanager.WindowManagerProvider;
import javax.inject.Inject;
@@ -31,10 +32,13 @@ import javax.inject.Inject;
public class KeyboardShortcutsReceiver extends BroadcastReceiver {
private final FeatureFlags mFeatureFlags;
+ private final WindowManagerProvider mWindowManagerProvider;
@Inject
- public KeyboardShortcutsReceiver(FeatureFlags featureFlags) {
+ public KeyboardShortcutsReceiver(FeatureFlags featureFlags,
+ WindowManagerProvider windowManagerProvider) {
mFeatureFlags = featureFlags;
+ mWindowManagerProvider = windowManagerProvider;
}
@Override
@@ -44,13 +48,14 @@ public class KeyboardShortcutsReceiver extends BroadcastReceiver {
}
if (isTabletLayoutFlagEnabled() && Utilities.isLargeScreen(context)) {
if (Intent.ACTION_SHOW_KEYBOARD_SHORTCUTS.equals(intent.getAction())) {
- KeyboardShortcutListSearch.show(context, -1 /* deviceId unknown */);
+ KeyboardShortcutListSearch.show(context, -1 /* deviceId unknown */,
+ mWindowManagerProvider);
} else if (Intent.ACTION_DISMISS_KEYBOARD_SHORTCUTS.equals(intent.getAction())) {
KeyboardShortcutListSearch.dismiss();
}
} else {
if (Intent.ACTION_SHOW_KEYBOARD_SHORTCUTS.equals(intent.getAction())) {
- KeyboardShortcuts.show(context, -1 /* deviceId unknown */);
+ KeyboardShortcuts.show(context, -1 /* deviceId unknown */, mWindowManagerProvider);
} else if (Intent.ACTION_DISMISS_KEYBOARD_SHORTCUTS.equals(intent.getAction())) {
KeyboardShortcuts.dismiss();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGroupingUtil.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGroupingUtil.java
index 03c191e40ccf..2030606e4274 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGroupingUtil.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGroupingUtil.java
@@ -38,6 +38,7 @@ import com.android.internal.R;
import com.android.internal.widget.CachingIconView;
import com.android.internal.widget.ConversationLayout;
import com.android.internal.widget.ImageFloatingTextView;
+import com.android.systemui.statusbar.notification.icon.IconPack;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.NotificationContentView;
import com.android.systemui.statusbar.notification.row.shared.AsyncGroupHeaderViewInflation;
@@ -61,10 +62,19 @@ public class NotificationGroupingUtil {
private static final VisibilityApplicator VISIBILITY_APPLICATOR = new VisibilityApplicator();
private static final VisibilityApplicator APP_NAME_APPLICATOR = new AppNameApplicator();
private static final ResultApplicator LEFT_ICON_APPLICATOR = new LeftIconApplicator();
- private static final DataExtractor ICON_EXTRACTOR = new DataExtractor() {
+
+ @VisibleForTesting
+ static final DataExtractor ICON_EXTRACTOR = new DataExtractor() {
@Override
public Object extractData(ExpandableNotificationRow row) {
- return row.getEntry().getSbn().getNotification();
+ if (NotificationBundleUi.isEnabled()) {
+ if (row.getEntryAdapter().getSbn() != null) {
+ return row.getEntryAdapter().getSbn().getNotification();
+ }
+ return null;
+ } else {
+ return row.getEntry().getSbn().getNotification();
+ }
}
};
@@ -253,7 +263,7 @@ public class NotificationGroupingUtil {
if (NotificationBundleUi.isEnabled()) {
sbn = row.getEntryAdapter() != null ? row.getEntryAdapter().getSbn() : null;
} else {
- sbn = row.getEntry().getSbn();
+ sbn = row.getEntryLegacy().getSbn();
}
return (sbn != null && sbn.getNotification().showsTime());
}
@@ -357,7 +367,8 @@ public class NotificationGroupingUtil {
boolean isEmpty(View view);
}
- private interface DataExtractor {
+ @VisibleForTesting
+ interface DataExtractor {
Object extractData(ExpandableNotificationRow row);
}
@@ -395,13 +406,17 @@ public class NotificationGroupingUtil {
}
}
- private abstract static class IconComparator implements ViewComparator {
+ @VisibleForTesting
+ static class IconComparator implements ViewComparator {
@Override
public boolean compare(View parent, View child, Object parentData, Object childData) {
return false;
}
protected boolean hasSameIcon(Object parentData, Object childData) {
+ if (parentData == null || childData == null) {
+ return false;
+ }
Icon parentIcon = ((Notification) parentData).getSmallIcon();
Icon childIcon = ((Notification) childData).getSmallIcon();
return parentIcon.sameAs(childIcon);
@@ -411,6 +426,10 @@ public class NotificationGroupingUtil {
* @return whether two ImageViews have the same colorFilterSet or none at all
*/
protected boolean hasSameColor(Object parentData, Object childData) {
+ if ((parentData == null && childData != null)
+ || (parentData != null && childData == null)) {
+ return false;
+ }
int parentColor = ((Notification) parentData).color;
int childColor = ((Notification) childData).color;
return parentColor == childColor;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
index 472dc823423e..e292bcf1f7a8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
@@ -24,6 +24,7 @@ import android.util.IndentingPrintWriter
import android.util.Log
import android.util.MathUtils
import android.view.Choreographer
+import android.view.Display
import android.view.View
import androidx.annotation.VisibleForTesting
import androidx.dynamicanimation.animation.FloatPropertyCompat
@@ -42,7 +43,9 @@ import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.shade.ShadeExpansionChangeEvent
import com.android.systemui.shade.ShadeExpansionListener
+import com.android.systemui.shade.data.repository.ShadeDisplaysRepository
import com.android.systemui.shade.domain.interactor.ShadeModeInteractor
+import com.android.systemui.shade.shared.flag.ShadeWindowGoesAround
import com.android.systemui.statusbar.phone.BiometricUnlockController
import com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_WAKE_AND_UNLOCK
import com.android.systemui.statusbar.phone.DozeParameters
@@ -52,6 +55,7 @@ import com.android.systemui.util.WallpaperController
import com.android.systemui.wallpapers.domain.interactor.WallpaperInteractor
import com.android.systemui.window.domain.interactor.WindowRootViewBlurInteractor
import com.android.wm.shell.appzoomout.AppZoomOut
+import dagger.Lazy
import java.io.PrintWriter
import java.util.Optional
import javax.inject.Inject
@@ -83,6 +87,7 @@ constructor(
private val appZoomOutOptional: Optional<AppZoomOut>,
@Application private val applicationScope: CoroutineScope,
dumpManager: DumpManager,
+ private val shadeDisplaysRepository: Lazy<ShadeDisplaysRepository>,
) : ShadeExpansionListener, Dumpable {
companion object {
private const val WAKE_UP_ANIMATION_ENABLED = true
@@ -228,6 +233,14 @@ constructor(
private data class WakeAndUnlockBlurData(val radius: Float, val useZoom: Boolean = true)
+ private val isShadeOnDefaultDisplay: Boolean
+ get() =
+ if (ShadeWindowGoesAround.isEnabled) {
+ shadeDisplaysRepository.get().displayId.value == Display.DEFAULT_DISPLAY
+ } else {
+ true
+ }
+
/** Blur radius of the wake and unlock animation on this frame, and whether to zoom out. */
private var wakeAndUnlockBlurData = WakeAndUnlockBlurData(0f)
set(value) {
@@ -265,9 +278,14 @@ constructor(
var blur = shadeRadius.toInt()
// If the blur comes from waking up, we don't want to zoom out the background
val zoomOut =
- if (shadeRadius != wakeAndUnlockBlurData.radius|| wakeAndUnlockBlurData.useZoom)
- blurRadiusToZoomOut(blurRadius = shadeRadius)
- else 0f
+ when {
+ // When the shade is in another display, we don't want to zoom out the background.
+ // Only the default display is supported right now.
+ !isShadeOnDefaultDisplay -> 0f
+ shadeRadius != wakeAndUnlockBlurData.radius || wakeAndUnlockBlurData.useZoom ->
+ blurRadiusToZoomOut(blurRadius = shadeRadius)
+ else -> 0f
+ }
// Make blur be 0 if it is necessary to stop blur effect.
if (scrimsVisible) {
if (!Flags.notificationShadeBlur()) {
@@ -353,7 +371,9 @@ constructor(
interpolator = Interpolators.FAST_OUT_SLOW_IN
addUpdateListener { animation: ValueAnimator ->
wakeAndUnlockBlurData =
- WakeAndUnlockBlurData(blurUtils.blurRadiusOfRatio(animation.animatedValue as Float))
+ WakeAndUnlockBlurData(
+ blurUtils.blurRadiusOfRatio(animation.animatedValue as Float)
+ )
}
addListener(
object : AnimatorListenerAdapter() {
@@ -428,8 +448,10 @@ constructor(
applicationScope.launch {
wallpaperInteractor.wallpaperSupportsAmbientMode.collect { supported ->
wallpaperSupportsAmbientMode = supported
- if (getNewWakeBlurRadius(prevDozeAmount) == wakeAndUnlockBlurData.radius
- && !wakeAndUnlockBlurData.useZoom) {
+ if (
+ getNewWakeBlurRadius(prevDozeAmount) == wakeAndUnlockBlurData.radius &&
+ !wakeAndUnlockBlurData.useZoom
+ ) {
// Update wake and unlock radius only if the previous value comes from wake-up.
updateWakeBlurRadius(prevDozeAmount)
}
@@ -452,6 +474,21 @@ constructor(
scheduleUpdate()
}
}
+
+ applicationScope.launch {
+ windowRootViewBlurInteractor.isBlurCurrentlySupported.collect { supported ->
+ if (supported) {
+ // when battery saver changes, try scheduling an update.
+ scheduleUpdate()
+ } else {
+ // when blur becomes unsupported, no more updates will be scheduled,
+ // reset updateScheduled state.
+ updateScheduled = false
+ // reset blur and internal state to 0
+ onBlurApplied(0, 0.0f)
+ }
+ }
+ }
}
fun addListener(listener: DepthListener) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModel.kt
index 922afc7825c4..0763cb7751a0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModel.kt
@@ -21,6 +21,7 @@ import android.content.ComponentName
import android.content.Context
import android.view.View
import com.android.internal.jank.Cuj
+import com.android.internal.logging.InstanceId
import com.android.systemui.animation.ActivityTransitionAnimator
import com.android.systemui.animation.ComposableControllerFactory
import com.android.systemui.animation.DelegateTransitionAnimatorController
@@ -41,6 +42,7 @@ import com.android.systemui.statusbar.chips.ui.model.ColorsModel
import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel
import com.android.systemui.statusbar.chips.ui.view.ChipBackgroundContainer
import com.android.systemui.statusbar.chips.ui.viewmodel.OngoingActivityChipViewModel
+import com.android.systemui.statusbar.chips.uievents.StatusBarChipsUiEventLogger
import com.android.systemui.statusbar.core.StatusBarConnectedDisplays
import com.android.systemui.statusbar.phone.ongoingcall.StatusBarChipsModernization
import com.android.systemui.statusbar.phone.ongoingcall.shared.model.OngoingCallModel
@@ -59,7 +61,7 @@ import kotlinx.coroutines.flow.stateIn
/** View model for the ongoing phone call chip shown in the status bar. */
@SysUISingleton
-open class CallChipViewModel
+class CallChipViewModel
@Inject
constructor(
@Main private val context: Context,
@@ -68,6 +70,7 @@ constructor(
systemClock: SystemClock,
private val activityStarter: ActivityStarter,
@StatusBarChipsLog private val logger: LogBuffer,
+ private val uiEventLogger: StatusBarChipsUiEventLogger,
) : OngoingActivityChipViewModel {
/** The transition cookie used to register and unregister launch and return animations. */
private val cookie =
@@ -180,7 +183,7 @@ constructor(
isHidden: Boolean,
transitionState: TransitionState = TransitionState.NoTransition,
): OngoingActivityChipModel.Active {
- val key = state.notificationKey
+ val key = "$KEY_PREFIX${state.notificationKey}"
val contentDescription = getContentDescription(state.appName)
val icon =
if (state.notificationIconView != null) {
@@ -199,6 +202,8 @@ constructor(
}
val colors = ColorsModel.AccentThemed
+ val intent = state.intent
+ val instanceId = state.notificationInstanceId
// This block mimics OngoingCallController#updateChip.
if (state.startTimeMs <= 0L) {
@@ -208,10 +213,11 @@ constructor(
key = key,
icon = icon,
colors = colors,
- onClickListenerLegacy = getOnClickListener(state.intent),
- clickBehavior = getClickBehavior(state.intent),
+ onClickListenerLegacy = getOnClickListener(intent, instanceId),
+ clickBehavior = getClickBehavior(intent, instanceId),
isHidden = isHidden,
transitionManager = getTransitionManager(state, transitionState),
+ instanceId = instanceId,
)
} else {
val startTimeInElapsedRealtime =
@@ -221,19 +227,26 @@ constructor(
icon = icon,
colors = colors,
startTimeMs = startTimeInElapsedRealtime,
- onClickListenerLegacy = getOnClickListener(state.intent),
- clickBehavior = getClickBehavior(state.intent),
+ onClickListenerLegacy = getOnClickListener(intent, instanceId),
+ clickBehavior = getClickBehavior(intent, instanceId),
isHidden = isHidden,
transitionManager = getTransitionManager(state, transitionState),
+ instanceId = instanceId,
)
}
}
- private fun getOnClickListener(intent: PendingIntent?): View.OnClickListener? {
+ private fun getOnClickListener(
+ intent: PendingIntent?,
+ instanceId: InstanceId?,
+ ): View.OnClickListener? {
if (intent == null) return null
return View.OnClickListener { view ->
StatusBarChipsModernization.assertInLegacyMode()
+
logger.log(TAG, LogLevel.INFO, {}, { "Chip clicked" })
+ uiEventLogger.logChipTapToShow(instanceId)
+
val backgroundView =
view.requireViewById<ChipBackgroundContainer>(R.id.ongoing_activity_chip_background)
// This mimics OngoingCallController#updateChipClickListener.
@@ -247,13 +260,20 @@ constructor(
}
}
- private fun getClickBehavior(intent: PendingIntent?): OngoingActivityChipModel.ClickBehavior =
+ private fun getClickBehavior(
+ intent: PendingIntent?,
+ instanceId: InstanceId?,
+ ): OngoingActivityChipModel.ClickBehavior =
if (intent == null) {
OngoingActivityChipModel.ClickBehavior.None
} else {
OngoingActivityChipModel.ClickBehavior.ExpandAction(
onClick = { expandable ->
StatusBarChipsModernization.unsafeAssertInNewMode()
+
+ logger.log(TAG, LogLevel.INFO, {}, { "Chip clicked" })
+ uiEventLogger.logChipTapToShow(instanceId)
+
val animationController =
if (
!StatusBarChipsReturnAnimations.isEnabled ||
@@ -329,6 +349,7 @@ constructor(
cookie,
component,
launchCujType = Cuj.CUJ_STATUS_BAR_APP_LAUNCH_FROM_CALL_CHIP,
+ returnCujType = Cuj.CUJ_STATUS_BAR_APP_RETURN_TO_CALL_CHIP,
) {
override suspend fun createController(
forLaunch: Boolean
@@ -412,6 +433,8 @@ constructor(
)
private val TAG = "CallVM".pad()
+ const val KEY_PREFIX = "callChip-"
+
/** Determines whether or not an active call chip should be hidden. */
private fun shouldChipBeHidden(
oldState: OngoingCallModel,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/viewmodel/CastToOtherDeviceChipViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/viewmodel/CastToOtherDeviceChipViewModel.kt
index 6ce350cb95f5..596770ff9dfc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/viewmodel/CastToOtherDeviceChipViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/viewmodel/CastToOtherDeviceChipViewModel.kt
@@ -18,7 +18,6 @@ package com.android.systemui.statusbar.chips.casttootherdevice.ui.viewmodel
import android.content.Context
import androidx.annotation.DrawableRes
-import androidx.annotation.VisibleForTesting
import com.android.internal.jank.Cuj
import com.android.systemui.animation.DialogCuj
import com.android.systemui.animation.DialogTransitionAnimator
@@ -44,6 +43,7 @@ import com.android.systemui.statusbar.chips.ui.viewmodel.ChipTransitionHelper
import com.android.systemui.statusbar.chips.ui.viewmodel.OngoingActivityChipViewModel
import com.android.systemui.statusbar.chips.ui.viewmodel.OngoingActivityChipViewModel.Companion.createDialogLaunchOnClickCallback
import com.android.systemui.statusbar.chips.ui.viewmodel.OngoingActivityChipViewModel.Companion.createDialogLaunchOnClickListener
+import com.android.systemui.statusbar.chips.uievents.StatusBarChipsUiEventLogger
import com.android.systemui.util.time.SystemClock
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
@@ -70,7 +70,11 @@ constructor(
private val dialogTransitionAnimator: DialogTransitionAnimator,
private val endMediaProjectionDialogHelper: EndMediaProjectionDialogHelper,
@StatusBarChipsLog private val logger: LogBuffer,
+ private val uiEventLogger: StatusBarChipsUiEventLogger,
) : OngoingActivityChipViewModel {
+ // There can only be 1 active cast-to-other-device chip at a time, so we can re-use the ID.
+ private val instanceId = uiEventLogger.createNewInstanceId()
+
/** The cast chip to show, based only on MediaProjection API events. */
private val projectionChip: StateFlow<OngoingActivityChipModel> =
mediaProjectionChipInteractor.projection
@@ -194,6 +198,7 @@ constructor(
): OngoingActivityChipModel.Active {
return OngoingActivityChipModel.Active.Timer(
key = KEY,
+ isImportantForPrivacy = true,
icon =
OngoingActivityChipModel.ChipIcon.SingleColorIcon(
Icon.Resource(
@@ -212,8 +217,10 @@ constructor(
createCastScreenToOtherDeviceDialogDelegate(state),
dialogTransitionAnimator,
DIALOG_CUJ,
- logger,
- TAG,
+ instanceId = instanceId,
+ uiEventLogger = uiEventLogger,
+ logger = logger,
+ tag = TAG,
),
clickBehavior =
OngoingActivityChipModel.ClickBehavior.ExpandAction(
@@ -222,16 +229,20 @@ constructor(
createCastScreenToOtherDeviceDialogDelegate(state),
dialogTransitionAnimator,
DIALOG_CUJ,
- logger,
- TAG,
+ instanceId = instanceId,
+ uiEventLogger = uiEventLogger,
+ logger = logger,
+ tag = TAG,
)
),
+ instanceId = instanceId,
)
}
private fun createIconOnlyCastChip(deviceName: String?): OngoingActivityChipModel.Active {
return OngoingActivityChipModel.Active.IconOnly(
key = KEY,
+ isImportantForPrivacy = true,
icon =
OngoingActivityChipModel.ChipIcon.SingleColorIcon(
Icon.Resource(
@@ -246,8 +257,10 @@ constructor(
createGenericCastToOtherDeviceDialogDelegate(deviceName),
dialogTransitionAnimator,
DIALOG_CUJ_AUDIO_ONLY,
- logger,
- TAG,
+ instanceId = instanceId,
+ uiEventLogger = uiEventLogger,
+ logger = logger,
+ tag = TAG,
),
clickBehavior =
OngoingActivityChipModel.ClickBehavior.ExpandAction(
@@ -255,10 +268,13 @@ constructor(
createGenericCastToOtherDeviceDialogDelegate(deviceName),
dialogTransitionAnimator,
DIALOG_CUJ_AUDIO_ONLY,
- logger,
- TAG,
+ instanceId = instanceId,
+ uiEventLogger = uiEventLogger,
+ logger = logger,
+ tag = TAG,
)
),
+ instanceId = instanceId,
)
}
@@ -279,7 +295,7 @@ constructor(
)
companion object {
- @VisibleForTesting const val KEY = "CastToOtherDevice"
+ const val KEY = "CastToOtherDevice"
@DrawableRes val CAST_TO_OTHER_DEVICE_ICON = R.drawable.ic_cast_connected
private val DIALOG_CUJ =
DialogCuj(Cuj.CUJ_STATUS_BAR_LAUNCH_DIALOG_FROM_CHIP, tag = "Cast to other device")
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/mediaprojection/ui/view/EndMediaProjectionDialogHelper.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/mediaprojection/ui/view/EndMediaProjectionDialogHelper.kt
index 2d9ccb7b09b0..33ff6c014125 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/mediaprojection/ui/view/EndMediaProjectionDialogHelper.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/mediaprojection/ui/view/EndMediaProjectionDialogHelper.kt
@@ -19,6 +19,7 @@ package com.android.systemui.statusbar.chips.mediaprojection.ui.view
import android.app.ActivityManager
import android.content.DialogInterface
import android.content.pm.PackageManager
+import android.util.Log
import com.android.systemui.animation.DialogTransitionAnimator
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.mediaprojection.data.model.MediaProjectionState
@@ -54,10 +55,6 @@ constructor(
// dialog to animate back into the chip just for the chip to disappear in a few frames.
dialogTransitionAnimator.disableAllCurrentDialogsExitAnimations()
stopAction.invoke()
- // TODO(b/332662551): If the projection is stopped, there's a brief moment where the
- // dialog closes and the chip re-shows because the system APIs haven't come back and
- // told SysUI that the projection has officially stopped. It would be great for the chip
- // to not re-show at all.
}
}
@@ -85,8 +82,17 @@ constructor(
val appInfo = packageManager.getApplicationInfo(packageName, 0)
appInfo.loadLabel(packageManager)
} catch (e: PackageManager.NameNotFoundException) {
- // TODO(b/332662551): Log this error.
+ Log.w(
+ TAG,
+ "Failed to find application info for package: $packageName when creating " +
+ "end media projection dialog",
+ e,
+ )
null
}
}
+
+ companion object {
+ private const val TAG = "EndMediaProjectionDialogHelper"
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/domain/interactor/SingleNotificationChipInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/domain/interactor/SingleNotificationChipInteractor.kt
index a0de879845d3..6e0682c4d5b9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/domain/interactor/SingleNotificationChipInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/domain/interactor/SingleNotificationChipInteractor.kt
@@ -149,6 +149,7 @@ constructor(
creationTime = creationTime,
isAppVisible = appVisibility.isAppCurrentlyVisible,
lastAppVisibleTime = appVisibility.lastAppVisibleTime,
+ instanceId = instanceId,
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/domain/interactor/StatusBarNotificationChipsInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/domain/interactor/StatusBarNotificationChipsInteractor.kt
index 9380dfe32bf5..0560c9e1f774 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/domain/interactor/StatusBarNotificationChipsInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/domain/interactor/StatusBarNotificationChipsInteractor.kt
@@ -145,7 +145,9 @@ constructor(
/**
* Emits all notifications that are eligible to show as chips in the status bar. This is
- * different from which chips will *actually* show, see [shownNotificationChips] for that.
+ * different from which chips will *actually* show, because
+ * [com.android.systemui.statusbar.chips.notification.ui.viewmodel.NotifChipsViewModel] will
+ * hide chips that have [NotificationChipModel.isAppVisible] as true.
*/
val allNotificationChips: Flow<List<NotificationChipModel>> =
if (StatusBarNotifChips.isEnabled) {
@@ -186,15 +188,6 @@ constructor(
initialValue = emptyList(),
)
- /** Emits the notifications that should actually be *shown* as chips in the status bar. */
- val shownNotificationChips: Flow<List<NotificationChipModel>> =
- allNotificationChips.map { chipsList ->
- // If the app that posted this notification is visible, we want to hide the chip
- // because information between the status bar chip and the app itself could be
- // out-of-sync (like a timer that's slightly off)
- chipsList.filter { !it.isAppVisible }
- }
-
/*
Stable sort the promoted notifications by two criteria:
Criteria #1: Whichever app was most recently visible has higher ranking.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/domain/model/NotificationChipModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/domain/model/NotificationChipModel.kt
index 1f2079d83e6f..dad51baced56 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/domain/model/NotificationChipModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/domain/model/NotificationChipModel.kt
@@ -16,8 +16,9 @@
package com.android.systemui.statusbar.chips.notification.domain.model
+import com.android.internal.logging.InstanceId
import com.android.systemui.statusbar.StatusBarIconView
-import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel
+import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModels
/** Modeling all the data needed to render a status bar notification chip. */
data class NotificationChipModel(
@@ -25,7 +26,7 @@ data class NotificationChipModel(
/** The user-readable name of the app that posted this notification. */
val appName: String,
val statusBarChipIconView: StatusBarIconView?,
- val promotedContent: PromotedNotificationContentModel,
+ val promotedContent: PromotedNotificationContentModels,
/** The time when the notification first appeared as promoted. */
val creationTime: Long,
/** True if the app managing this notification is currently visible to the user. */
@@ -35,4 +36,6 @@ data class NotificationChipModel(
* hasn't become visible since the notification became promoted.
*/
val lastAppVisibleTime: Long?,
+ /** An optional per-notification ID used for logging. */
+ val instanceId: InstanceId?,
)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModel.kt
index 1a802d634894..83ef13d341d8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModel.kt
@@ -55,12 +55,12 @@ constructor(
private val systemClock: SystemClock,
) {
/**
- * A flow modeling the notification chips that should be shown. Emits an empty list if there are
- * no notifications that should show a status bar chip.
+ * A flow modeling the current notification chips. Emits an empty list if there are no
+ * notifications that are eligible to show a status bar chip.
*/
val chips: Flow<List<OngoingActivityChipModel.Active>> =
combine(
- notifChipsInteractor.shownNotificationChips,
+ notifChipsInteractor.allNotificationChips,
headsUpNotificationInteractor.statusBarHeadsUpState,
) { notifications, headsUpState ->
notifications.map { it.toActivityChipModel(headsUpState) }
@@ -72,6 +72,8 @@ constructor(
headsUpState: TopPinnedState
): OngoingActivityChipModel.Active {
StatusBarNotifChips.unsafeAssertInNewMode()
+ // Chips are never shown when locked, so it's safe to use the version with sensitive content
+ val chipContent = promotedContent.privateVersion
val contentDescription = getContentDescription(this.appName)
val icon =
if (this.statusBarChipIconView != null) {
@@ -96,6 +98,10 @@ constructor(
notifChipsInteractor.onPromotedNotificationChipTapped(this@toActivityChipModel.key)
}
}
+ // If the app that posted this notification is visible, we want to hide the chip
+ // because information between the status bar chip and the app itself could be
+ // out-of-sync (like a timer that's slightly off)
+ val isHidden = this.isAppVisible
val onClickListenerLegacy =
View.OnClickListener {
StatusBarChipsModernization.assertInLegacyMode()
@@ -115,65 +121,72 @@ constructor(
// If the user tapped this chip to show the HUN, we want to just show the icon because
// the HUN will show the rest of the information.
return OngoingActivityChipModel.Active.IconOnly(
- this.key,
- icon,
- colors,
- onClickListenerLegacy,
- clickBehavior,
+ key = this.key,
+ icon = icon,
+ colors = colors,
+ onClickListenerLegacy = onClickListenerLegacy,
+ clickBehavior = clickBehavior,
+ isHidden = isHidden,
+ instanceId = instanceId,
)
}
- if (this.promotedContent.shortCriticalText != null) {
+ if (chipContent.shortCriticalText != null) {
return OngoingActivityChipModel.Active.Text(
- this.key,
- icon,
- colors,
- this.promotedContent.shortCriticalText,
- onClickListenerLegacy,
- clickBehavior,
+ key = this.key,
+ icon = icon,
+ colors = colors,
+ text = chipContent.shortCriticalText,
+ onClickListenerLegacy = onClickListenerLegacy,
+ clickBehavior = clickBehavior,
+ isHidden = isHidden,
+ instanceId = instanceId,
)
}
- if (
- Flags.promoteNotificationsAutomatically() &&
- this.promotedContent.wasPromotedAutomatically
- ) {
+ if (Flags.promoteNotificationsAutomatically() && chipContent.wasPromotedAutomatically) {
// When we're promoting notifications automatically, the `when` time set on the
// notification will likely just be set to the current time, which would cause the chip
// to always show "now". We don't want early testers to get that experience since it's
// not what will happen at launch, so just don't show any time.onometerstate
return OngoingActivityChipModel.Active.IconOnly(
- this.key,
- icon,
- colors,
- onClickListenerLegacy,
- clickBehavior,
+ key = this.key,
+ icon = icon,
+ colors = colors,
+ onClickListenerLegacy = onClickListenerLegacy,
+ clickBehavior = clickBehavior,
+ isHidden = isHidden,
+ instanceId = instanceId,
)
}
- if (this.promotedContent.time == null) {
+ if (chipContent.time == null) {
return OngoingActivityChipModel.Active.IconOnly(
- this.key,
- icon,
- colors,
- onClickListenerLegacy,
- clickBehavior,
+ key = this.key,
+ icon = icon,
+ colors = colors,
+ onClickListenerLegacy = onClickListenerLegacy,
+ clickBehavior = clickBehavior,
+ isHidden = isHidden,
+ instanceId = instanceId,
)
}
- when (this.promotedContent.time) {
+ when (chipContent.time) {
is PromotedNotificationContentModel.When.Time -> {
return if (
- this.promotedContent.time.currentTimeMillis >=
+ chipContent.time.currentTimeMillis >=
systemClock.currentTimeMillis() + FUTURE_TIME_THRESHOLD_MILLIS
) {
OngoingActivityChipModel.Active.ShortTimeDelta(
- this.key,
- icon,
- colors,
- time = this.promotedContent.time.currentTimeMillis,
- onClickListenerLegacy,
- clickBehavior,
+ key = this.key,
+ icon = icon,
+ colors = colors,
+ time = chipContent.time.currentTimeMillis,
+ onClickListenerLegacy = onClickListenerLegacy,
+ clickBehavior = clickBehavior,
+ isHidden = isHidden,
+ instanceId = instanceId,
)
} else {
// Don't show a `when` time that's close to now or in the past because it's
@@ -185,23 +198,27 @@ constructor(
// automatically handles this for us and we're hoping to launch the notification
// chips at the same time as the Compose chips.
return OngoingActivityChipModel.Active.IconOnly(
- this.key,
- icon,
- colors,
- onClickListenerLegacy,
- clickBehavior,
+ key = this.key,
+ icon = icon,
+ colors = colors,
+ onClickListenerLegacy = onClickListenerLegacy,
+ clickBehavior = clickBehavior,
+ isHidden = isHidden,
+ instanceId = instanceId,
)
}
}
is PromotedNotificationContentModel.When.Chronometer -> {
return OngoingActivityChipModel.Active.Timer(
- this.key,
- icon,
- colors,
- startTimeMs = this.promotedContent.time.elapsedRealtimeMillis,
- isEventInFuture = this.promotedContent.time.isCountDown,
+ key = this.key,
+ icon = icon,
+ colors = colors,
+ startTimeMs = chipContent.time.elapsedRealtimeMillis,
+ isEventInFuture = chipContent.time.isCountDown,
onClickListenerLegacy = onClickListenerLegacy,
clickBehavior = clickBehavior,
+ isHidden = isHidden,
+ instanceId = instanceId,
)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/screenrecord/domain/interactor/ScreenRecordChipInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/screenrecord/domain/interactor/ScreenRecordChipInteractor.kt
index 572c7faf19e6..fce428dcb7e1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/screenrecord/domain/interactor/ScreenRecordChipInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/screenrecord/domain/interactor/ScreenRecordChipInteractor.kt
@@ -94,9 +94,11 @@ constructor(
TAG,
LogLevel.INFO,
{},
- { "State: Recording(taskPackage=null) due to force-start" },
+ {
+ "State: Recording(hostPackage=null, taskPackage=null) due to force-start"
+ },
)
- ScreenRecordChipModel.Recording(recordedTask = null)
+ ScreenRecordChipModel.Recording(hostPackage = null, recordedTask = null)
} else {
when (screenRecordState) {
is ScreenRecordModel.DoingNothing -> {
@@ -124,13 +126,25 @@ constructor(
} else {
null
}
+ val hostPackage =
+ if (mediaProjectionState is MediaProjectionState.Projecting) {
+ mediaProjectionState.hostPackage
+ } else {
+ null
+ }
logger.log(
TAG,
LogLevel.INFO,
- { str1 = recordedTask?.baseIntent?.component?.packageName },
- { "State: Recording(taskPackage=$str1)" },
+ {
+ str1 = hostPackage
+ str2 = recordedTask?.baseIntent?.component?.packageName
+ },
+ { "State: Recording(hostPackage=$str1, taskPackage=$str2)" },
+ )
+ ScreenRecordChipModel.Recording(
+ hostPackage = hostPackage,
+ recordedTask = recordedTask,
)
- ScreenRecordChipModel.Recording(recordedTask)
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/screenrecord/domain/model/ScreenRecordChipModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/screenrecord/domain/model/ScreenRecordChipModel.kt
index ba6cd4df8d47..0a7278792e70 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/screenrecord/domain/model/ScreenRecordChipModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/screenrecord/domain/model/ScreenRecordChipModel.kt
@@ -29,10 +29,13 @@ sealed interface ScreenRecordChipModel {
/**
* There's an active screen recording happening.
*
+ * @property hostPackage the package name of the app that is receiving the content of the media
+ * projection (aka which app the phone screen contents are being sent to).
* @property recordedTask the task being recorded if the user is recording only a single app.
* Null if the user is recording the entire screen or we don't have the task info yet.
*/
data class Recording(
+ val hostPackage: String?,
val recordedTask: ActivityManager.RunningTaskInfo?,
) : ScreenRecordChipModel
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/screenrecord/ui/viewmodel/ScreenRecordChipViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/screenrecord/ui/viewmodel/ScreenRecordChipViewModel.kt
index 55c89a96422f..336a9f47e5d1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/screenrecord/ui/viewmodel/ScreenRecordChipViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/screenrecord/ui/viewmodel/ScreenRecordChipViewModel.kt
@@ -19,7 +19,6 @@ package com.android.systemui.statusbar.chips.screenrecord.ui.viewmodel
import android.app.ActivityManager
import android.content.Context
import androidx.annotation.DrawableRes
-import androidx.annotation.VisibleForTesting
import com.android.internal.jank.Cuj
import com.android.systemui.animation.DialogCuj
import com.android.systemui.animation.DialogTransitionAnimator
@@ -44,6 +43,7 @@ import com.android.systemui.statusbar.chips.ui.viewmodel.ChipTransitionHelper
import com.android.systemui.statusbar.chips.ui.viewmodel.OngoingActivityChipViewModel
import com.android.systemui.statusbar.chips.ui.viewmodel.OngoingActivityChipViewModel.Companion.createDialogLaunchOnClickCallback
import com.android.systemui.statusbar.chips.ui.viewmodel.OngoingActivityChipViewModel.Companion.createDialogLaunchOnClickListener
+import com.android.systemui.statusbar.chips.uievents.StatusBarChipsUiEventLogger
import com.android.systemui.util.kotlin.pairwise
import com.android.systemui.util.time.SystemClock
import javax.inject.Inject
@@ -66,7 +66,9 @@ constructor(
private val endMediaProjectionDialogHelper: EndMediaProjectionDialogHelper,
private val dialogTransitionAnimator: DialogTransitionAnimator,
@StatusBarChipsLog private val logger: LogBuffer,
+ private val uiEventLogger: StatusBarChipsUiEventLogger,
) : OngoingActivityChipViewModel {
+ private val instanceId = uiEventLogger.createNewInstanceId()
/** A direct mapping from [ScreenRecordChipModel] to [OngoingActivityChipModel]. */
private val simpleChip =
@@ -77,13 +79,16 @@ constructor(
is ScreenRecordChipModel.Starting -> {
OngoingActivityChipModel.Active.Countdown(
key = KEY,
+ isImportantForPrivacy = true,
colors = ColorsModel.Red,
secondsUntilStarted = state.millisUntilStarted.toCountdownSeconds(),
+ instanceId = instanceId,
)
}
is ScreenRecordChipModel.Recording -> {
OngoingActivityChipModel.Active.Timer(
key = KEY,
+ isImportantForPrivacy = true,
icon =
OngoingActivityChipModel.ChipIcon.SingleColorIcon(
Icon.Resource(
@@ -100,8 +105,10 @@ constructor(
createDelegate(state.recordedTask),
dialogTransitionAnimator,
DIALOG_CUJ,
- logger,
- TAG,
+ instanceId = instanceId,
+ uiEventLogger = uiEventLogger,
+ logger = logger,
+ tag = TAG,
),
clickBehavior =
OngoingActivityChipModel.ClickBehavior.ExpandAction(
@@ -109,10 +116,13 @@ constructor(
dialogDelegate = createDelegate(state.recordedTask),
dialogTransitionAnimator = dialogTransitionAnimator,
DIALOG_CUJ,
- logger,
- TAG,
+ instanceId = instanceId,
+ uiEventLogger = uiEventLogger,
+ logger = logger,
+ tag = TAG,
)
),
+ instanceId = instanceId,
)
}
}
@@ -165,7 +175,7 @@ constructor(
}
companion object {
- @VisibleForTesting const val KEY = "ScreenRecord"
+ const val KEY = "ScreenRecord"
@DrawableRes val ICON = R.drawable.ic_screenrecord
private val DIALOG_CUJ =
DialogCuj(Cuj.CUJ_STATUS_BAR_LAUNCH_DIALOG_FROM_CHIP, tag = "Screen record")
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/sharetoapp/ui/viewmodel/ShareToAppChipViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/sharetoapp/ui/viewmodel/ShareToAppChipViewModel.kt
index 92e17bdd511a..26787189209e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/sharetoapp/ui/viewmodel/ShareToAppChipViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/sharetoapp/ui/viewmodel/ShareToAppChipViewModel.kt
@@ -18,7 +18,6 @@ package com.android.systemui.statusbar.chips.sharetoapp.ui.viewmodel
import android.content.Context
import androidx.annotation.DrawableRes
-import androidx.annotation.VisibleForTesting
import com.android.internal.jank.Cuj
import com.android.systemui.CoreStartable
import com.android.systemui.animation.DialogCuj
@@ -44,6 +43,7 @@ import com.android.systemui.statusbar.chips.ui.viewmodel.ChipTransitionHelper
import com.android.systemui.statusbar.chips.ui.viewmodel.OngoingActivityChipViewModel
import com.android.systemui.statusbar.chips.ui.viewmodel.OngoingActivityChipViewModel.Companion.createDialogLaunchOnClickCallback
import com.android.systemui.statusbar.chips.ui.viewmodel.OngoingActivityChipViewModel.Companion.createDialogLaunchOnClickListener
+import com.android.systemui.statusbar.chips.uievents.StatusBarChipsUiEventLogger
import com.android.systemui.util.kotlin.sample
import com.android.systemui.util.time.SystemClock
import javax.inject.Inject
@@ -72,7 +72,10 @@ constructor(
private val endMediaProjectionDialogHelper: EndMediaProjectionDialogHelper,
private val dialogTransitionAnimator: DialogTransitionAnimator,
@StatusBarChipsLog private val logger: LogBuffer,
+ private val uiEventLogger: StatusBarChipsUiEventLogger,
) : OngoingActivityChipViewModel, CoreStartable {
+ // There can only be 1 active cast-to-other-device chip at a time, so we can re-use the ID.
+ private val instanceId = uiEventLogger.createNewInstanceId()
private val _stopDialogToShow: MutableStateFlow<MediaProjectionStopDialogModel> =
MutableStateFlow(MediaProjectionStopDialogModel.Hidden)
@@ -222,6 +225,7 @@ constructor(
): OngoingActivityChipModel.Active {
return OngoingActivityChipModel.Active.Timer(
key = KEY,
+ isImportantForPrivacy = true,
icon =
OngoingActivityChipModel.ChipIcon.SingleColorIcon(
Icon.Resource(
@@ -237,8 +241,10 @@ constructor(
createShareScreenToAppDialogDelegate(state),
dialogTransitionAnimator,
DIALOG_CUJ,
- logger,
- TAG,
+ instanceId = instanceId,
+ uiEventLogger = uiEventLogger,
+ logger = logger,
+ tag = TAG,
),
clickBehavior =
OngoingActivityChipModel.ClickBehavior.ExpandAction(
@@ -247,16 +253,20 @@ constructor(
createShareScreenToAppDialogDelegate(state),
dialogTransitionAnimator,
DIALOG_CUJ,
- logger,
- TAG,
+ instanceId = instanceId,
+ uiEventLogger = uiEventLogger,
+ logger = logger,
+ tag = TAG,
)
),
+ instanceId = instanceId,
)
}
private fun createIconOnlyShareToAppChip(): OngoingActivityChipModel.Active {
return OngoingActivityChipModel.Active.IconOnly(
key = KEY,
+ isImportantForPrivacy = true,
icon =
OngoingActivityChipModel.ChipIcon.SingleColorIcon(
Icon.Resource(
@@ -272,8 +282,10 @@ constructor(
createGenericShareToAppDialogDelegate(),
dialogTransitionAnimator,
DIALOG_CUJ_AUDIO_ONLY,
- logger,
- TAG,
+ instanceId = instanceId,
+ uiEventLogger = uiEventLogger,
+ logger = logger,
+ tag = TAG,
),
clickBehavior =
OngoingActivityChipModel.ClickBehavior.ExpandAction(
@@ -281,10 +293,13 @@ constructor(
createGenericShareToAppDialogDelegate(),
dialogTransitionAnimator,
DIALOG_CUJ_AUDIO_ONLY,
- logger,
- TAG,
+ instanceId = instanceId,
+ uiEventLogger = uiEventLogger,
+ logger = logger,
+ tag = TAG,
)
),
+ instanceId = instanceId,
)
}
@@ -304,7 +319,7 @@ constructor(
)
companion object {
- @VisibleForTesting const val KEY = "ShareToApp"
+ const val KEY = "ShareToApp"
@DrawableRes val SHARE_TO_APP_ICON = R.drawable.ic_present_to_all
private val DIALOG_CUJ =
DialogCuj(Cuj.CUJ_STATUS_BAR_LAUNCH_DIALOG_FROM_CHIP, tag = "Share to app")
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/compose/OngoingActivityChip.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/compose/OngoingActivityChip.kt
index 74b3f302cdc8..104c2b546200 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/compose/OngoingActivityChip.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/compose/OngoingActivityChip.kt
@@ -25,6 +25,7 @@ import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.layout.widthIn
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
@@ -36,8 +37,10 @@ import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.dimensionResource
import androidx.compose.ui.semantics.contentDescription
import androidx.compose.ui.semantics.semantics
+import androidx.compose.ui.unit.Dp
import androidx.compose.ui.viewinterop.AndroidView
import com.android.compose.animation.Expandable
+import com.android.compose.modifiers.thenIf
import com.android.systemui.animation.Expandable
import com.android.systemui.common.ui.compose.Icon
import com.android.systemui.common.ui.compose.load
@@ -79,6 +82,17 @@ fun OngoingActivityChip(
}
is OngoingActivityChipModel.ClickBehavior.None -> null
}
+ val isClickable = onClick != null
+
+ val chipSidePadding = dimensionResource(id = R.dimen.ongoing_activity_chip_side_padding)
+ val minWidth =
+ if (isClickable) {
+ dimensionResource(id = R.dimen.min_clickable_item_size)
+ } else if (model.icon != null) {
+ dimensionResource(id = R.dimen.ongoing_activity_chip_icon_size) + chipSidePadding
+ } else {
+ dimensionResource(id = R.dimen.ongoing_activity_chip_min_text_width) + chipSidePadding
+ }
Expandable(
color = Color(model.colors.background(LocalContext.current).defaultColor),
@@ -92,6 +106,19 @@ fun OngoingActivityChip(
this.contentDescription = contentDescription
}
}
+ .thenIf(isClickable) { Modifier.widthIn(min = minWidth) }
+ // For non-privacy-related chips, only show the chip if there's enough space for at
+ // least the minimum width.
+ .thenIf(!model.isImportantForPrivacy) {
+ Modifier.layout { measurable, constraints ->
+ val placeable = measurable.measure(constraints)
+ layout(placeable.width, placeable.height) {
+ if (constraints.maxWidth >= minWidth.roundToPx()) {
+ placeable.place(0, 0)
+ }
+ }
+ }
+ }
.graphicsLayer(
alpha =
if (model.transitionManager?.hideChipForTransition == true) {
@@ -103,9 +130,12 @@ fun OngoingActivityChip(
borderStroke = borderStroke,
onClick = onClick,
useModifierBasedImplementation = StatusBarChipsReturnAnimations.isEnabled,
+ // Some chips like the 3-2-1 countdown chip should be very small, smaller than a
+ // reasonable minimum size.
+ defaultMinSize = false,
transitionControllerFactory = model.transitionManager?.controllerFactory,
) {
- ChipBody(model, iconViewStore, isClickable = onClick != null)
+ ChipBody(model, iconViewStore, isClickable = isClickable, minWidth = minWidth)
}
}
@@ -114,36 +144,22 @@ private fun ChipBody(
model: OngoingActivityChipModel.Active,
iconViewStore: NotificationIconContainerViewBinder.IconViewStore?,
isClickable: Boolean,
+ minWidth: Dp,
modifier: Modifier = Modifier,
) {
val hasEmbeddedIcon =
model.icon is OngoingActivityChipModel.ChipIcon.StatusBarView ||
model.icon is OngoingActivityChipModel.ChipIcon.StatusBarNotificationIcon
- val chipSidePadding = dimensionResource(id = R.dimen.ongoing_activity_chip_side_padding)
- val minWidth =
- if (isClickable) {
- dimensionResource(id = R.dimen.min_clickable_item_size)
- } else if (model.icon != null) {
- dimensionResource(id = R.dimen.ongoing_activity_chip_icon_size) + chipSidePadding
- } else {
- dimensionResource(id = R.dimen.ongoing_activity_chip_min_text_width) + chipSidePadding
- }
-
Row(
horizontalArrangement = Arrangement.Center,
verticalAlignment = Alignment.CenterVertically,
modifier =
modifier
.fillMaxHeight()
- .layout { measurable, constraints ->
- val placeable = measurable.measure(constraints)
- layout(placeable.width, placeable.height) {
- if (constraints.maxWidth >= minWidth.roundToPx()) {
- placeable.place(0, 0)
- }
- }
- }
+ // Set the minWidth here as well as on the Expandable so that the content within
+ // this row is still centered correctly horizontally
+ .thenIf(isClickable) { Modifier.widthIn(min = minWidth) }
.padding(
horizontal =
if (hasEmbeddedIcon) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/model/ColorsModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/model/ColorsModel.kt
index b2683762cb2a..fe6065d0bb42 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/model/ColorsModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/model/ColorsModel.kt
@@ -46,8 +46,9 @@ sealed interface ColorsModel {
}
/** The chip should match the system theme main color. */
- // TODO(b/347717946): The chip's color isn't getting updated when the user switches theme, it
- // only gets updated when a different configuration change happens, like a rotation.
+ // Note: When StatusBarChipsModernization is disabled, the chip's color doesn't get
+ // updated when the user switches theme. It only gets updated when a different
+ // configuration change happens, like a rotation.
data object SystemThemed : ColorsModel {
override fun background(context: Context): ColorStateList =
ColorStateList.valueOf(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/model/OngoingActivityChipModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/model/OngoingActivityChipModel.kt
index 364e6656ee9d..d7b67b1f7bfb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/model/OngoingActivityChipModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/model/OngoingActivityChipModel.kt
@@ -20,6 +20,7 @@ import android.annotation.CurrentTimeMillisLong
import android.annotation.ElapsedRealtimeLong
import android.os.SystemClock
import android.view.View
+import com.android.internal.logging.InstanceId
import com.android.systemui.animation.ComposableControllerFactory
import com.android.systemui.animation.Expandable
import com.android.systemui.common.shared.model.ContentDescription
@@ -57,6 +58,11 @@ sealed class OngoingActivityChipModel {
* A key that uniquely identifies this chip. Used for better visual effects, like animation.
*/
open val key: String,
+ /**
+ * True if this chip is critical for privacy so we should keep it visible at all times, and
+ * false otherwise.
+ */
+ open val isImportantForPrivacy: Boolean = false,
/** The icon to show on the chip. If null, no icon will be shown. */
open val icon: ChipIcon?,
/** What colors to use for the chip. */
@@ -76,11 +82,17 @@ sealed class OngoingActivityChipModel {
open val isHidden: Boolean,
/** Whether the transition from hidden to shown should be animated. */
open val shouldAnimate: Boolean,
+ /**
+ * An optional per-chip ID used for logging. Should stay the same throughout the lifetime of
+ * a single chip.
+ */
+ open val instanceId: InstanceId? = null,
) : OngoingActivityChipModel() {
/** This chip shows only an icon and nothing else. */
data class IconOnly(
override val key: String,
+ override val isImportantForPrivacy: Boolean = false,
override val icon: ChipIcon,
override val colors: ColorsModel,
override val onClickListenerLegacy: View.OnClickListener?,
@@ -88,9 +100,11 @@ sealed class OngoingActivityChipModel {
override val transitionManager: TransitionManager? = null,
override val isHidden: Boolean = false,
override val shouldAnimate: Boolean = true,
+ override val instanceId: InstanceId? = null,
) :
Active(
key,
+ isImportantForPrivacy,
icon,
colors,
onClickListenerLegacy,
@@ -98,6 +112,7 @@ sealed class OngoingActivityChipModel {
transitionManager,
isHidden,
shouldAnimate,
+ instanceId,
) {
override val logName = "Active.Icon"
}
@@ -105,6 +120,7 @@ sealed class OngoingActivityChipModel {
/** The chip shows a timer, counting up from [startTimeMs]. */
data class Timer(
override val key: String,
+ override val isImportantForPrivacy: Boolean = false,
override val icon: ChipIcon,
override val colors: ColorsModel,
/**
@@ -135,9 +151,11 @@ sealed class OngoingActivityChipModel {
override val transitionManager: TransitionManager? = null,
override val isHidden: Boolean = false,
override val shouldAnimate: Boolean = true,
+ override val instanceId: InstanceId? = null,
) :
Active(
key,
+ isImportantForPrivacy,
icon,
colors,
onClickListenerLegacy,
@@ -145,6 +163,7 @@ sealed class OngoingActivityChipModel {
transitionManager,
isHidden,
shouldAnimate,
+ instanceId,
) {
override val logName = "Active.Timer"
}
@@ -155,6 +174,7 @@ sealed class OngoingActivityChipModel {
*/
data class ShortTimeDelta(
override val key: String,
+ override val isImportantForPrivacy: Boolean = false,
override val icon: ChipIcon,
override val colors: ColorsModel,
/**
@@ -172,9 +192,11 @@ sealed class OngoingActivityChipModel {
override val transitionManager: TransitionManager? = null,
override val isHidden: Boolean = false,
override val shouldAnimate: Boolean = true,
+ override val instanceId: InstanceId? = null,
) :
Active(
key,
+ isImportantForPrivacy,
icon,
colors,
onClickListenerLegacy,
@@ -182,6 +204,7 @@ sealed class OngoingActivityChipModel {
transitionManager,
isHidden,
shouldAnimate,
+ instanceId,
) {
init {
StatusBarNotifChips.unsafeAssertInNewMode()
@@ -196,15 +219,18 @@ sealed class OngoingActivityChipModel {
*/
data class Countdown(
override val key: String,
+ override val isImportantForPrivacy: Boolean = false,
override val colors: ColorsModel,
/** The number of seconds until an event is started. */
val secondsUntilStarted: Long,
override val transitionManager: TransitionManager? = null,
override val isHidden: Boolean = false,
override val shouldAnimate: Boolean = true,
+ override val instanceId: InstanceId? = null,
) :
Active(
key,
+ isImportantForPrivacy,
icon = null,
colors,
onClickListenerLegacy = null,
@@ -212,6 +238,7 @@ sealed class OngoingActivityChipModel {
transitionManager,
isHidden,
shouldAnimate,
+ instanceId,
) {
override val logName = "Active.Countdown"
}
@@ -219,18 +246,20 @@ sealed class OngoingActivityChipModel {
/** This chip shows the specified [text] in the chip. */
data class Text(
override val key: String,
+ override val isImportantForPrivacy: Boolean = false,
override val icon: ChipIcon,
override val colors: ColorsModel,
- // TODO(b/361346412): Enforce a max length requirement?
val text: String,
override val onClickListenerLegacy: View.OnClickListener? = null,
override val clickBehavior: ClickBehavior,
override val transitionManager: TransitionManager? = null,
override val isHidden: Boolean = false,
override val shouldAnimate: Boolean = true,
+ override val instanceId: InstanceId? = null,
) :
Active(
key,
+ isImportantForPrivacy,
icon,
colors,
onClickListenerLegacy,
@@ -238,6 +267,7 @@ sealed class OngoingActivityChipModel {
transitionManager,
isHidden,
shouldAnimate,
+ instanceId,
) {
override val logName = "Active.Text"
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipViewModel.kt
index 229d1a56e177..efc54f1c5a74 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipViewModel.kt
@@ -17,6 +17,7 @@
package com.android.systemui.statusbar.chips.ui.viewmodel
import android.view.View
+import com.android.internal.logging.InstanceId
import com.android.systemui.animation.DialogCuj
import com.android.systemui.animation.DialogTransitionAnimator
import com.android.systemui.animation.Expandable
@@ -26,6 +27,7 @@ import com.android.systemui.res.R
import com.android.systemui.statusbar.chips.StatusBarChipsLog
import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel
import com.android.systemui.statusbar.chips.ui.view.ChipBackgroundContainer
+import com.android.systemui.statusbar.chips.uievents.StatusBarChipsUiEventLogger
import com.android.systemui.statusbar.phone.SystemUIDialog
import com.android.systemui.statusbar.phone.ongoingcall.StatusBarChipsModernization
import kotlinx.coroutines.flow.StateFlow
@@ -44,12 +46,17 @@ interface OngoingActivityChipViewModel {
dialogDelegate: SystemUIDialog.Delegate,
dialogTransitionAnimator: DialogTransitionAnimator,
cuj: DialogCuj,
+ instanceId: InstanceId,
+ uiEventLogger: StatusBarChipsUiEventLogger,
@StatusBarChipsLog logger: LogBuffer,
tag: String,
): View.OnClickListener {
return View.OnClickListener { view ->
StatusBarChipsModernization.assertInLegacyMode()
+
logger.log(tag, LogLevel.INFO, {}, { "Chip clicked" })
+ uiEventLogger.logChipTapToShow(instanceId)
+
val dialog = dialogDelegate.createDialog()
val launchableView =
view.requireViewById<ChipBackgroundContainer>(
@@ -67,12 +74,17 @@ interface OngoingActivityChipViewModel {
dialogDelegate: SystemUIDialog.Delegate,
dialogTransitionAnimator: DialogTransitionAnimator,
cuj: DialogCuj,
+ instanceId: InstanceId,
+ uiEventLogger: StatusBarChipsUiEventLogger,
@StatusBarChipsLog logger: LogBuffer,
tag: String,
): (Expandable) -> Unit {
return { expandable ->
StatusBarChipsModernization.unsafeAssertInNewMode()
+
logger.log(tag, LogLevel.INFO, {}, { "Chip clicked" })
+ uiEventLogger.logChipTapToShow(instanceId)
+
val dialog = dialogDelegate.createDialog()
val controller = expandable.dialogTransitionController(cuj)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModel.kt
index 8228b5533fca..606604a090cf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModel.kt
@@ -264,12 +264,15 @@ constructor(
// [OngoingActivityChipModel.Active.Countdown] is the only chip without an icon and
// [shouldSquish] returns false for that model, but protect against it just in case.)
val currentIcon = icon ?: return this
+ // TODO(b/364653005): Make sure every field is copied over.
return OngoingActivityChipModel.Active.IconOnly(
- key,
- currentIcon,
- colors,
- onClickListenerLegacy,
- clickBehavior,
+ key = key,
+ isImportantForPrivacy = isImportantForPrivacy,
+ icon = currentIcon,
+ colors = colors,
+ onClickListenerLegacy = onClickListenerLegacy,
+ clickBehavior = clickBehavior,
+ instanceId = instanceId,
)
}
@@ -374,6 +377,9 @@ constructor(
* Sort the given chip [bundle] in order of priority, and divide the chips between active,
* overflow, and inactive (see [MultipleOngoingActivityChipsModel] for a description of each).
*/
+ // IMPORTANT: PromotedNotificationsInteractor re-implements this same ordering scheme. Any
+ // changes here should also be made in PromotedNotificationsInteractor.
+ // TODO(b/402471288): Create a single source of truth for the ordering.
private fun rankChips(bundle: ChipBundle): MultipleOngoingActivityChipsModel {
val activeChips = mutableListOf<OngoingActivityChipModel.Active>()
val overflowChips = mutableListOf<OngoingActivityChipModel.Active>()
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/uievents/StatusBarChipUiEvent.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/uievents/StatusBarChipUiEvent.kt
new file mode 100644
index 000000000000..837493016c95
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/uievents/StatusBarChipUiEvent.kt
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2025 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.chips.uievents
+
+import com.android.internal.logging.UiEvent
+import com.android.internal.logging.UiEventLogger
+
+/** All UI Events related to the status bar chips. */
+enum class StatusBarChipUiEvent(private val _id: Int) : UiEventLogger.UiEventEnum {
+ // New chip events, with chip type embedded in the event
+ @UiEvent(doc = "New status bar chip: Call") STATUS_BAR_NEW_CHIP_CALL(2211),
+ @UiEvent(doc = "New status bar chip: Screen record") STATUS_BAR_NEW_CHIP_SCREEN_RECORD(2212),
+ @UiEvent(doc = "New status bar chip: Share screen/audio to another app")
+ STATUS_BAR_NEW_CHIP_SHARE_TO_APP(2213),
+ @UiEvent(doc = "New status bar chip: Cast screen/audio to different device")
+ STATUS_BAR_NEW_CHIP_CAST_TO_OTHER_DEVICE(2214),
+ @UiEvent(doc = "New status bar chip: Promoted notification")
+ STATUS_BAR_NEW_CHIP_NOTIFICATION(2215),
+
+ // Other chip events, which don't need the chip type embedded in the event because an instanceId
+ // should also be provided with the new event and all subsequent events
+ @UiEvent(doc = "A status bar chip was removed") STATUS_BAR_CHIP_REMOVED(2216),
+ @UiEvent(doc = "A status bar chip was tapped to show more information")
+ STATUS_BAR_CHIP_TAP_TO_SHOW(2217),
+ @UiEvent(
+ doc = "A status bar chip was re-tapped to hide the information that was previously shown"
+ )
+ STATUS_BAR_CHIP_TAP_TO_HIDE(2218);
+
+ override fun getId() = _id
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/uievents/StatusBarChipsUiEventLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/uievents/StatusBarChipsUiEventLogger.kt
new file mode 100644
index 000000000000..c2349db2a188
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/uievents/StatusBarChipsUiEventLogger.kt
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2025 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.chips.uievents
+
+import com.android.internal.logging.InstanceId
+import com.android.internal.logging.InstanceIdSequence
+import com.android.internal.logging.UiEventLogger
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.statusbar.chips.call.ui.viewmodel.CallChipViewModel
+import com.android.systemui.statusbar.chips.casttootherdevice.ui.viewmodel.CastToOtherDeviceChipViewModel
+import com.android.systemui.statusbar.chips.screenrecord.ui.viewmodel.ScreenRecordChipViewModel
+import com.android.systemui.statusbar.chips.sharetoapp.ui.viewmodel.ShareToAppChipViewModel
+import com.android.systemui.statusbar.pipeline.shared.ui.model.ChipsVisibilityModel
+import com.android.systemui.util.kotlin.pairwise
+import javax.inject.Inject
+import kotlinx.coroutines.coroutineScope
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.launch
+
+/** Does all the UiEvent-related logging for the status bar chips. */
+@SysUISingleton
+class StatusBarChipsUiEventLogger @Inject constructor(private val logger: UiEventLogger) {
+ private val instanceIdSequence = InstanceIdSequence(INSTANCE_ID_MAX)
+
+ /** Get a new instance ID for a status bar chip. */
+ fun createNewInstanceId(): InstanceId {
+ return instanceIdSequence.newInstanceId()
+ }
+
+ /** Logs that the chip with the given ID was tapped to show additional information. */
+ fun logChipTapToShow(instanceId: InstanceId?) {
+ logger.log(StatusBarChipUiEvent.STATUS_BAR_CHIP_TAP_TO_SHOW, instanceId)
+ }
+
+ /**
+ * Logs that the chip with the given ID was tapped to hide the additional information that was
+ * previously shown.
+ */
+ fun logChipTapToHide(instanceId: InstanceId?) {
+ logger.log(StatusBarChipUiEvent.STATUS_BAR_CHIP_TAP_TO_HIDE, instanceId)
+ }
+
+ /** Starts UiEvent logging for the chips. */
+ suspend fun hydrateUiEventLogging(chipsFlow: Flow<ChipsVisibilityModel>) {
+ coroutineScope {
+ launch {
+ chipsFlow
+ .map { it.chips }
+ .distinctUntilChanged()
+ .pairwise()
+ .collect { (old, new) ->
+ val oldActive: Map<String, Pair<InstanceId?, Int>> =
+ old.active.withIndex().associate {
+ it.value.key to Pair(it.value.instanceId, it.index)
+ }
+ val newActive: Map<String, Pair<InstanceId?, Int>> =
+ new.active.withIndex().associate {
+ it.value.key to Pair(it.value.instanceId, it.index)
+ }
+
+ // Newly active keys
+ newActive.keys.minus(oldActive.keys).forEach { key ->
+ val uiEvent = key.getUiEventForNewChip()
+ val instanceId = newActive[key]!!.first
+ val position = newActive[key]!!.second
+ logger.logWithInstanceIdAndPosition(
+ uiEvent,
+ /* uid= */ 0,
+ /* packageName= */ null,
+ instanceId,
+ position,
+ )
+ }
+
+ // Newly inactive keys
+ oldActive.keys.minus(newActive.keys).forEach { key ->
+ val instanceId = oldActive[key]?.first
+ logger.log(StatusBarChipUiEvent.STATUS_BAR_CHIP_REMOVED, instanceId)
+ }
+ }
+ }
+ }
+ }
+
+ companion object {
+ private const val INSTANCE_ID_MAX = 1 shl 20
+
+ /**
+ * Given a key from an [OngoingActivityChipModel.Active] instance that was just added,
+ * return the right UiEvent type to log.
+ */
+ private fun String.getUiEventForNewChip(): StatusBarChipUiEvent {
+ return when {
+ this == ScreenRecordChipViewModel.KEY ->
+ StatusBarChipUiEvent.STATUS_BAR_NEW_CHIP_SCREEN_RECORD
+ this == ShareToAppChipViewModel.KEY ->
+ StatusBarChipUiEvent.STATUS_BAR_NEW_CHIP_SHARE_TO_APP
+ this == CastToOtherDeviceChipViewModel.KEY ->
+ StatusBarChipUiEvent.STATUS_BAR_NEW_CHIP_CAST_TO_OTHER_DEVICE
+ this.startsWith(CallChipViewModel.KEY_PREFIX) ->
+ StatusBarChipUiEvent.STATUS_BAR_NEW_CHIP_CALL
+ else -> StatusBarChipUiEvent.STATUS_BAR_NEW_CHIP_NOTIFICATION
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/ConnectivityModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/ConnectivityModule.kt
index 48f0245fd5db..4cf456d69b7b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/ConnectivityModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/ConnectivityModule.kt
@@ -32,25 +32,25 @@ import com.android.systemui.qs.tiles.HotspotTile
import com.android.systemui.qs.tiles.InternetTile
import com.android.systemui.qs.tiles.InternetTileNewImpl
import com.android.systemui.qs.tiles.NfcTile
-import com.android.systemui.qs.tiles.base.interactor.QSTileAvailabilityInteractor
-import com.android.systemui.qs.tiles.base.viewmodel.QSTileViewModelFactory
+import com.android.systemui.qs.tiles.base.domain.interactor.QSTileAvailabilityInteractor
+import com.android.systemui.qs.tiles.base.shared.model.QSTileConfig
+import com.android.systemui.qs.tiles.base.shared.model.QSTilePolicy
+import com.android.systemui.qs.tiles.base.shared.model.QSTileUIConfig
+import com.android.systemui.qs.tiles.base.ui.viewmodel.QSTileViewModel
+import com.android.systemui.qs.tiles.base.ui.viewmodel.QSTileViewModelFactory
import com.android.systemui.qs.tiles.dialog.InternetDetailsViewModel
-import com.android.systemui.qs.tiles.impl.airplane.domain.AirplaneModeMapper
import com.android.systemui.qs.tiles.impl.airplane.domain.interactor.AirplaneModeTileDataInteractor
import com.android.systemui.qs.tiles.impl.airplane.domain.interactor.AirplaneModeTileUserActionInteractor
import com.android.systemui.qs.tiles.impl.airplane.domain.model.AirplaneModeTileModel
-import com.android.systemui.qs.tiles.impl.internet.domain.InternetTileMapper
+import com.android.systemui.qs.tiles.impl.airplane.ui.mapper.AirplaneModeTileMapper
import com.android.systemui.qs.tiles.impl.internet.domain.interactor.InternetTileDataInteractor
import com.android.systemui.qs.tiles.impl.internet.domain.interactor.InternetTileUserActionInteractor
import com.android.systemui.qs.tiles.impl.internet.domain.model.InternetTileModel
-import com.android.systemui.qs.tiles.impl.saver.domain.DataSaverTileMapper
+import com.android.systemui.qs.tiles.impl.internet.ui.mapper.InternetTileMapper
import com.android.systemui.qs.tiles.impl.saver.domain.interactor.DataSaverTileDataInteractor
import com.android.systemui.qs.tiles.impl.saver.domain.interactor.DataSaverTileUserActionInteractor
import com.android.systemui.qs.tiles.impl.saver.domain.model.DataSaverTileModel
-import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
-import com.android.systemui.qs.tiles.viewmodel.QSTilePolicy
-import com.android.systemui.qs.tiles.viewmodel.QSTileUIConfig
-import com.android.systemui.qs.tiles.viewmodel.QSTileViewModel
+import com.android.systemui.qs.tiles.impl.saver.ui.mapper.DataSaverTileMapper
import com.android.systemui.res.R
import dagger.Binds
import dagger.Module
@@ -161,10 +161,10 @@ interface ConnectivityModule {
@StringKey(AIRPLANE_MODE_TILE_SPEC)
fun provideAirplaneModeTileViewModel(
factory: QSTileViewModelFactory.Static<AirplaneModeTileModel>,
- mapper: AirplaneModeMapper,
+ mapper: AirplaneModeTileMapper,
stateInteractor: AirplaneModeTileDataInteractor,
userActionInteractor: AirplaneModeTileUserActionInteractor,
- internetDetailsViewModelFactory: InternetDetailsViewModel.Factory
+ internetDetailsViewModelFactory: InternetDetailsViewModel.Factory,
): QSTileViewModel =
factory.create(
TileSpec.create(AIRPLANE_MODE_TILE_SPEC),
@@ -197,7 +197,7 @@ interface ConnectivityModule {
factory: QSTileViewModelFactory.Static<DataSaverTileModel>,
mapper: DataSaverTileMapper,
stateInteractor: DataSaverTileDataInteractor,
- userActionInteractor: DataSaverTileUserActionInteractor
+ userActionInteractor: DataSaverTileUserActionInteractor,
): QSTileViewModel =
factory.create(
TileSpec.create(DATA_SAVER_TILE_SPEC),
@@ -230,7 +230,7 @@ interface ConnectivityModule {
mapper: InternetTileMapper,
stateInteractor: InternetTileDataInteractor,
userActionInteractor: InternetTileUserActionInteractor,
- internetDetailsViewModelFactory: InternetDetailsViewModel.Factory
+ internetDetailsViewModelFactory: InternetDetailsViewModel.Factory,
): QSTileViewModel =
factory.create(
TileSpec.create(INTERNET_TILE_SPEC),
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/core/MultiDisplayStatusBarOrchestratorStore.kt b/packages/SystemUI/src/com/android/systemui/statusbar/core/MultiDisplayStatusBarOrchestratorStore.kt
index 7964950a2917..499e3ae2fa5b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/core/MultiDisplayStatusBarOrchestratorStore.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/core/MultiDisplayStatusBarOrchestratorStore.kt
@@ -16,10 +16,10 @@
package com.android.systemui.statusbar.core
+import com.android.app.displaylib.PerDisplayRepository
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.display.data.repository.DisplayRepository
-import com.android.systemui.display.data.repository.DisplayScopeRepository
import com.android.systemui.statusbar.data.repository.StatusBarModeRepositoryStore
import com.android.systemui.statusbar.data.repository.StatusBarPerDisplayStoreImpl
import com.android.systemui.statusbar.phone.AutoHideControllerStore
@@ -40,7 +40,7 @@ constructor(
private val statusBarModeRepositoryStore: StatusBarModeRepositoryStore,
private val initializerStore: StatusBarInitializerStore,
private val autoHideControllerStore: AutoHideControllerStore,
- private val displayScopeRepository: DisplayScopeRepository,
+ private val displayScopeRepository: PerDisplayRepository<CoroutineScope>,
private val statusBarWindowStateRepositoryStore: StatusBarWindowStateRepositoryStore,
) :
StatusBarPerDisplayStoreImpl<StatusBarOrchestrator>(
@@ -59,10 +59,10 @@ constructor(
val statusBarWindowController =
statusBarWindowControllerStore.forDisplay(displayId) ?: return null
val autoHideController = autoHideControllerStore.forDisplay(displayId) ?: return null
+ val displayScope = displayScopeRepository[displayId] ?: return null
return factory.create(
displayId,
- // TODO: b/398825844 - Handle nullness to prevent leaking CoroutineScope.
- displayScopeRepository.scopeForDisplay(displayId),
+ displayScope,
statusBarWindowStateRepositoryStore.forDisplay(displayId),
statusBarModeRepository,
statusBarInitializer,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/LightBarControllerStore.kt b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/LightBarControllerStore.kt
index 3c0d6c3b8df3..b257c2bfc29a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/LightBarControllerStore.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/LightBarControllerStore.kt
@@ -16,11 +16,11 @@
package com.android.systemui.statusbar.data.repository
+import com.android.app.displaylib.PerDisplayRepository
import com.android.systemui.CoreStartable
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.display.data.repository.DisplayRepository
-import com.android.systemui.display.data.repository.DisplayScopeRepository
import com.android.systemui.display.data.repository.PerDisplayStore
import com.android.systemui.statusbar.phone.LightBarController
import com.android.systemui.statusbar.phone.LightBarControllerImpl
@@ -41,7 +41,7 @@ constructor(
@Background backgroundApplicationScope: CoroutineScope,
displayRepository: DisplayRepository,
private val factory: LightBarControllerImpl.Factory,
- private val displayScopeRepository: DisplayScopeRepository,
+ private val displayScopeRepository: PerDisplayRepository<CoroutineScope>,
private val statusBarModeRepositoryStore: StatusBarModeRepositoryStore,
private val darkIconDispatcherStore: DarkIconDispatcherStore,
) :
@@ -55,13 +55,9 @@ constructor(
val darkIconDispatcher = darkIconDispatcherStore.forDisplay(displayId) ?: return null
val statusBarModePerDisplayRepository =
statusBarModeRepositoryStore.forDisplay(displayId) ?: return null
+ val displayScope = displayScopeRepository[displayId] ?: return null
return factory
- .create(
- displayId,
- displayScopeRepository.scopeForDisplay(displayId),
- darkIconDispatcher,
- statusBarModePerDisplayRepository,
- )
+ .create(displayId, displayScope, darkIconDispatcher, statusBarModePerDisplayRepository)
.also { it.start() }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/PrivacyDotViewControllerStore.kt b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/PrivacyDotViewControllerStore.kt
index 32dc8407ac90..f3c68553abfd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/PrivacyDotViewControllerStore.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/PrivacyDotViewControllerStore.kt
@@ -16,11 +16,11 @@
package com.android.systemui.statusbar.data.repository
+import com.android.app.displaylib.PerDisplayRepository
import com.android.systemui.CoreStartable
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.display.data.repository.DisplayRepository
-import com.android.systemui.display.data.repository.DisplayScopeRepository
import com.android.systemui.display.data.repository.PerDisplayStore
import com.android.systemui.display.data.repository.SingleDisplayStore
import com.android.systemui.statusbar.core.StatusBarConnectedDisplays
@@ -44,7 +44,7 @@ constructor(
@Background backgroundApplicationScope: CoroutineScope,
displayRepository: DisplayRepository,
private val factory: PrivacyDotViewControllerImpl.Factory,
- private val displayScopeRepository: DisplayScopeRepository,
+ private val displayScopeRepository: PerDisplayRepository<CoroutineScope>,
private val statusBarConfigurationControllerStore: StatusBarConfigurationControllerStore,
private val contentInsetsProviderStore: StatusBarContentInsetsProviderStore,
) :
@@ -58,11 +58,8 @@ constructor(
val configurationController =
statusBarConfigurationControllerStore.forDisplay(displayId) ?: return null
val contentInsetsProvider = contentInsetsProviderStore.forDisplay(displayId) ?: return null
- return factory.create(
- displayScopeRepository.scopeForDisplay(displayId),
- configurationController,
- contentInsetsProvider,
- )
+ val displayScope = displayScopeRepository[displayId] ?: return null
+ return factory.create(displayScope, configurationController, contentInsetsProvider)
}
override suspend fun onDisplayRemovalAction(instance: PrivacyDotViewController) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/PrivacyDotWindowControllerStore.kt b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/PrivacyDotWindowControllerStore.kt
index 7fc5e8abe904..587d80fc3848 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/PrivacyDotWindowControllerStore.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/PrivacyDotWindowControllerStore.kt
@@ -18,7 +18,6 @@ package com.android.systemui.statusbar.data.repository
import android.view.Display
import android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL
-import com.android.app.viewcapture.ViewCaptureAwareWindowManager
import com.android.systemui.CoreStartable
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
@@ -48,7 +47,6 @@ constructor(
private val windowControllerFactory: PrivacyDotWindowController.Factory,
private val displayWindowPropertiesRepository: DisplayWindowPropertiesRepository,
private val privacyDotViewControllerStore: PrivacyDotViewControllerStore,
- private val viewCaptureAwareWindowManagerFactory: ViewCaptureAwareWindowManager.Factory,
) :
PrivacyDotWindowControllerStore,
StatusBarPerDisplayStoreImpl<PrivacyDotWindowController>(
@@ -72,8 +70,7 @@ constructor(
return windowControllerFactory.create(
displayId = displayId,
privacyDotViewController = privacyDotViewController,
- viewCaptureAwareWindowManager =
- viewCaptureAwareWindowManagerFactory.create(displayWindowProperties.windowManager),
+ windowManager = displayWindowProperties.windowManager,
inflater = displayWindowProperties.layoutInflater,
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarPerDisplayConfigurationStateRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarPerDisplayConfigurationStateRepository.kt
index 3168a22c56ad..cb1002a83179 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarPerDisplayConfigurationStateRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarPerDisplayConfigurationStateRepository.kt
@@ -17,14 +17,14 @@
package com.android.systemui.statusbar.data.repository
import android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR
+import com.android.app.displaylib.PerDisplayInstanceProvider
+import com.android.app.displaylib.PerDisplayInstanceRepositoryImpl
+import com.android.app.displaylib.PerDisplayRepository
+import com.android.app.displaylib.SingleInstanceRepositoryImpl
import com.android.systemui.common.ui.ConfigurationState
import com.android.systemui.common.ui.ConfigurationStateImpl
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.display.data.repository.DisplayWindowPropertiesRepository
-import com.android.systemui.display.data.repository.PerDisplayInstanceProvider
-import com.android.systemui.display.data.repository.PerDisplayInstanceRepositoryImpl
-import com.android.systemui.display.data.repository.PerDisplayRepository
-import com.android.systemui.display.data.repository.SingleInstanceRepositoryImpl
import com.android.systemui.statusbar.core.StatusBarConnectedDisplays
import dagger.Lazy
import dagger.Module
@@ -39,7 +39,6 @@ constructor(
private val statusBarConfigurationControllerStore: StatusBarConfigurationControllerStore,
private val factory: ConfigurationStateImpl.Factory,
) : PerDisplayInstanceProvider<ConfigurationState> {
-
override fun createInstance(displayId: Int): ConfigurationState? {
val displayWindowProperties =
displayWindowPropertiesRepository.get(displayId, TYPE_STATUS_BAR) ?: return null
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotWindowController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotWindowController.kt
index e2bcfb752e6c..7999ca9fd3e9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotWindowController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotWindowController.kt
@@ -24,10 +24,10 @@ import android.view.DisplayCutout.BOUNDS_POSITION_RIGHT
import android.view.DisplayCutout.BOUNDS_POSITION_TOP
import android.view.LayoutInflater
import android.view.View
+import android.view.WindowManager
import android.view.WindowManager.InvalidDisplayException
import android.view.WindowManager.LayoutParams.WRAP_CONTENT
import android.widget.FrameLayout
-import com.android.app.viewcapture.ViewCaptureAwareWindowManager
import com.android.systemui.ScreenDecorations
import com.android.systemui.ScreenDecorationsThread
import com.android.systemui.decor.DecorProvider
@@ -54,7 +54,7 @@ class PrivacyDotWindowController
constructor(
@Assisted private val displayId: Int,
@Assisted private val privacyDotViewController: PrivacyDotViewController,
- @Assisted private val viewCaptureAwareWindowManager: ViewCaptureAwareWindowManager,
+ @Assisted private val windowManager: WindowManager,
@Assisted private val inflater: LayoutInflater,
@ScreenDecorationsThread private val uiExecutor: Executor,
private val dotFactory: PrivacyDotDecorProviderFactory,
@@ -106,7 +106,7 @@ constructor(
try {
// Wrapping this in a try/catch to avoid crashes when a display is instantly removed
// after being added, and initialization hasn't finished yet.
- viewCaptureAwareWindowManager.addView(rootView, params)
+ windowManager.addView(rootView, params)
} catch (e: InvalidDisplayException) {
Log.e(
TAG,
@@ -118,7 +118,7 @@ constructor(
}
fun stop() {
- dotViews.forEach { viewCaptureAwareWindowManager.removeView(it) }
+ dotViews.forEach { windowManager.removeView(it) }
}
@AssistedFactory
@@ -126,7 +126,7 @@ constructor(
fun create(
displayId: Int,
privacyDotViewController: PrivacyDotViewController,
- viewCaptureAwareWindowManager: ViewCaptureAwareWindowManager,
+ windowManager: WindowManager,
inflater: LayoutInflater,
): PrivacyDotWindowController
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/AssistantFeedbackController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/AssistantFeedbackController.java
index 11ec2edb36f6..e9a6ad26f5e9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/AssistantFeedbackController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/AssistantFeedbackController.java
@@ -108,13 +108,12 @@ public class AssistantFeedbackController {
/**
* Get the feedback status according to assistant's adjustments
*
- * @param entry Notification Entry to show feedback for
+ * @param ranking Ranking of the notification show feedback for
*/
- public int getFeedbackStatus(NotificationEntry entry) {
+ public int getFeedbackStatus(Ranking ranking) {
if (!isFeedbackEnabled()) {
return STATUS_UNCHANGED;
}
- Ranking ranking = entry.getRanking();
int oldImportance = ranking.getChannel().getImportance();
int newImportance = ranking.getImportance();
if (oldImportance < NotificationManager.IMPORTANCE_DEFAULT
@@ -138,11 +137,11 @@ public class AssistantFeedbackController {
* Get the feedback indicator image and content description resources according to assistant's
* changes on this notification's rank or importance.
*
- * @param entry Notification Entry to show feedback for
+ * @param ranking Ranking of the notification to show feedback for
*/
@Nullable
- public FeedbackIcon getFeedbackIcon(NotificationEntry entry) {
- int feedbackStatus = getFeedbackStatus(entry);
+ public FeedbackIcon getFeedbackIcon(Ranking ranking) {
+ int feedbackStatus = getFeedbackStatus(ranking);
return mIcons.get(feedbackStatus);
}
@@ -150,10 +149,10 @@ public class AssistantFeedbackController {
* Get the inline settings description resource according to assistant's changes on this
* notification's rank or importance.
*
- * @param entry Notification Entry to show feedback for
+ * @param ranking Ranking of the notification to show feedback for
*/
- public int getInlineDescriptionResource(NotificationEntry entry) {
- int feedbackStatus = getFeedbackStatus(entry);
+ public int getInlineDescriptionResource(Ranking ranking) {
+ int feedbackStatus = getFeedbackStatus(ranking);
switch (feedbackStatus) {
case STATUS_ALERTED:
return com.android.systemui.res.R.string.notification_channel_summary_automatic_alerted;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClicker.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClicker.java
index fcdcc3f698de..fed9417edd88 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClicker.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClicker.java
@@ -23,6 +23,7 @@ import android.view.View;
import com.android.systemui.DejankUtils;
import com.android.systemui.power.domain.interactor.PowerInteractor;
+import com.android.systemui.statusbar.notification.collection.EntryAdapter;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.shared.NotificationBundleUi;
@@ -44,13 +45,20 @@ public final class NotificationClicker implements View.OnClickListener {
private final Optional<Bubbles> mBubblesOptional;
private final NotificationActivityStarter mNotificationActivityStarter;
- private ExpandableNotificationRow.OnDragSuccessListener mOnDragSuccessListener =
- new ExpandableNotificationRow.OnDragSuccessListener() {
- @Override
- public void onDragSuccess(NotificationEntry entry) {
- mNotificationActivityStarter.onDragSuccess(entry);
- }
- };
+ private ExpandableNotificationRow.OnDragSuccessListener mOnDragSuccessListener
+ = new ExpandableNotificationRow.OnDragSuccessListener() {
+ @Override
+ public void onDragSuccess(NotificationEntry entry) {
+ NotificationBundleUi.assertInLegacyMode();
+ mNotificationActivityStarter.onDragSuccess(entry);
+ }
+
+ @Override
+ public void onDragSuccess(EntryAdapter entryAdapter) {
+ NotificationBundleUi.isUnexpectedlyInLegacyMode();
+ entryAdapter.onDragSuccess();
+ }
+ };
private NotificationClicker(
NotificationClickerLogger logger,
@@ -73,7 +81,6 @@ public final class NotificationClicker implements View.OnClickListener {
mPowerInteractor.wakeUpIfDozing("NOTIFICATION_CLICK", PowerManager.WAKE_REASON_GESTURE);
final ExpandableNotificationRow row = (ExpandableNotificationRow) v;
- final NotificationEntry entry = row.getEntry();
mLogger.logOnClick(row.getLoggingKey());
// Check if the notification is displaying the menu, if so slide notification back
@@ -101,16 +108,16 @@ public final class NotificationClicker implements View.OnClickListener {
DejankUtils.postAfterTraversal(() -> row.setJustClicked(false));
if (NotificationBundleUi.isEnabled()) {
- if (!row.getEntryAdapter().isBubbleCapable() && mBubblesOptional.isPresent()) {
+ if (!row.getEntryAdapter().isBubble() && mBubblesOptional.isPresent()) {
mBubblesOptional.get().collapseStack();
}
+ row.getEntryAdapter().onEntryClicked(row);
} else {
if (!row.getEntryLegacy().isBubble() && mBubblesOptional.isPresent()) {
mBubblesOptional.get().collapseStack();
}
+ mNotificationActivityStarter.onNotificationClicked(row.getEntryLegacy(), row);
}
-
- mNotificationActivityStarter.onNotificationClicked(entry, row);
}
private boolean isMenuVisible(ExpandableNotificationRow row) {
@@ -121,9 +128,12 @@ public final class NotificationClicker implements View.OnClickListener {
* Attaches the click listener to the row if appropriate.
*/
public void register(ExpandableNotificationRow row, StatusBarNotification sbn) {
+ boolean isBubble = NotificationBundleUi.isEnabled()
+ ? row.getEntryAdapter().isBubble()
+ : row.getEntryLegacy().isBubble();
Notification notification = sbn.getNotification();
if (notification.contentIntent != null || notification.fullScreenIntent != null
- || row.getEntry().isBubble()) {
+ || isBubble) {
if (NotificationBundleUi.isEnabled()) {
row.setBubbleClickListener(
v -> row.getEntryAdapter().onNotificationBubbleIconClicked());
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/OWNERS b/packages/SystemUI/src/com/android/systemui/statusbar/notification/OWNERS
index d06f24fdb81b..ba4001014681 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/OWNERS
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/OWNERS
@@ -17,3 +17,4 @@ valiiftime@google.com
yurilin@google.com
per-file MediaNotificationProcessor.java = ethibodeau@google.com
+per-file MagicActionBackgroundDrawable.kt = dupin@google.com
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/BundleEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/BundleEntry.java
index f6535730cf77..8fc6cbe7c9e7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/BundleEntry.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/BundleEntry.java
@@ -69,10 +69,13 @@ public class BundleEntry extends PipelineEntry {
return mUnmodifiableChildren;
}
+ void clearChildren() {
+ mChildren.clear();
+ }
+
/**
* @return Null because bundles do not have an associated NotificationEntry.
*/
-
@Nullable
@Override
public NotificationEntry getRepresentativeEntry() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/BundleEntryAdapter.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/BundleEntryAdapter.kt
index b1a26af336d8..6a3f8f166c34 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/BundleEntryAdapter.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/BundleEntryAdapter.kt
@@ -19,13 +19,20 @@ package com.android.systemui.statusbar.notification.collection
import android.app.Notification
import android.content.Context
import android.os.Build
+import android.service.notification.NotificationListenerService
import android.service.notification.StatusBarNotification
import android.util.Log
+import com.android.systemui.statusbar.notification.collection.notifcollection.NotifLifetimeExtender
+import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider
import com.android.systemui.statusbar.notification.icon.IconPack
+import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier.Companion.TYPE_NON_PERSON
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
import kotlinx.coroutines.flow.StateFlow
-class BundleEntryAdapter(val entry: BundleEntry) : EntryAdapter {
+class BundleEntryAdapter(
+ private val highPriorityProvider: HighPriorityProvider,
+ val entry: BundleEntry,
+) : EntryAdapter {
/** TODO (b/394483200): convert to PipelineEntry.ROOT_ENTRY when pipeline is migrated? */
override fun getParent(): GroupEntry {
return GroupEntry.ROOT_ENTRY
@@ -93,11 +100,46 @@ class BundleEntryAdapter(val entry: BundleEntry) : EntryAdapter {
return null
}
+ override fun getRanking(): NotificationListenerService.Ranking? {
+ return null
+ }
+
+ override fun endLifetimeExtension(
+ callback: NotifLifetimeExtender.OnEndLifetimeExtensionCallback?,
+ extender: NotifLifetimeExtender,
+ ) {
+ Log.wtf(TAG, "endLifetimeExtension() called")
+ }
+
+ override fun onImportanceChanged() {
+ Log.wtf(TAG, "onImportanceChanged() called")
+ }
+
+ override fun markForUserTriggeredMovement() {
+ Log.wtf(TAG, "markForUserTriggeredMovement() called")
+ }
+
+ override fun isMarkedForUserTriggeredMovement(): Boolean {
+ return false
+ }
+
+ override fun isHighPriority(): Boolean {
+ return highPriorityProvider.isHighPriority(entry)
+ }
+
+ override fun setInlineControlsShown(currentlyVisible: Boolean) {
+ // nothing to do, yet
+ }
+
+ override fun isBlockable(): Boolean {
+ return false
+ }
+
override fun canDragAndDrop(): Boolean {
return false
}
- override fun isBubbleCapable(): Boolean {
+ override fun isBubble(): Boolean {
return false
}
@@ -113,6 +155,10 @@ class BundleEntryAdapter(val entry: BundleEntry) : EntryAdapter {
return false
}
+ override fun getPeopleNotificationType(): Int {
+ return TYPE_NON_PERSON
+ }
+
override fun isPromotedOngoing(): Boolean {
return false
}
@@ -121,6 +167,11 @@ class BundleEntryAdapter(val entry: BundleEntry) : EntryAdapter {
return false
}
+ override fun onDragSuccess() {
+ // do nothing. these should not be draggable
+ Log.wtf(TAG, "onDragSuccess() called")
+ }
+
override fun onNotificationBubbleIconClicked() {
// do nothing. these cannot be a bubble
Log.wtf(TAG, "onNotificationBubbleIconClicked() called")
@@ -130,6 +181,16 @@ class BundleEntryAdapter(val entry: BundleEntry) : EntryAdapter {
// do nothing. these have no actions
Log.wtf(TAG, "onNotificationActionClicked() called")
}
+
+ override fun getDismissState(): NotificationEntry.DismissState {
+ // TODO(b/394483200): setDismissState is only called in NotifCollection so it does not
+ // work on bundles yet
+ return NotificationEntry.DismissState.NOT_DISMISSED
+ }
+
+ override fun onEntryClicked(row: ExpandableNotificationRow) {
+ // TODO(b/396446620): should anything happen when you click on a bundle?
+ }
}
private const val TAG = "BundleEntryAdapter"
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/EntryAdapter.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/EntryAdapter.java
index 4299825bd5e3..16d9c787d435 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/EntryAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/EntryAdapter.java
@@ -17,12 +17,15 @@
package com.android.systemui.statusbar.notification.collection;
import android.content.Context;
+import android.service.notification.NotificationListenerService;
import android.service.notification.StatusBarNotification;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import com.android.systemui.statusbar.notification.collection.notifcollection.NotifLifetimeExtender;
import com.android.systemui.statusbar.notification.icon.IconPack;
+import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import kotlinx.coroutines.flow.StateFlow;
@@ -122,9 +125,43 @@ public interface EntryAdapter {
@Nullable
StatusBarNotification getSbn();
+ /**
+ * Returns the ranking that backs this row, if present
+ */
+ @Nullable
+ NotificationListenerService.Ranking getRanking();
+
+ void endLifetimeExtension(
+ @Nullable NotifLifetimeExtender.OnEndLifetimeExtensionCallback callback,
+ @NonNull NotifLifetimeExtender extender);
+
+
+ void onImportanceChanged();
+
+ /**
+ * Use when a change has been made to the underlying object that will both rerank the object
+ * in the shade and change something about its appearance to delay the appearance change until
+ * the ranking reordering is likely to have settled.
+ */
+ void markForUserTriggeredMovement();
+
+ /**
+ * Determines whether a row is considered 'high priority'.
+ *
+ * Notifications that are high priority are visible on the lock screen/status bar and in the top
+ * section in the shade.
+ */
+ boolean isHighPriority();
+
+ boolean isMarkedForUserTriggeredMovement();
+
+ void setInlineControlsShown(boolean currentlyVisible);
+
+ boolean isBlockable();
+
boolean canDragAndDrop();
- boolean isBubbleCapable();
+ boolean isBubble();
@Nullable String getStyle();
@@ -132,6 +169,8 @@ public interface EntryAdapter {
boolean isAmbient();
+ @PeopleNotificationIdentifier.Companion.PeopleNotificationType int getPeopleNotificationType();
+
/**
* Returns whether this row represents promoted ongoing notification.
*/
@@ -141,6 +180,8 @@ public interface EntryAdapter {
return false;
}
+ void onDragSuccess();
+
/**
* Process a click on a notification bubble icon
*/
@@ -150,5 +191,9 @@ public interface EntryAdapter {
* Processes a click on a notification action
*/
void onNotificationActionClicked();
+
+ NotificationEntry.DismissState getDismissState();
+
+ void onEntryClicked(ExpandableNotificationRow row);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/EntryAdapterFactoryImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/EntryAdapterFactoryImpl.kt
index a5169865c3c9..2fce19c2becb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/EntryAdapterFactoryImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/EntryAdapterFactoryImpl.kt
@@ -19,6 +19,8 @@ package com.android.systemui.statusbar.notification.collection
import com.android.internal.logging.MetricsLogger
import com.android.systemui.statusbar.notification.NotificationActivityStarter
import com.android.systemui.statusbar.notification.collection.coordinator.VisualStabilityCoordinator
+import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider
+import com.android.systemui.statusbar.notification.headsup.HeadsUpManager
import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier
import com.android.systemui.statusbar.notification.row.NotificationActionClickManager
import com.android.systemui.statusbar.notification.row.icon.NotificationIconStyleProvider
@@ -34,6 +36,8 @@ constructor(
private val iconStyleProvider: NotificationIconStyleProvider,
private val visualStabilityCoordinator: VisualStabilityCoordinator,
private val notificationActionClickManager: NotificationActionClickManager,
+ private val highPriorityProvider: HighPriorityProvider,
+ private val headsUpManager: HeadsUpManager,
) : EntryAdapterFactory {
override fun create(entry: PipelineEntry): EntryAdapter {
return if (entry is NotificationEntry) {
@@ -44,10 +48,12 @@ constructor(
iconStyleProvider,
visualStabilityCoordinator,
notificationActionClickManager,
+ highPriorityProvider,
+ headsUpManager,
entry,
)
} else {
- BundleEntryAdapter((entry as BundleEntry))
+ BundleEntryAdapter(highPriorityProvider, (entry as BundleEntry))
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
index 4558017a98c8..c94289c3befa 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
@@ -68,6 +68,7 @@ import com.android.systemui.statusbar.notification.collection.notifcollection.No
import com.android.systemui.statusbar.notification.headsup.PinnedStatus;
import com.android.systemui.statusbar.notification.icon.IconPack;
import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel;
+import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModels;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRowController;
import com.android.systemui.statusbar.notification.row.NotificationGuts;
@@ -198,7 +199,7 @@ public final class NotificationEntry extends ListEntry {
// TODO(b/377565433): Move into NotificationContentModel during/after
// NotificationRowContentBinderRefactor.
- private PromotedNotificationContentModel mPromotedNotificationContentModel;
+ private PromotedNotificationContentModels mPromotedNotificationContentModels;
/**
* True if both
@@ -1029,7 +1030,7 @@ public final class NotificationEntry extends ListEntry {
}
/**
- * Mark this entry for movement triggered by a user action (ex: changing the priorirty of a
+ * Mark this entry for movement triggered by a user action (ex: changing the priority of a
* conversation). This can then be used for custom animations.
*/
public void markForUserTriggeredMovement(boolean marked) {
@@ -1106,9 +1107,9 @@ public final class NotificationEntry extends ListEntry {
* Gets the content needed to render this notification as a promoted notification on various
* surfaces (like status bar chips and AOD).
*/
- public PromotedNotificationContentModel getPromotedNotificationContentModel() {
+ public PromotedNotificationContentModels getPromotedNotificationContentModels() {
if (PromotedNotificationContentModel.featureFlagEnabled()) {
- return mPromotedNotificationContentModel;
+ return mPromotedNotificationContentModels;
} else {
Log.wtf(TAG, "getting promoted content without feature flag enabled", new Throwable());
return null;
@@ -1127,10 +1128,10 @@ public final class NotificationEntry extends ListEntry {
* Sets the content needed to render this notification as a promoted notification on various
* surfaces (like status bar chips and AOD).
*/
- public void setPromotedNotificationContentModel(
- @Nullable PromotedNotificationContentModel promotedNotificationContentModel) {
+ public void setPromotedNotificationContentModels(
+ @Nullable PromotedNotificationContentModels promotedNotificationContentModels) {
if (PromotedNotificationContentModel.featureFlagEnabled()) {
- this.mPromotedNotificationContentModel = promotedNotificationContentModel;
+ this.mPromotedNotificationContentModels = promotedNotificationContentModels;
} else {
Log.wtf(TAG, "setting promoted content without feature flag enabled", new Throwable());
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntryAdapter.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntryAdapter.kt
index 345b6aae9673..339a999e1535 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntryAdapter.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntryAdapter.kt
@@ -17,10 +17,15 @@
package com.android.systemui.statusbar.notification.collection
import android.content.Context
+import android.os.SystemClock
+import android.service.notification.NotificationListenerService
import android.service.notification.StatusBarNotification
import com.android.internal.logging.MetricsLogger
import com.android.systemui.statusbar.notification.NotificationActivityStarter
import com.android.systemui.statusbar.notification.collection.coordinator.VisualStabilityCoordinator
+import com.android.systemui.statusbar.notification.collection.notifcollection.NotifLifetimeExtender
+import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider
+import com.android.systemui.statusbar.notification.headsup.HeadsUpManager
import com.android.systemui.statusbar.notification.icon.IconPack
import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
@@ -35,6 +40,8 @@ class NotificationEntryAdapter(
private val iconStyleProvider: NotificationIconStyleProvider,
private val visualStabilityCoordinator: VisualStabilityCoordinator,
private val notificationActionClickManager: NotificationActionClickManager,
+ private val highPriorityProvider: HighPriorityProvider,
+ private val headsUpManager: HeadsUpManager,
private val entry: NotificationEntry,
) : EntryAdapter {
@@ -110,6 +117,41 @@ class NotificationEntryAdapter(
return entry.sbn
}
+ override fun getRanking(): NotificationListenerService.Ranking? {
+ return entry.ranking
+ }
+
+ override fun endLifetimeExtension(
+ callback: NotifLifetimeExtender.OnEndLifetimeExtensionCallback?,
+ extender: NotifLifetimeExtender,
+ ) {
+ callback?.onEndLifetimeExtension(extender, entry)
+ }
+
+ override fun onImportanceChanged() {
+ visualStabilityCoordinator.temporarilyAllowSectionChanges(entry, SystemClock.uptimeMillis())
+ }
+
+ override fun markForUserTriggeredMovement() {
+ entry.markForUserTriggeredMovement(true)
+ }
+
+ override fun isMarkedForUserTriggeredMovement(): Boolean {
+ return entry.isMarkedForUserTriggeredMovement
+ }
+
+ override fun isHighPriority(): Boolean {
+ return highPriorityProvider.isHighPriority(entry)
+ }
+
+ override fun setInlineControlsShown(currentlyVisible: Boolean) {
+ headsUpManager.setGutsShown(entry, currentlyVisible)
+ }
+
+ override fun isBlockable(): Boolean {
+ return entry.isBlockable
+ }
+
override fun canDragAndDrop(): Boolean {
val canBubble: Boolean = entry.canBubble()
val notif = entry.sbn.notification
@@ -121,7 +163,7 @@ class NotificationEntryAdapter(
return false
}
- override fun isBubbleCapable(): Boolean {
+ override fun isBubble(): Boolean {
return entry.isBubble
}
@@ -137,6 +179,10 @@ class NotificationEntryAdapter(
return entry.ranking.isAmbient
}
+ override fun getPeopleNotificationType(): Int {
+ return peopleNotificationIdentifier.getPeopleNotificationType(entry)
+ }
+
override fun isPromotedOngoing(): Boolean {
return entry.isPromotedOngoing
}
@@ -145,6 +191,10 @@ class NotificationEntryAdapter(
return entry.sbn.notification.fullScreenIntent != null
}
+ override fun onDragSuccess() {
+ notificationActivityStarter.onDragSuccess(entry)
+ }
+
override fun onNotificationBubbleIconClicked() {
notificationActivityStarter.onNotificationBubbleIconClicked(entry)
}
@@ -152,4 +202,12 @@ class NotificationEntryAdapter(
override fun onNotificationActionClicked() {
notificationActionClickManager.onNotificationActionClicked(entry)
}
+
+ override fun getDismissState(): NotificationEntry.DismissState {
+ return entry.dismissState
+ }
+
+ override fun onEntryClicked(row: ExpandableNotificationRow) {
+ notificationActivityStarter.onNotificationClicked(entry, row)
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java
index 5cea82140692..fe2bd345d559 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java
@@ -567,7 +567,7 @@ public class ShadeListBuilder implements Dumpable, PipelineDumpable {
for (BundleEntry be : mIdToBundleEntry.values()) {
be.beginNewAttachState();
- // TODO(b/399736937) Clear bundle children
+ be.clearChildren();
// BundleEntry has not representative summary so we do not need to clear it here.
}
mNotifList.clear();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ColorizedFgsCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ColorizedFgsCoordinator.java
index afba85b49c30..c8ec85d0a227 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ColorizedFgsCoordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ColorizedFgsCoordinator.java
@@ -24,10 +24,9 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.android.systemui.dagger.qualifiers.Application;
-import com.android.systemui.statusbar.notification.collection.ListEntry;
-import com.android.systemui.statusbar.notification.collection.PipelineEntry;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.collection.PipelineEntry;
import com.android.systemui.statusbar.notification.collection.coordinator.dagger.CoordinatorScope;
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifComparator;
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifPromoter;
@@ -100,7 +99,7 @@ public class ColorizedFgsCoordinator implements Coordinator {
public boolean isInSection(PipelineEntry entry) {
NotificationEntry notificationEntry = entry.getRepresentativeEntry();
if (notificationEntry != null) {
- return isRichOngoing(notificationEntry);
+ return isRichOngoing(notificationEntry) || isPromotedNotifChip(notificationEntry);
}
return false;
}
@@ -159,4 +158,10 @@ public class ColorizedFgsCoordinator implements Coordinator {
return entry.getImportance() > IMPORTANCE_MIN
&& notification.isStyle(Notification.CallStyle.class);
}
+
+ private boolean isPromotedNotifChip(NotificationEntry entry) {
+ return PromotedNotificationUi.isEnabled()
+ && entry.getImportance() > IMPORTANCE_MIN
+ && mOrderedPromotedNotifKeys.contains(entry.getKey());
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/GroupCountCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/GroupCountCoordinator.kt
index 2f0701f96f28..3747aba3a109 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/GroupCountCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/GroupCountCoordinator.kt
@@ -17,6 +17,7 @@
package com.android.systemui.statusbar.notification.collection.coordinator
import android.util.ArrayMap
+import com.android.systemui.statusbar.notification.collection.BundleEntry
import com.android.systemui.statusbar.notification.collection.GroupEntry
import com.android.systemui.statusbar.notification.collection.PipelineEntry
import com.android.systemui.statusbar.notification.collection.NotifPipeline
@@ -37,9 +38,17 @@ class GroupCountCoordinator @Inject constructor() : Coordinator {
private fun onBeforeFinalizeFilter(entries: List<PipelineEntry>) {
// save untruncated child counts to our internal map
untruncatedChildCounts.clear()
- entries.asSequence().filterIsInstance<GroupEntry>().forEach { groupEntry ->
- untruncatedChildCounts[groupEntry] = groupEntry.children.size
- }
+ entries.asSequence()
+ .flatMap { entry ->
+ when (entry) {
+ is GroupEntry -> listOf(entry)
+ is BundleEntry -> entry.children.filterIsInstance<GroupEntry>()
+ else -> emptyList()
+ }
+ }
+ .forEach { groupEntry ->
+ untruncatedChildCounts[groupEntry] = groupEntry.children.size
+ }
}
private fun onAfterRenderGroup(group: GroupEntry, controller: NotifGroupController) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/GutsCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/GutsCoordinator.kt
index 9045aac6ea6f..754f89191684 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/GutsCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/GutsCoordinator.kt
@@ -18,9 +18,10 @@ package com.android.systemui.statusbar.notification.collection.coordinator
import android.util.ArraySet
import com.android.systemui.Dumpable
import com.android.systemui.dump.DumpManager
-import com.android.systemui.statusbar.notification.collection.PipelineEntry
+import com.android.systemui.statusbar.notification.collection.EntryAdapter
import com.android.systemui.statusbar.notification.collection.NotifPipeline
import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import com.android.systemui.statusbar.notification.collection.PipelineEntry
import com.android.systemui.statusbar.notification.collection.coordinator.dagger.CoordinatorScope
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifLifetimeExtender
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifLifetimeExtender.OnEndLifetimeExtensionCallback
@@ -28,6 +29,7 @@ import com.android.systemui.statusbar.notification.collection.render.NotifGutsVi
import com.android.systemui.statusbar.notification.collection.render.NotifGutsViewManager
import com.android.systemui.statusbar.notification.row.NotificationGuts
import com.android.systemui.statusbar.notification.row.NotificationGutsManager
+import com.android.systemui.statusbar.notification.shared.NotificationBundleUi
import com.android.systemui.util.asIndenting
import com.android.systemui.util.printCollection
import com.android.systemui.util.println
@@ -38,23 +40,25 @@ import javax.inject.Inject
private const val TAG = "GutsCoordinator"
/**
- * Coordinates the guts displayed by the [NotificationGutsManager] with the pipeline.
- * Specifically, this just adds the lifetime extension necessary to keep guts from disappearing.
+ * Coordinates the guts displayed by the [NotificationGutsManager] with the pipeline. Specifically,
+ * this just adds the lifetime extension necessary to keep guts from disappearing.
*/
@CoordinatorScope
-class GutsCoordinator @Inject constructor(
+class GutsCoordinator
+@Inject
+constructor(
private val notifGutsViewManager: NotifGutsViewManager,
private val logger: GutsCoordinatorLogger,
- dumpManager: DumpManager
+ dumpManager: DumpManager,
) : Coordinator, Dumpable {
- /** Keys of any Notifications for which we've been told the guts are open */
+ /** Keys of any Notifications for which we've been told the guts are open */
private val notifsWithOpenGuts = ArraySet<String>()
- /** Keys of any Notifications we've extended the lifetime for, based on guts */
+ /** Keys of any Notifications we've extended the lifetime for, based on guts */
private val notifsExtendingLifetime = ArraySet<String>()
- /** Callback for ending lifetime extension */
+ /** Callback for ending lifetime extension */
private var onEndLifetimeExtensionCallback: OnEndLifetimeExtensionCallback? = null
init {
@@ -66,55 +70,77 @@ class GutsCoordinator @Inject constructor(
pipeline.addNotificationLifetimeExtender(mLifetimeExtender)
}
- override fun dump(pw: PrintWriter, args: Array<String>) = pw.asIndenting().run {
- withIncreasedIndent {
- printCollection("notifsWithOpenGuts", notifsWithOpenGuts)
- printCollection("notifsExtendingLifetime", notifsExtendingLifetime)
- println("onEndLifetimeExtensionCallback", onEndLifetimeExtensionCallback)
+ override fun dump(pw: PrintWriter, args: Array<String>) =
+ pw.asIndenting().run {
+ withIncreasedIndent {
+ printCollection("notifsWithOpenGuts", notifsWithOpenGuts)
+ printCollection("notifsExtendingLifetime", notifsExtendingLifetime)
+ println("onEndLifetimeExtensionCallback", onEndLifetimeExtensionCallback)
+ }
}
- }
- private val mLifetimeExtender: NotifLifetimeExtender = object : NotifLifetimeExtender {
- override fun getName(): String {
- return TAG
- }
+ private val mLifetimeExtender: NotifLifetimeExtender =
+ object : NotifLifetimeExtender {
+ override fun getName(): String {
+ return TAG
+ }
- override fun setCallback(callback: OnEndLifetimeExtensionCallback) {
- onEndLifetimeExtensionCallback = callback
- }
+ override fun setCallback(callback: OnEndLifetimeExtensionCallback) {
+ onEndLifetimeExtensionCallback = callback
+ }
- override fun maybeExtendLifetime(entry: NotificationEntry, reason: Int): Boolean {
- val isShowingGuts = isCurrentlyShowingGuts(entry)
- if (isShowingGuts) {
- notifsExtendingLifetime.add(entry.key)
+ override fun maybeExtendLifetime(entry: NotificationEntry, reason: Int): Boolean {
+ val isShowingGuts = isCurrentlyShowingGuts(entry)
+ if (isShowingGuts) {
+ notifsExtendingLifetime.add(entry.key)
+ }
+ return isShowingGuts
}
- return isShowingGuts
- }
- override fun cancelLifetimeExtension(entry: NotificationEntry) {
- notifsExtendingLifetime.remove(entry.key)
+ override fun cancelLifetimeExtension(entry: NotificationEntry) {
+ notifsExtendingLifetime.remove(entry.key)
+ }
}
- }
- private val mGutsListener: NotifGutsViewListener = object : NotifGutsViewListener {
- override fun onGutsOpen(entry: NotificationEntry, guts: NotificationGuts) {
- logger.logGutsOpened(entry.key, guts)
- if (guts.isLeavebehind) {
- // leave-behind guts should not extend the lifetime of the notification
+ private val mGutsListener: NotifGutsViewListener =
+ object : NotifGutsViewListener {
+ override fun onGutsOpen(entry: NotificationEntry, guts: NotificationGuts) {
+ NotificationBundleUi.assertInLegacyMode()
+ logger.logGutsOpened(entry.key, guts)
+ if (guts.isLeavebehind) {
+ // leave-behind guts should not extend the lifetime of the notification
+ closeGutsAndEndLifetimeExtension(entry)
+ } else {
+ notifsWithOpenGuts.add(entry.key)
+ }
+ }
+
+ override fun onGutsClose(entry: NotificationEntry) {
+ NotificationBundleUi.assertInLegacyMode()
+ logger.logGutsClosed(entry.key)
closeGutsAndEndLifetimeExtension(entry)
- } else {
- notifsWithOpenGuts.add(entry.key)
}
- }
- override fun onGutsClose(entry: NotificationEntry) {
- logger.logGutsClosed(entry.key)
- closeGutsAndEndLifetimeExtension(entry)
+ override fun onGutsOpen(entryAdapter: EntryAdapter, guts: NotificationGuts) {
+ NotificationBundleUi.isUnexpectedlyInLegacyMode()
+ logger.logGutsOpened(entryAdapter.key, guts)
+ if (guts.isLeavebehind) {
+ // leave-behind guts should not extend the lifetime of the notification
+ closeGutsAndEndLifetimeExtension(entryAdapter)
+ } else {
+ notifsWithOpenGuts.add(entryAdapter.key)
+ }
+ }
+
+ override fun onGutsClose(entryAdapter: EntryAdapter) {
+ NotificationBundleUi.isUnexpectedlyInLegacyMode()
+ logger.logGutsClosed(entryAdapter.key)
+ closeGutsAndEndLifetimeExtension(entryAdapter)
+ }
}
- }
private fun isCurrentlyShowingGuts(entry: PipelineEntry) =
- notifsWithOpenGuts.contains(entry.key)
+ notifsWithOpenGuts.contains(entry.key)
private fun closeGutsAndEndLifetimeExtension(entry: NotificationEntry) {
notifsWithOpenGuts.remove(entry.key)
@@ -122,4 +148,11 @@ class GutsCoordinator @Inject constructor(
onEndLifetimeExtensionCallback?.onEndLifetimeExtension(mLifetimeExtender, entry)
}
}
+
+ private fun closeGutsAndEndLifetimeExtension(entryAdapter: EntryAdapter) {
+ notifsWithOpenGuts.remove(entryAdapter.key)
+ if (notifsExtendingLifetime.remove(entryAdapter.key)) {
+ entryAdapter.endLifetimeExtension(onEndLifetimeExtensionCallback, mLifetimeExtender)
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.kt
index a0eab43f854b..87733725e133 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.kt
@@ -20,11 +20,13 @@ import android.app.Notification.GROUP_ALERT_SUMMARY
import android.util.ArrayMap
import android.util.ArraySet
import com.android.internal.annotations.VisibleForTesting
+import com.android.systemui.Flags.notificationSkipSilentUpdates
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.statusbar.NotificationRemoteInputManager
import com.android.systemui.statusbar.chips.notification.domain.interactor.StatusBarNotificationChipsInteractor
import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips
+import com.android.systemui.statusbar.chips.uievents.StatusBarChipsUiEventLogger
import com.android.systemui.statusbar.notification.NotifPipelineFlags
import com.android.systemui.statusbar.notification.collection.BundleEntry
import com.android.systemui.statusbar.notification.collection.GroupEntry
@@ -89,6 +91,7 @@ constructor(
private val mLaunchFullScreenIntentProvider: LaunchFullScreenIntentProvider,
private val mFlags: NotifPipelineFlags,
private val statusBarNotificationChipsInteractor: StatusBarNotificationChipsInteractor,
+ private val statusBarChipsUiEventLogger: StatusBarChipsUiEventLogger,
@IncomingHeader private val mIncomingHeaderController: NodeController,
@Main private val mExecutor: DelayableExecutor,
) : Coordinator {
@@ -144,6 +147,14 @@ constructor(
// not just any notification.
val isCurrentlyHeadsUp = mHeadsUpManager.isHeadsUpEntry(entry.key)
+
+ if (isCurrentlyHeadsUp) {
+ // If the chip's notif is currently showing as heads up, then we'll stop showing it.
+ statusBarChipsUiEventLogger.logChipTapToHide(entry.sbn.instanceId)
+ } else {
+ statusBarChipsUiEventLogger.logChipTapToShow(entry.sbn.instanceId)
+ }
+
val posted =
PostedEntry(
entry,
@@ -465,15 +476,34 @@ constructor(
}
hunMutator.updateNotification(posted.key, pinnedStatus)
}
- } else {
+ } else { // shouldHeadsUpEver = false
if (posted.isHeadsUpEntry) {
- // We don't want this to be interrupting anymore, let's remove it
- // If the notification is pinned by the user, the only way a user can un-pin
- // it is by tapping the status bar notification chip. Since that's a clear
- // user action, we should remove the HUN immediately instead of waiting for
- // any sort of minimum timeout.
- val shouldRemoveImmediately = posted.isPinnedByUser
- hunMutator.removeNotification(posted.key, shouldRemoveImmediately)
+ if (notificationSkipSilentUpdates()) {
+ if (posted.isPinnedByUser) {
+ // We don't want this to be interrupting anymore, let's remove it
+ // If the notification is pinned by the user, the only way a user
+ // can un-pin it by tapping the status bar notification chip. Since
+ // that's a clear user action, we should remove the HUN immediately
+ // instead of waiting for any sort of minimum timeout.
+ // TODO(b/401068530) Ensure that status bar chip HUNs are not
+ // removed for silent update
+ hunMutator.removeNotification(
+ posted.key,
+ /* releaseImmediately= */ true,
+ )
+ } else {
+ // Do NOT remove HUN for non-user update.
+ // Let the HUN show for its remaining duration.
+ }
+ } else {
+ // We don't want this to be interrupting anymore, let's remove it
+ // If the notification is pinned by the user, the only way a user can
+ // un-pin it is by tapping the status bar notification chip. Since
+ // that's a clear user action, we should remove the HUN immediately
+ // instead of waiting for any sort of minimum timeout.
+ val shouldRemoveImmediately = posted.isPinnedByUser
+ hunMutator.removeNotification(posted.key, shouldRemoveImmediately)
+ }
} else {
// Don't let the bind finish
cancelHeadsUpBind(posted.entry)
@@ -573,24 +603,37 @@ constructor(
isBinding = isBinding,
)
}
- // Handle cancelling heads up here, rather than in the OnBeforeFinalizeFilter, so
- // that
- // work can be done before the ShadeListBuilder is run. This prevents re-entrant
- // behavior between this Coordinator, HeadsUpManager, and VisualStabilityManager.
- if (posted?.shouldHeadsUpEver == false) {
- if (posted.isHeadsUpEntry) {
- // We don't want this to be interrupting anymore, let's remove it
- mHeadsUpManager.removeNotification(
- posted.key,
- /* removeImmediately= */ false,
- "onEntryUpdated",
- )
- } else if (posted.isBinding) {
+ if (notificationSkipSilentUpdates()) {
+ // TODO(b/403703828) Move canceling to OnBeforeFinalizeFilter, since we are not
+ // removing from HeadsUpManager and don't need to deal with re-entrant behavior
+ // between HeadsUpCoordinator, HeadsUpManager, and VisualStabilityManager.
+ if (
+ posted?.shouldHeadsUpEver == false &&
+ !posted.isHeadsUpEntry &&
+ posted.isBinding
+ ) {
// Don't let the bind finish
cancelHeadsUpBind(posted.entry)
}
+ } else {
+ // Handle cancelling heads up here, rather than in the OnBeforeFinalizeFilter,
+ // so that work can be done before the ShadeListBuilder is run. This prevents
+ // re-entrant behavior between this Coordinator, HeadsUpManager, and
+ // VisualStabilityManager.
+ if (posted?.shouldHeadsUpEver == false) {
+ if (posted.isHeadsUpEntry) {
+ // We don't want this to be interrupting anymore, let's remove it
+ mHeadsUpManager.removeNotification(
+ posted.key,
+ /* removeImmediately= */ false,
+ "onEntryUpdated",
+ )
+ } else if (posted.isBinding) {
+ // Don't let the bind finish
+ cancelHeadsUpBind(posted.entry)
+ }
+ }
}
-
// Update last updated time for this entry
setUpdateTime(entry, mSystemClock.currentTimeMillis())
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java
index 1be415d7bf47..20169ef481bf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java
@@ -36,6 +36,7 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.statusbar.IStatusBarService;
import com.android.systemui.statusbar.notification.collection.BundleEntry;
import com.android.systemui.statusbar.notification.collection.GroupEntry;
+import com.android.systemui.statusbar.notification.collection.ListEntry;
import com.android.systemui.statusbar.notification.collection.PipelineEntry;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
@@ -307,8 +308,15 @@ public class PreparationCoordinator implements Coordinator {
private void inflateAllRequiredViews(List<PipelineEntry> entries) {
for (int i = 0, size = entries.size(); i < size; i++) {
PipelineEntry entry = entries.get(i);
- if (NotificationBundleUi.isEnabled() && entry instanceof BundleEntry) {
- // TODO(b/399738511) Inflate bundle views.
+ if (NotificationBundleUi.isEnabled() && entry instanceof BundleEntry bundleEntry) {
+ for (ListEntry listEntry : bundleEntry.getChildren()) {
+ if (listEntry instanceof GroupEntry groupEntry) {
+ inflateRequiredGroupViews(groupEntry);
+ } else {
+ NotificationEntry notifEntry = (NotificationEntry) listEntry;
+ inflateRequiredNotifViews(notifEntry);
+ }
+ }
} else if (entry instanceof GroupEntry) {
GroupEntry groupEntry = (GroupEntry) entry;
inflateRequiredGroupViews(groupEntry);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RowAppearanceCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RowAppearanceCoordinator.kt
index a987c544bb50..341cc09aafc1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RowAppearanceCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RowAppearanceCoordinator.kt
@@ -20,9 +20,9 @@ import android.content.Context
import com.android.systemui.res.R
import com.android.systemui.shade.ShadeDisplayAware
import com.android.systemui.statusbar.notification.AssistantFeedbackController
-import com.android.systemui.statusbar.notification.collection.PipelineEntry
import com.android.systemui.statusbar.notification.collection.NotifPipeline
import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import com.android.systemui.statusbar.notification.collection.PipelineEntry
import com.android.systemui.statusbar.notification.collection.coordinator.dagger.CoordinatorScope
import com.android.systemui.statusbar.notification.collection.provider.SectionStyleProvider
import com.android.systemui.statusbar.notification.collection.render.NotifRowController
@@ -79,6 +79,6 @@ internal constructor(
(mAutoExpandFirstNotification && entry == entryToExpand)
)
// Show/hide the feedback icon
- controller.setFeedbackIcon(mAssistantFeedbackController.getFeedbackIcon(entry))
+ controller.setFeedbackIcon(mAssistantFeedbackController.getFeedbackIcon(entry.ranking))
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/OnUserInteractionCallbackImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/OnUserInteractionCallbackImpl.java
index 03b4076ba6fb..ca3d2e2a2a35 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/OnUserInteractionCallbackImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/OnUserInteractionCallbackImpl.java
@@ -35,6 +35,7 @@ import com.android.systemui.statusbar.notification.collection.notifcollection.Di
import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider;
import com.android.systemui.statusbar.notification.row.OnUserInteractionCallback;
import com.android.systemui.statusbar.notification.headsup.HeadsUpManager;
+import com.android.systemui.statusbar.notification.shared.NotificationBundleUi;
import javax.inject.Inject;
@@ -83,6 +84,7 @@ public class OnUserInteractionCallbackImpl implements OnUserInteractionCallback
@Override
public void onImportanceChanged(NotificationEntry entry) {
+ NotificationBundleUi.assertInLegacyMode();
mVisualStabilityCoordinator.temporarilyAllowSectionChanges(
entry,
UseElapsedRealtimeForCreationTime.getCurrentTime());
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NotifGutsViewListener.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NotifGutsViewListener.kt
index 129f6b1750e6..9e49083e4d04 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NotifGutsViewListener.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NotifGutsViewListener.kt
@@ -15,16 +15,21 @@
*/
package com.android.systemui.statusbar.notification.collection.render
+import com.android.systemui.statusbar.notification.collection.EntryAdapter
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.row.NotificationGuts
-/**
- * Interface for listening to guts open and close events.
- */
+/** Interface for listening to guts open and close events. */
interface NotifGutsViewListener {
/** A notification's guts are being opened */
fun onGutsOpen(entry: NotificationEntry, guts: NotificationGuts)
/** A notification's guts are being closed */
fun onGutsClose(entry: NotificationEntry)
+
+ /** A notification's guts are being opened */
+ fun onGutsOpen(entryAdapter: EntryAdapter, guts: NotificationGuts)
+
+ /** A notification's guts are being closed */
+ fun onGutsClose(entryAdapter: EntryAdapter)
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
index 1e5aa01714be..6042bff4bb97 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
@@ -83,7 +83,6 @@ import com.android.systemui.statusbar.notification.logging.dagger.NotificationsL
import com.android.systemui.statusbar.notification.promoted.PromotedNotificationContentExtractor;
import com.android.systemui.statusbar.notification.promoted.PromotedNotificationContentExtractorImpl;
import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel;
-import com.android.systemui.statusbar.notification.row.NotificationActionClickManager;
import com.android.systemui.statusbar.notification.row.NotificationEntryProcessorFactory;
import com.android.systemui.statusbar.notification.row.NotificationEntryProcessorFactoryLooperImpl;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
@@ -325,7 +324,7 @@ public interface NotificationsModule {
if (PromotedNotificationContentModel.featureFlagEnabled()) {
return implProvider.get();
} else {
- return (entry, recoveredBuilder, imageModelProvider) -> null;
+ return (entry, recoveredBuilder, redactionType, imageModelProvider) -> null;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/RenderNotificationListInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/RenderNotificationListInteractor.kt
index adc049e7cdf1..9f42a1d9b145 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/RenderNotificationListInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/RenderNotificationListInteractor.kt
@@ -20,21 +20,24 @@ import android.app.Notification.CallStyle.CALL_TYPE_ONGOING
import android.app.Notification.CallStyle.CALL_TYPE_SCREENING
import android.app.Notification.CallStyle.CALL_TYPE_UNKNOWN
import android.app.Notification.EXTRA_CALL_TYPE
+import android.app.Notification.FLAG_ONGOING_EVENT
import android.app.PendingIntent
import android.content.Context
import android.graphics.drawable.Icon
import android.service.notification.StatusBarNotification
import android.util.ArrayMap
import com.android.app.tracing.traceSection
+import com.android.internal.logging.InstanceId
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.statusbar.StatusBarIconView
import com.android.systemui.statusbar.notification.collection.GroupEntry
-import com.android.systemui.statusbar.notification.collection.PipelineEntry
import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import com.android.systemui.statusbar.notification.collection.PipelineEntry
import com.android.systemui.statusbar.notification.collection.provider.SectionStyleProvider
import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationListRepository
import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationsStore
import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel
+import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModels
import com.android.systemui.statusbar.notification.shared.ActiveNotificationEntryModel
import com.android.systemui.statusbar.notification.shared.ActiveNotificationGroupModel
import com.android.systemui.statusbar.notification.shared.ActiveNotificationModel
@@ -140,7 +143,7 @@ private class ActiveNotificationsStoreBuilder(
private fun NotificationEntry.toModel(): ActiveNotificationModel {
val promotedContent =
if (PromotedNotificationContentModel.featureFlagEnabled()) {
- promotedNotificationContentModel
+ promotedNotificationContentModels
} else {
null
}
@@ -149,6 +152,8 @@ private class ActiveNotificationsStoreBuilder(
key = key,
groupKey = sbn.groupKey,
whenTime = sbn.notification.`when`,
+ isForegroundService = sbn.notification.isForegroundService,
+ isOngoingEvent = (sbn.notification.flags and FLAG_ONGOING_EVENT) != 0,
isAmbient = sectionStyleProvider.isMinimized(this),
isRowDismissed = isRowDismissed,
isSilent = sectionStyleProvider.isSilent(this),
@@ -163,7 +168,7 @@ private class ActiveNotificationsStoreBuilder(
packageName = sbn.packageName,
appName = sbn.notification.loadHeaderAppName(context),
contentIntent = sbn.notification.contentIntent,
- instanceId = sbn.instanceId?.id,
+ instanceId = sbn.instanceId,
isGroupSummary = sbn.notification.isGroupSummary,
bucket = bucket,
callType = sbn.toCallType(),
@@ -176,6 +181,8 @@ private fun ActiveNotificationsStore.createOrReuse(
key: String,
groupKey: String?,
whenTime: Long,
+ isForegroundService: Boolean,
+ isOngoingEvent: Boolean,
isAmbient: Boolean,
isRowDismissed: Boolean,
isSilent: Boolean,
@@ -190,17 +197,19 @@ private fun ActiveNotificationsStore.createOrReuse(
packageName: String,
appName: String,
contentIntent: PendingIntent?,
- instanceId: Int?,
+ instanceId: InstanceId?,
isGroupSummary: Boolean,
bucket: Int,
callType: CallType,
- promotedContent: PromotedNotificationContentModel?,
+ promotedContent: PromotedNotificationContentModels?,
): ActiveNotificationModel {
return individuals[key]?.takeIf {
it.isCurrent(
key = key,
groupKey = groupKey,
whenTime = whenTime,
+ isForegroundService = isForegroundService,
+ isOngoingEvent = isOngoingEvent,
isAmbient = isAmbient,
isRowDismissed = isRowDismissed,
isSilent = isSilent,
@@ -226,6 +235,8 @@ private fun ActiveNotificationsStore.createOrReuse(
key = key,
groupKey = groupKey,
whenTime = whenTime,
+ isForegroundService = isForegroundService,
+ isOngoingEvent = isOngoingEvent,
isAmbient = isAmbient,
isRowDismissed = isRowDismissed,
isSilent = isSilent,
@@ -252,6 +263,8 @@ private fun ActiveNotificationModel.isCurrent(
key: String,
groupKey: String?,
whenTime: Long,
+ isForegroundService: Boolean,
+ isOngoingEvent: Boolean,
isAmbient: Boolean,
isRowDismissed: Boolean,
isSilent: Boolean,
@@ -266,16 +279,18 @@ private fun ActiveNotificationModel.isCurrent(
packageName: String,
appName: String,
contentIntent: PendingIntent?,
- instanceId: Int?,
+ instanceId: InstanceId?,
isGroupSummary: Boolean,
bucket: Int,
callType: CallType,
- promotedContent: PromotedNotificationContentModel?,
+ promotedContent: PromotedNotificationContentModels?,
): Boolean {
return when {
key != this.key -> false
groupKey != this.groupKey -> false
whenTime != this.whenTime -> false
+ isForegroundService != this.isForegroundService -> false
+ isOngoingEvent != this.isOngoingEvent -> false
isAmbient != this.isAmbient -> false
isRowDismissed != this.isRowDismissed -> false
isSilent != this.isSilent -> false
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterView.java
index d09546fe80ca..3caaf542e787 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterView.java
@@ -67,6 +67,7 @@ public class FooterView extends StackScrollerDecorView {
private FooterViewButton mSettingsButton;
private FooterViewButton mHistoryButton;
private boolean mShouldBeHidden;
+ private boolean mIsBlurSupported;
// Footer label
private TextView mSeenNotifsFooterTextView;
@@ -390,15 +391,20 @@ public class FooterView extends StackScrollerDecorView {
if (!notificationFooterBackgroundTintOptimization()) {
if (notificationShadeBlur()) {
- Color backgroundColor = Color.valueOf(
- SurfaceEffectColors.surfaceEffect1(getContext()));
- scHigh = ColorUtils.setAlphaComponent(backgroundColor.toArgb(), 0xFF);
- // Apply alpha on background drawables.
- int backgroundAlpha = (int) (backgroundColor.alpha() * 0xFF);
- clearAllBg.setAlpha(backgroundAlpha);
- settingsBg.setAlpha(backgroundAlpha);
- if (historyBg != null) {
- historyBg.setAlpha(backgroundAlpha);
+ if (mIsBlurSupported) {
+ Color backgroundColor = Color.valueOf(
+ SurfaceEffectColors.surfaceEffect1(getContext()));
+ scHigh = ColorUtils.setAlphaComponent(backgroundColor.toArgb(), 0xFF);
+ // Apply alpha on background drawables.
+ int backgroundAlpha = (int) (backgroundColor.alpha() * 0xFF);
+ clearAllBg.setAlpha(backgroundAlpha);
+ settingsBg.setAlpha(backgroundAlpha);
+ if (historyBg != null) {
+ historyBg.setAlpha(backgroundAlpha);
+ }
+ } else {
+ scHigh = mContext.getColor(
+ com.android.internal.R.color.materialColorSurfaceContainer);
}
} else {
scHigh = mContext.getColor(
@@ -438,6 +444,16 @@ public class FooterView extends StackScrollerDecorView {
}
}
+ public void setIsBlurSupported(boolean isBlurSupported) {
+ if (notificationShadeBlur()) {
+ if (mIsBlurSupported == isBlurSupported) {
+ return;
+ }
+ mIsBlurSupported = isBlurSupported;
+ updateColors();
+ }
+ }
+
@Override
@NonNull
public ExpandableViewState createExpandableViewState() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/viewbinder/FooterViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/viewbinder/FooterViewBinder.kt
index 3383ce98549f..3213754ca9a9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/viewbinder/FooterViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/viewbinder/FooterViewBinder.kt
@@ -20,6 +20,7 @@ import android.view.View
import androidx.lifecycle.lifecycleScope
import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.internal.jank.InteractionJankMonitor
+import com.android.systemui.Flags.notificationShadeBlur
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.statusbar.notification.NotificationActivityStarter
import com.android.systemui.statusbar.notification.NotificationActivityStarter.SettingsIntent
@@ -81,6 +82,14 @@ object FooterViewBinder {
launch { bindHistoryButton(footer, viewModel, notificationActivityStarter) }
}
launch { bindMessage(footer, viewModel) }
+
+ if (notificationShadeBlur()) {
+ launch {
+ viewModel.isBlurSupported.collect { supported ->
+ footer.setIsBlurSupported(supported)
+ }
+ }
+ }
}
private suspend fun bindClearAllButton(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterViewModel.kt
index c895c41960d4..c1fc72c618ba 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterViewModel.kt
@@ -31,6 +31,7 @@ import com.android.systemui.util.kotlin.sample
import com.android.systemui.util.ui.AnimatableEvent
import com.android.systemui.util.ui.AnimatedValue
import com.android.systemui.util.ui.toAnimatedValueFlow
+import com.android.systemui.window.domain.interactor.WindowRootViewBlurInteractor
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import kotlinx.coroutines.flow.Flow
@@ -48,6 +49,7 @@ constructor(
notificationSettingsInteractor: NotificationSettingsInteractor,
seenNotificationsInteractor: SeenNotificationsInteractor,
shadeInteractor: ShadeInteractor,
+ windowRootViewBlurInteractor: WindowRootViewBlurInteractor,
) {
/** A message to show instead of the footer buttons. */
val message: FooterMessageViewModel =
@@ -119,6 +121,8 @@ constructor(
}
}
+ val isBlurSupported = windowRootViewBlurInteractor.isBlurCurrentlySupported
+
private val manageOrHistoryButtonText: Flow<Int> =
notificationSettingsInteractor.isNotificationHistoryEnabled.map { shouldLaunchHistory ->
if (shouldLaunchHistory) R.string.manage_notifications_history_text
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerStatusBarViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerStatusBarViewBinder.kt
index 147a5afea306..619d48f8ba81 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerStatusBarViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerStatusBarViewBinder.kt
@@ -18,9 +18,9 @@ package com.android.systemui.statusbar.notification.icon.ui.viewbinder
import android.view.Display
import androidx.lifecycle.lifecycleScope
+import com.android.app.displaylib.PerDisplayRepository
import com.android.app.tracing.traceSection
import com.android.systemui.common.ui.ConfigurationState
-import com.android.systemui.display.data.repository.PerDisplayRepository
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.statusbar.notification.collection.NotifCollection
import com.android.systemui.statusbar.notification.icon.ui.viewbinder.NotificationIconContainerViewBinder.IconViewStore
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/AODPromotedNotification.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/AODPromotedNotification.kt
index 777392df67cc..fdbd75bd33ca 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/AODPromotedNotification.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/AODPromotedNotification.kt
@@ -226,7 +226,7 @@ private val PromotedNotificationContentModel.layoutResource: Int?
private class AODPromotedNotificationViewUpdater(root: View) {
private val alertedIcon: ImageView? = root.findViewById(R.id.alerted_icon)
private val alternateExpandTarget: View? = root.findViewById(R.id.alternate_expand_target)
- private val appNameDivider: View? = root.findViewById(R.id.app_name_divider)
+ private val appNameDivider: TextView? = root.findViewById(R.id.app_name_divider)
private val appNameText: TextView? = root.findViewById(R.id.app_name_text)
private val bigPicture: BigPictureNotificationImageView? = root.findViewById(R.id.big_picture)
private val bigText: ImageFloatingTextView? = root.findViewById(R.id.big_text)
@@ -241,9 +241,9 @@ private class AODPromotedNotificationViewUpdater(root: View) {
)
private val expandButton: NotificationExpandButton? = root.findViewById(R.id.expand_button)
private val headerText: TextView? = root.findViewById(R.id.header_text)
- private val headerTextDivider: View? = root.findViewById(R.id.header_text_divider)
+ private val headerTextDivider: TextView? = root.findViewById(R.id.header_text_divider)
private val headerTextSecondary: TextView? = root.findViewById(R.id.header_text_secondary)
- private val headerTextSecondaryDivider: View? =
+ private val headerTextSecondaryDivider: TextView? =
root.findViewById(R.id.header_text_secondary_divider)
private val icon: NotificationRowIconView? = root.findViewById(R.id.icon)
private val leftIcon: ImageView? = root.findViewById(R.id.left_icon)
@@ -256,9 +256,9 @@ private class AODPromotedNotificationViewUpdater(root: View) {
private val rightIcon: ImageView? = root.findViewById(R.id.right_icon)
private val text: ImageFloatingTextView? = root.findViewById(R.id.text)
private val time: DateTimeView? = root.findViewById(R.id.time)
- private val timeDivider: View? = root.findViewById(R.id.time_divider)
+ private val timeDivider: TextView? = root.findViewById(R.id.time_divider)
private val title: TextView? = root.findViewById(R.id.title)
- private val verificationDivider: View? = root.findViewById(R.id.verification_divider)
+ private val verificationDivider: TextView? = root.findViewById(R.id.verification_divider)
private val verificationIcon: ImageView? = root.findViewById(R.id.verification_icon)
private val verificationText: TextView? = root.findViewById(R.id.verification_text)
@@ -283,6 +283,12 @@ private class AODPromotedNotificationViewUpdater(root: View) {
?.mutate()
?.setColorFilter(SecondaryText.colorInt, PorterDuff.Mode.SRC_IN)
+ setTextViewColor(appNameDivider, SecondaryText)
+ setTextViewColor(headerTextDivider, SecondaryText)
+ setTextViewColor(headerTextSecondaryDivider, SecondaryText)
+ setTextViewColor(timeDivider, SecondaryText)
+ setTextViewColor(verificationDivider, SecondaryText)
+
if (Flags.notificationsRedesignTemplates()) {
(mainColumn?.layoutParams as? MarginLayoutParams)?.let { mainColumnMargins ->
mainColumnMargins.topMargin =
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractor.kt
index d35c3b617246..a8a7e885d1f7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractor.kt
@@ -21,6 +21,7 @@ import android.app.Notification.BigPictureStyle
import android.app.Notification.BigTextStyle
import android.app.Notification.CallStyle
import android.app.Notification.EXTRA_BIG_TEXT
+import android.app.Notification.EXTRA_CALL_PERSON
import android.app.Notification.EXTRA_CHRONOMETER_COUNT_DOWN
import android.app.Notification.EXTRA_PROGRESS
import android.app.Notification.EXTRA_PROGRESS_INDETERMINATE
@@ -33,11 +34,14 @@ import android.app.Notification.EXTRA_VERIFICATION_ICON
import android.app.Notification.EXTRA_VERIFICATION_TEXT
import android.app.Notification.InboxStyle
import android.app.Notification.ProgressStyle
+import android.app.Person
import android.content.Context
import android.graphics.drawable.Icon
import com.android.systemui.Flags
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.shade.ShadeDisplayAware
+import com.android.systemui.statusbar.NotificationLockscreenUserManager.REDACTION_TYPE_NONE
+import com.android.systemui.statusbar.NotificationLockscreenUserManager.RedactionType
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.promoted.AutomaticPromotionCoordinator.Companion.EXTRA_AUTOMATICALLY_EXTRACTED_SHORT_CRITICAL_TEXT
import com.android.systemui.statusbar.notification.promoted.AutomaticPromotionCoordinator.Companion.EXTRA_WAS_AUTOMATICALLY_PROMOTED
@@ -46,6 +50,7 @@ import com.android.systemui.statusbar.notification.promoted.shared.model.Promote
import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel.OldProgress
import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel.Style
import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel.When
+import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModels
import com.android.systemui.statusbar.notification.row.shared.ImageModel
import com.android.systemui.statusbar.notification.row.shared.ImageModelProvider
import com.android.systemui.statusbar.notification.row.shared.ImageModelProvider.ImageSizeClass.MediumSquare
@@ -58,8 +63,9 @@ interface PromotedNotificationContentExtractor {
fun extractContent(
entry: NotificationEntry,
recoveredBuilder: Notification.Builder,
+ @RedactionType redactionType: Int,
imageModelProvider: ImageModelProvider,
- ): PromotedNotificationContentModel?
+ ): PromotedNotificationContentModels?
}
@SysUISingleton
@@ -74,8 +80,9 @@ constructor(
override fun extractContent(
entry: NotificationEntry,
recoveredBuilder: Notification.Builder,
+ @RedactionType redactionType: Int,
imageModelProvider: ImageModelProvider,
- ): PromotedNotificationContentModel? {
+ ): PromotedNotificationContentModels? {
if (!PromotedNotificationContentModel.featureFlagEnabled()) {
logger.logExtractionSkipped(entry, "feature flags disabled")
return null
@@ -93,7 +100,55 @@ constructor(
return null
}
- val contentBuilder = PromotedNotificationContentModel.Builder(entry.key)
+ val privateVersion =
+ extractPrivateContent(
+ key = entry.key,
+ notification = notification,
+ recoveredBuilder = recoveredBuilder,
+ lastAudiblyAlertedMs = entry.lastAudiblyAlertedMs,
+ imageModelProvider = imageModelProvider,
+ )
+ val publicVersion =
+ if (redactionType == REDACTION_TYPE_NONE) {
+ privateVersion
+ } else {
+ if (notification.publicVersion == null) {
+ privateVersion.toDefaultPublicVersion()
+ } else {
+ // TODO(b/400991304): implement extraction for [Notification.publicVersion]
+ privateVersion.toDefaultPublicVersion()
+ }
+ }
+ return PromotedNotificationContentModels(
+ privateVersion = privateVersion,
+ publicVersion = publicVersion,
+ )
+ .also { logger.logExtractionSucceeded(entry, it) }
+ }
+
+ private fun PromotedNotificationContentModel.toDefaultPublicVersion():
+ PromotedNotificationContentModel =
+ PromotedNotificationContentModel.Builder(key = identity.key).let {
+ it.style = if (style == Style.Ineligible) Style.Ineligible else Style.Base
+ it.smallIcon = smallIcon
+ it.iconLevel = iconLevel
+ it.appName = appName
+ it.time = time
+ it.lastAudiblyAlertedMs = lastAudiblyAlertedMs
+ it.profileBadgeResId = profileBadgeResId
+ it.colors = colors
+ it.build()
+ }
+
+ private fun extractPrivateContent(
+ key: String,
+ notification: Notification,
+ recoveredBuilder: Notification.Builder,
+ lastAudiblyAlertedMs: Long,
+ imageModelProvider: ImageModelProvider,
+ ): PromotedNotificationContentModel {
+
+ val contentBuilder = PromotedNotificationContentModel.Builder(key)
// TODO: Pitch a fit if style is unsupported or mandatory fields are missing once
// FLAG_PROMOTED_ONGOING is set reliably and we're not testing status bar chips.
@@ -106,14 +161,14 @@ constructor(
contentBuilder.subText = notification.subText()
contentBuilder.time = notification.extractWhen()
contentBuilder.shortCriticalText = notification.shortCriticalText()
- contentBuilder.lastAudiblyAlertedMs = entry.lastAudiblyAlertedMs
+ contentBuilder.lastAudiblyAlertedMs = lastAudiblyAlertedMs
contentBuilder.profileBadgeResId = null // TODO
- contentBuilder.title = notification.resolveTitle(recoveredBuilder.style)
- contentBuilder.text = notification.resolveText(recoveredBuilder.style)
+ contentBuilder.title = notification.title(recoveredBuilder.style)
+ contentBuilder.text = notification.text(recoveredBuilder.style)
contentBuilder.skeletonLargeIcon = notification.skeletonLargeIcon(imageModelProvider)
contentBuilder.oldProgress = notification.oldProgress()
- val colorsFromNotif = recoveredBuilder.getColors(/* header= */ false)
+ val colorsFromNotif = recoveredBuilder.getColors(/* isHeader= */ false)
contentBuilder.colors =
PromotedNotificationContentModel.Colors(
backgroundColor = colorsFromNotif.backgroundColor,
@@ -122,7 +177,7 @@ constructor(
recoveredBuilder.extractStyleContent(notification, contentBuilder, imageModelProvider)
- return contentBuilder.build().also { logger.logExtractionSucceeded(entry, it) }
+ return contentBuilder.build()
}
private fun Notification.smallIconModel(imageModelProvider: ImageModelProvider): ImageModel? =
@@ -132,20 +187,16 @@ constructor(
private fun Notification.bigTitle(): CharSequence? = extras?.getCharSequence(EXTRA_TITLE_BIG)
- private fun Notification.Style.bigTitleOverridesTitle(): Boolean {
- return when (this) {
+ private fun Notification.callPerson(): Person? =
+ extras?.getParcelable(EXTRA_CALL_PERSON, Person::class.java)
+
+ private fun Notification.title(style: Notification.Style?): CharSequence? {
+ return when (style) {
is BigTextStyle,
is BigPictureStyle,
- is InboxStyle -> true
- else -> false
- }
- }
-
- private fun Notification.resolveTitle(style: Notification.Style?): CharSequence? {
- return if (style?.bigTitleOverridesTitle() == true) {
- bigTitle()
- } else {
- null
+ is InboxStyle -> bigTitle()
+ is CallStyle -> callPerson()?.name
+ else -> null
} ?: title()
}
@@ -153,13 +204,10 @@ constructor(
private fun Notification.bigText(): CharSequence? = extras?.getCharSequence(EXTRA_BIG_TEXT)
- private fun Notification.Style.bigTextOverridesText(): Boolean = this is BigTextStyle
-
- private fun Notification.resolveText(style: Notification.Style?): CharSequence? {
- return if (style?.bigTextOverridesText() == true) {
- bigText()
- } else {
- null
+ private fun Notification.text(style: Notification.Style?): CharSequence? {
+ return when (style) {
+ is BigTextStyle -> bigText()
+ else -> null
} ?: text()
}
@@ -204,16 +252,18 @@ constructor(
extras?.getBoolean(EXTRA_PROGRESS_INDETERMINATE)
private fun Notification.extractWhen(): When? {
+ val whenTime = getWhen()
+
return when {
showsChronometer() -> {
When.Chronometer(
elapsedRealtimeMillis =
- `when` + systemClock.elapsedRealtime() - systemClock.currentTimeMillis(),
+ whenTime + systemClock.elapsedRealtime() - systemClock.currentTimeMillis(),
isCountDown = chronometerCountDown(),
)
}
- showsTime() -> When.Time(currentTimeMillis = `when`)
+ showsTime() -> When.Time(currentTimeMillis = whenTime)
else -> null
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationLogger.kt
index 468934791525..6b6203d6ea4d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationLogger.kt
@@ -23,7 +23,7 @@ import com.android.systemui.log.core.LogLevel.ERROR
import com.android.systemui.log.core.LogLevel.INFO
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.logKey
-import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel
+import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModels
import javax.inject.Inject
@OptIn(ExperimentalStdlibApi::class)
@@ -56,14 +56,14 @@ constructor(@PromotedNotificationLog private val buffer: LogBuffer) {
fun logExtractionSucceeded(
entry: NotificationEntry,
- content: PromotedNotificationContentModel,
+ content: PromotedNotificationContentModels,
) {
buffer.log(
EXTRACTION_TAG,
INFO,
{
str1 = entry.logKey
- str2 = content.toString()
+ str2 = content.toRedactedString()
},
{ "extraction succeeded: $str2 for $str1" },
)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/domain/interactor/AODPromotedNotificationInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/domain/interactor/AODPromotedNotificationInteractor.kt
index ec4ee4560ea1..d9778bdde0a5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/domain/interactor/AODPromotedNotificationInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/domain/interactor/AODPromotedNotificationInteractor.kt
@@ -22,6 +22,7 @@ import com.android.systemui.statusbar.notification.promoted.shared.model.Promote
import com.android.systemui.util.kotlin.FlowDumperImpl
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.map
@SysUISingleton
@@ -34,6 +35,16 @@ constructor(
/** The content to show as the promoted notification on AOD */
val content: Flow<PromotedNotificationContentModel?> =
promotedNotificationsInteractor.aodPromotedNotification
+ .map {
+ // TODO(b/400991304): show the private version when unlocked
+ it?.publicVersion
+ }
+ .distinctUntilNewInstance()
val isPresent: Flow<Boolean> = content.map { it != null }.dumpWhileCollecting("isPresent")
+
+ /**
+ * Returns flow where all subsequent repetitions of the same object instance are filtered out.
+ */
+ private fun <T> Flow<T>.distinctUntilNewInstance() = distinctUntilChanged { a, b -> a === b }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/domain/interactor/PromotedNotificationsInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/domain/interactor/PromotedNotificationsInteractor.kt
index a99ca072b6c8..08e7528631c7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/domain/interactor/PromotedNotificationsInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/domain/interactor/PromotedNotificationsInteractor.kt
@@ -19,17 +19,24 @@ package com.android.systemui.statusbar.notification.promoted.domain.interactor
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.statusbar.chips.call.domain.interactor.CallChipInteractor
+import com.android.systemui.statusbar.chips.mediaprojection.domain.interactor.MediaProjectionChipInteractor
+import com.android.systemui.statusbar.chips.mediaprojection.domain.model.ProjectionChipModel
import com.android.systemui.statusbar.chips.notification.domain.interactor.StatusBarNotificationChipsInteractor
+import com.android.systemui.statusbar.chips.screenrecord.domain.interactor.ScreenRecordChipInteractor
+import com.android.systemui.statusbar.chips.screenrecord.domain.model.ScreenRecordChipModel
import com.android.systemui.statusbar.notification.domain.interactor.ActiveNotificationsInteractor
-import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel
import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel.Style.Ineligible
+import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModels
import com.android.systemui.statusbar.notification.shared.ActiveNotificationModel
import com.android.systemui.statusbar.phone.ongoingcall.shared.model.OngoingCallModel
import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
@@ -38,52 +45,169 @@ import kotlinx.coroutines.flow.map
* presented order of current notification status bar chips.
*/
@SysUISingleton
+@OptIn(ExperimentalCoroutinesApi::class)
class PromotedNotificationsInteractor
@Inject
constructor(
- activeNotificationsInteractor: ActiveNotificationsInteractor,
+ private val activeNotificationsInteractor: ActiveNotificationsInteractor,
+ screenRecordChipInteractor: ScreenRecordChipInteractor,
+ mediaProjectionChipInteractor: MediaProjectionChipInteractor,
callChipInteractor: CallChipInteractor,
notifChipsInteractor: StatusBarNotificationChipsInteractor,
@Background backgroundDispatcher: CoroutineDispatcher,
) {
+ private val screenRecordChipNotification: Flow<NotifAndPromotedContent?> =
+ screenRecordChipInteractor.screenRecordState.flatMapLatest { screenRecordModel ->
+ when (screenRecordModel) {
+ is ScreenRecordChipModel.DoingNothing -> flowOf(null)
+ is ScreenRecordChipModel.Starting -> flowOf(null)
+ is ScreenRecordChipModel.Recording ->
+ createRecordingNotificationFlow(hostPackage = screenRecordModel.hostPackage)
+ }
+ }
+
+ private val mediaProjectionChipNotification: Flow<NotifAndPromotedContent?> =
+ mediaProjectionChipInteractor.projection.flatMapLatest { projectionModel ->
+ when (projectionModel) {
+ is ProjectionChipModel.NotProjecting -> flowOf(null)
+ is ProjectionChipModel.Projecting ->
+ createRecordingNotificationFlow(
+ hostPackage = projectionModel.projectionState.hostPackage
+ )
+ }
+ }
+
+ /**
+ * Creates a flow emitting the screen-recording-related notification corresponding to the given
+ * package name (if we can find it).
+ *
+ * @param hostPackage the package name of the app that is receiving the content of the media
+ * projection (aka which app the phone screen contents are being sent to).
+ */
+ private fun createRecordingNotificationFlow(
+ hostPackage: String?
+ ): Flow<NotifAndPromotedContent?> =
+ if (hostPackage == null) {
+ flowOf(null)
+ } else {
+ activeNotificationsInteractor.allRepresentativeNotifications
+ .map { allNotifs ->
+ findBestMatchingMediaProjectionNotif(allNotifs.values, hostPackage)
+ }
+ .distinctUntilChanged()
+ }
+
+ /**
+ * Finds the best notification that matches the given [hostPackage] that looks like a recording
+ * notification, or null if we couldn't find a uniquely good match.
+ */
+ private fun findBestMatchingMediaProjectionNotif(
+ allNotifs: Collection<ActiveNotificationModel>,
+ hostPackage: String,
+ ): NotifAndPromotedContent? {
+ val candidates = allNotifs.filter { it.packageName == hostPackage }
+ if (candidates.isEmpty()) {
+ return null
+ }
+
+ candidates
+ .findOnlyOrNull { it.isForegroundService }
+ ?.let {
+ return it.toNotifAndPromotedContent()
+ }
+ candidates
+ .findOnlyOrNull { it.isOngoingEvent }
+ ?.let {
+ return it.toNotifAndPromotedContent()
+ }
+ candidates
+ .findOnlyOrNull { it.isForegroundService && it.isOngoingEvent }
+ ?.let {
+ return it.toNotifAndPromotedContent()
+ }
+ // We weren't able to find exactly 1 match for the given [hostPackage], so just don't match
+ // at all.
+ return null
+ }
+
+ /**
+ * Returns the single notification matching the given [predicate] if there's only 1 match, or
+ * null if there's 0 or 2+ matches.
+ */
+ private fun List<ActiveNotificationModel>.findOnlyOrNull(
+ predicate: (ActiveNotificationModel) -> Boolean
+ ): ActiveNotificationModel? {
+ val list = this.filter(predicate)
+ return if (list.size == 1) {
+ list.first()
+ } else {
+ null
+ }
+ }
+
+ private fun ActiveNotificationModel.toNotifAndPromotedContent(): NotifAndPromotedContent {
+ return NotifAndPromotedContent(this.key, this.promotedContent)
+ }
+
+ private val callNotification: Flow<NotifAndPromotedContent?> =
+ callChipInteractor.ongoingCallState
+ .map {
+ when (it) {
+ is OngoingCallModel.InCall ->
+ NotifAndPromotedContent(it.notificationKey, it.promotedContent)
+ is OngoingCallModel.NoCall -> null
+ }
+ }
+ .distinctUntilChanged()
+
+ private val promotedChipNotifications: Flow<List<NotifAndPromotedContent>> =
+ notifChipsInteractor.allNotificationChips
+ .map { chips -> chips.map { NotifAndPromotedContent(it.key, it.promotedContent) } }
+ .distinctUntilChanged()
+
/**
* This is the ordered list of notifications (and the promoted content) represented as chips in
* the status bar.
*/
private val orderedChipNotifications: Flow<List<NotifAndPromotedContent>> =
- combine(callChipInteractor.ongoingCallState, notifChipsInteractor.allNotificationChips) {
- callState,
- notifChips ->
- buildList {
- val callData = callState.getNotifData()?.also { add(it) }
- addAll(
- notifChips.mapNotNull {
- when (it.key) {
- callData?.key -> null // do not re-add the same call
- else -> NotifAndPromotedContent(it.key, it.promotedContent)
- }
- }
- )
+ combine(
+ screenRecordChipNotification,
+ mediaProjectionChipNotification,
+ callNotification,
+ promotedChipNotifications,
+ ) { screenRecordNotif, mediaProjectionNotif, callNotif, promotedNotifs ->
+ val chipNotifications = mutableListOf<NotifAndPromotedContent>()
+ val usedKeys = mutableListOf<String>()
+
+ fun addToList(item: NotifAndPromotedContent?) {
+ if (item != null && !usedKeys.contains(item.key)) {
+ chipNotifications.add(item)
+ usedKeys.add(item.key)
+ }
}
- }
- private fun OngoingCallModel.getNotifData(): NotifAndPromotedContent? =
- when (this) {
- is OngoingCallModel.InCall -> NotifAndPromotedContent(notificationKey, promotedContent)
- is OngoingCallModel.NoCall -> null
+ // IMPORTANT: This ordering is prescribed by OngoingActivityChipsViewModel. Be sure to
+ // always keep this ordering in sync with that view model.
+ // TODO(b/402471288): Create a single source of truth for the ordering.
+ addToList(screenRecordNotif)
+ addToList(mediaProjectionNotif)
+ addToList(callNotif)
+ promotedNotifs.forEach { addToList(it) }
+
+ chipNotifications
}
/**
* The top promoted notification represented by a chip, with the order determined by the order
* of the chips, not the notifications.
*/
- private val topPromotedChipNotification: Flow<PromotedNotificationContentModel?> =
+ private val topPromotedChipNotification: Flow<PromotedNotificationContentModels?> =
orderedChipNotifications
.map { list -> list.firstNotNullOfOrNull { it.promotedContent } }
.distinctUntilNewInstance()
/** This is the AOD promoted notification, which should avoid regular changing. */
- val aodPromotedNotification: Flow<PromotedNotificationContentModel?> =
+ val aodPromotedNotification: Flow<PromotedNotificationContentModels?> =
combine(
topPromotedChipNotification,
activeNotificationsInteractor.topLevelRepresentativeNotifications,
@@ -105,13 +229,13 @@ constructor(
.flowOn(backgroundDispatcher)
private fun List<ActiveNotificationModel>.firstAodEligibleOrNull():
- PromotedNotificationContentModel? {
+ PromotedNotificationContentModels? {
return this.firstNotNullOfOrNull { it.promotedContent?.takeIfAodEligible() }
}
- private fun PromotedNotificationContentModel.takeIfAodEligible():
- PromotedNotificationContentModel? {
- return this.takeUnless { it.style == Ineligible }
+ private fun PromotedNotificationContentModels.takeIfAodEligible():
+ PromotedNotificationContentModels? {
+ return this.takeUnless { it.privateVersion.style == Ineligible }
}
/**
@@ -127,7 +251,7 @@ constructor(
*/
private data class NotifAndPromotedContent(
val key: String,
- val promotedContent: PromotedNotificationContentModel?,
+ val promotedContent: PromotedNotificationContentModels?,
) {
/**
* Define the equals of this object to only check the reference equality of the promoted
@@ -145,7 +269,7 @@ constructor(
/** Define the hashCode to be very quick, even if it increases collisions. */
override fun hashCode(): Int {
var result = key.hashCode()
- result = 31 * result + (promotedContent?.identity?.hashCode() ?: 0)
+ result = 31 * result + (promotedContent?.key?.hashCode() ?: 0)
return result
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/shared/model/PromotedNotificationContentModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/shared/model/PromotedNotificationContentModel.kt
index 0c2859fa1766..339a5bb29a34 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/shared/model/PromotedNotificationContentModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/shared/model/PromotedNotificationContentModel.kt
@@ -23,9 +23,38 @@ import android.app.Notification
import android.app.Notification.FLAG_PROMOTED_ONGOING
import androidx.annotation.ColorInt
import com.android.internal.widget.NotificationProgressModel
+import com.android.systemui.Flags
import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips
import com.android.systemui.statusbar.notification.promoted.PromotedNotificationUi
+import com.android.systemui.statusbar.notification.row.ImageResult
+import com.android.systemui.statusbar.notification.row.LazyImage
import com.android.systemui.statusbar.notification.row.shared.ImageModel
+import com.android.systemui.util.Compile
+
+data class PromotedNotificationContentModels(
+ /** The potentially redacted version of the content that will be exposed to the public */
+ val publicVersion: PromotedNotificationContentModel,
+ /** The unredacted version of the content that will be kept private */
+ val privateVersion: PromotedNotificationContentModel,
+) {
+ val key: String
+ get() = privateVersion.identity.key
+
+ init {
+ check(publicVersion.identity.key == privateVersion.identity.key) {
+ "public and private models must have the same key"
+ }
+ }
+
+ fun toRedactedString(): String {
+ val publicVersionString =
+ "==privateVersion".takeIf { privateVersion === publicVersion }
+ ?: publicVersion.toRedactedString()
+ return ("PromotedNotificationContentModels(" +
+ "privateVersion=${privateVersion.toRedactedString()}, " +
+ "publicVersion=$publicVersionString)")
+ }
+}
/**
* The content needed to render a promoted notification to surfaces besides the notification stack,
@@ -152,6 +181,54 @@ data class PromotedNotificationContentModel(
Ineligible,
}
+ fun toRedactedString(): String {
+ return ("PromotedNotificationContentModel(" +
+ "identity=$identity, " +
+ "wasPromotedAutomatically=$wasPromotedAutomatically, " +
+ "smallIcon=${smallIcon?.toRedactedString()}, " +
+ "appName=$appName, " +
+ "subText=${subText?.toRedactedString()}, " +
+ "shortCriticalText=$shortCriticalText, " +
+ "time=$time, " +
+ "lastAudiblyAlertedMs=$lastAudiblyAlertedMs, " +
+ "profileBadgeResId=$profileBadgeResId, " +
+ "title=${title?.toRedactedString()}, " +
+ "text=${text?.toRedactedString()}, " +
+ "skeletonLargeIcon=${skeletonLargeIcon?.toRedactedString()}, " +
+ "oldProgress=$oldProgress, " +
+ "colors=$colors, " +
+ "style=$style, " +
+ "personIcon=${personIcon?.toRedactedString()}, " +
+ "personName=${personName?.toRedactedString()}, " +
+ "verificationIcon=$verificationIcon, " +
+ "verificationText=$verificationText, " +
+ "newProgress=$newProgress)")
+ }
+
+ private fun CharSequence.toRedactedString(): String = "[$length]"
+
+ private fun ImageModel.toRedactedString(): String {
+ return when (this) {
+ is LazyImage -> this.toRedactedString()
+ else -> this.toString()
+ }
+ }
+
+ private fun LazyImage.toRedactedString(): String {
+ return ("LazyImage(" +
+ "icon=[${icon.javaClass.simpleName}], " +
+ "sizeClass=$sizeClass, " +
+ "transform=$transform, " +
+ "result=${result?.toRedactedString()})")
+ }
+
+ private fun ImageResult.toRedactedString(): String {
+ return when (this) {
+ is ImageResult.Empty -> this.toString()
+ is ImageResult.Image -> "Image(drawable=[${drawable.javaClass.simpleName}])"
+ }
+ }
+
companion object {
@JvmStatic
fun featureFlagEnabled(): Boolean =
@@ -163,6 +240,10 @@ data class PromotedNotificationContentModel(
*/
@JvmStatic
fun isPromotedForStatusBarChip(notification: Notification): Boolean {
+ if (Compile.IS_DEBUG && Flags.debugLiveUpdatesPromoteAll()) {
+ return true
+ }
+
// Notification.isPromotedOngoing checks the ui_rich_ongoing flag, but we want the
// status bar chip to be ready before all the features behind the ui_rich_ongoing flag
// are ready.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index 5160b909fd6a..bef3c691cb4d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -22,9 +22,9 @@ import static android.service.notification.NotificationListenerService.REASON_CA
import static android.view.accessibility.AccessibilityEvent.CONTENT_CHANGE_TYPE_EXPANDED;
import static android.view.accessibility.AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED;
+import static com.android.systemui.Flags.notificationRowAccessibilityExpanded;
import static com.android.systemui.Flags.notificationRowTransparency;
import static com.android.systemui.Flags.notificationsPinnedHunInShade;
-import static com.android.systemui.Flags.notificationRowAccessibilityExpanded;
import static com.android.systemui.flags.Flags.ENABLE_NOTIFICATIONS_SIMULATE_SLOW_MEASURE;
import static com.android.systemui.statusbar.notification.NotificationUtils.logKey;
import static com.android.systemui.statusbar.notification.collection.NotificationEntry.DismissState.PARENT_DISMISSED;
@@ -671,8 +671,13 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
}
private boolean isConversation() {
- return mPeopleNotificationIdentifier.getPeopleNotificationType(getEntry())
- != PeopleNotificationIdentifier.TYPE_NON_PERSON;
+ if (NotificationBundleUi.isEnabled()) {
+ return getEntryAdapter().getPeopleNotificationType()
+ != PeopleNotificationIdentifier.TYPE_NON_PERSON;
+ } else {
+ return mPeopleNotificationIdentifier.getPeopleNotificationType(getEntryLegacy())
+ != PeopleNotificationIdentifier.TYPE_NON_PERSON;
+ }
}
public void onNotificationUpdated() {
@@ -971,7 +976,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
return mEntry;
}
- @Nullable
+ @NonNull
public EntryAdapter getEntryAdapter() {
NotificationBundleUi.unsafeAssertInNewMode();
return mEntryAdapter;
@@ -1000,7 +1005,9 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
} else if (isAboveShelf() != wasAboveShelf) {
mAboveShelfChangedListener.onAboveShelfStateChanged(!wasAboveShelf);
}
- updateBackgroundTint();
+ if (notificationRowTransparency()) {
+ updateBackgroundTint();
+ }
}
/**
@@ -1786,7 +1793,11 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
/** @return true if the User has dismissed this notif's parent */
public boolean isParentDismissed() {
- return getEntry().getDismissState() == PARENT_DISMISSED;
+ if (NotificationBundleUi.isEnabled()) {
+ return getEntryAdapter().getDismissState() == PARENT_DISMISSED;
+ } else {
+ return getEntryLegacy().getDismissState() == PARENT_DISMISSED;
+ }
}
@Override
@@ -2511,7 +2522,11 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
*/
public void dragAndDropSuccess() {
if (mOnDragSuccessListener != null) {
- mOnDragSuccessListener.onDragSuccess(getEntry());
+ if (NotificationBundleUi.isEnabled()) {
+ mOnDragSuccessListener.onDragSuccess(getEntryAdapter());
+ } else {
+ mOnDragSuccessListener.onDragSuccess(getEntryLegacy());
+ }
}
}
@@ -2671,30 +2686,58 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
}
/**
+ * Whether to allow dismissal with the whole-row translation animation.
+ *
+ * If true, either animation is permissible.
+ * If false, usingRTX behavior is forbidden, only clipping animation should be used.
+ *
+ * Usually either is OK, except for promoted notifications, where we always need to
+ * dismiss with content clipping/partial translation animation instead, so that we
+ * can show the demotion options.
+ * @return
+ */
+ private boolean allowDismissUsingRowTranslationX() {
+ if (Flags.permissionHelperInlineUiRichOngoing()) {
+ return !isPromotedOngoing();
+ } else {
+ // Don't change behavior unless the flag is on.
+ return true;
+ }
+ }
+
+ /**
* Set the dismiss behavior of the view.
*
* @param usingRowTranslationX {@code true} if the view should translate using regular
* translationX, otherwise the contents will be
* translated.
+ * @param forceUpdateChildren {@code true} to force initialization, {@code false} if lazy
+ * behavior is OK.
*/
@Override
- public void setDismissUsingRowTranslationX(boolean usingRowTranslationX) {
- if (usingRowTranslationX != mDismissUsingRowTranslationX) {
+ public void setDismissUsingRowTranslationX(boolean usingRowTranslationX,
+ boolean forceUpdateChildren) {
+ // Before updating dismiss behavior, make sure this is an allowable configuration for this
+ // notification.
+ usingRowTranslationX = usingRowTranslationX && allowDismissUsingRowTranslationX();
+
+ if (forceUpdateChildren || (usingRowTranslationX != mDismissUsingRowTranslationX)) {
// In case we were already transitioning, let's switch over!
float previousTranslation = getTranslation();
if (previousTranslation != 0) {
setTranslation(0);
}
- super.setDismissUsingRowTranslationX(usingRowTranslationX);
+ super.setDismissUsingRowTranslationX(usingRowTranslationX, forceUpdateChildren);
if (previousTranslation != 0) {
setTranslation(previousTranslation);
}
+
if (mChildrenContainer != null) {
List<ExpandableNotificationRow> notificationChildren =
mChildrenContainer.getAttachedChildren();
for (int i = 0; i < notificationChildren.size(); i++) {
ExpandableNotificationRow child = notificationChildren.get(i);
- child.setDismissUsingRowTranslationX(usingRowTranslationX);
+ child.setDismissUsingRowTranslationX(usingRowTranslationX, forceUpdateChildren);
}
}
}
@@ -3151,7 +3194,9 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
mChildrenContainer.setOnKeyguard(onKeyguard);
}
}
- updateBackgroundTint();
+ if (notificationRowTransparency()) {
+ updateBackgroundTint();
+ }
}
}
@@ -3998,7 +4043,8 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
}
} else if (isChildInGroup()) {
final int childColor = getShowingLayout().getBackgroundColorForExpansionState();
- if (Flags.notificationRowTransparency() && childColor == Color.TRANSPARENT) {
+ if ((Flags.notificationRowTransparency() || notificationsRedesignTemplates())
+ && childColor == Color.TRANSPARENT) {
// If child is not customizing its background color, switch from the parent to
// the child background when the expansion finishes.
mShowNoBackground = !mNotificationParent.mShowNoBackground;
@@ -4412,6 +4458,12 @@ public class ExpandableNotificationRow extends ActivatableNotificationView
* @param entry NotificationEntry that succeed to drop on proper target window.
*/
void onDragSuccess(NotificationEntry entry);
+
+ /**
+ * @param entryAdapter The EntryAdapter that successfully dropped on the proper
+ * target window
+ */
+ void onDragSuccess(EntryAdapter entryAdapter);
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableOutlineView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableOutlineView.java
index 80cf818e985f..6c990df5d05e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableOutlineView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableOutlineView.java
@@ -292,7 +292,8 @@ public abstract class ExpandableOutlineView extends ExpandableView {
* translationX, otherwise the contents will be
* translated.
*/
- public void setDismissUsingRowTranslationX(boolean usingRowTranslationX) {
+ public void setDismissUsingRowTranslationX(boolean usingRowTranslationX,
+ boolean forceUpdateChildren) {
mDismissUsingRowTranslationX = usingRowTranslationX;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java
index 292f74a65554..f36a0cf51b97 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java
@@ -19,6 +19,8 @@ package com.android.systemui.statusbar.notification.row;
import static com.android.systemui.Flags.notificationColorUpdateLogger;
import static com.android.systemui.Flags.physicalNotificationMovement;
+import static java.lang.Math.abs;
+
import android.animation.AnimatorListenerAdapter;
import android.content.Context;
import android.content.res.Configuration;
@@ -29,6 +31,7 @@ import android.util.FloatProperty;
import android.util.IndentingPrintWriter;
import android.util.Log;
import android.view.View;
+import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.view.ViewParent;
import android.widget.FrameLayout;
@@ -110,14 +113,27 @@ public abstract class ExpandableView extends FrameLayout implements Dumpable, Ro
protected SpringAnimation mMagneticAnimator = new SpringAnimation(
this /* object */, DynamicAnimation.TRANSLATION_X);
+ private int mTouchSlop;
+
protected MagneticRowListener mMagneticRowListener = new MagneticRowListener() {
@Override
- public void setMagneticTranslation(float translation) {
- if (mMagneticAnimator.isRunning()) {
- mMagneticAnimator.animateToFinalPosition(translation);
- } else {
+ public void setMagneticTranslation(float translation, boolean trackEagerly) {
+ if (!mMagneticAnimator.isRunning()) {
setTranslation(translation);
+ return;
+ }
+
+ if (trackEagerly) {
+ float delta = abs(getTranslation() - translation);
+ if (delta > mTouchSlop) {
+ mMagneticAnimator.animateToFinalPosition(translation);
+ } else {
+ mMagneticAnimator.cancel();
+ setTranslation(translation);
+ }
+ } else {
+ mMagneticAnimator.animateToFinalPosition(translation);
}
}
@@ -183,6 +199,7 @@ public abstract class ExpandableView extends FrameLayout implements Dumpable, Ro
private void initDimens() {
mContentShift = getResources().getDimensionPixelSize(
R.dimen.shelf_transform_content_shift);
+ mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FeedbackInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FeedbackInfo.java
index 40897dae4c44..d80d5e101294 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FeedbackInfo.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FeedbackInfo.java
@@ -45,7 +45,6 @@ import com.android.internal.statusbar.IStatusBarService;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
import com.android.systemui.res.R;
import com.android.systemui.statusbar.notification.AssistantFeedbackController;
-import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.util.Compile;
public class FeedbackInfo extends LinearLayout implements NotificationGuts.GutsContent {
@@ -58,7 +57,6 @@ public class FeedbackInfo extends LinearLayout implements NotificationGuts.GutsC
private PackageManager mPm;
private String mAppName;
private String mPkg;
- private NotificationEntry mEntry;
private IStatusBarService mStatusBarService;
private AssistantFeedbackController mFeedbackController;
@@ -73,16 +71,15 @@ public class FeedbackInfo extends LinearLayout implements NotificationGuts.GutsC
public void bindGuts(
final PackageManager pm,
final StatusBarNotification sbn,
- final NotificationEntry entry,
+ final NotificationListenerService.Ranking ranking,
final ExpandableNotificationRow row,
final AssistantFeedbackController controller,
final IStatusBarService statusBarService,
final NotificationGutsManager notificationGutsManager) {
mPkg = sbn.getPackageName();
mPm = pm;
- mEntry = entry;
mExpandableNotificationRow = row;
- mRanking = entry.getRanking();
+ mRanking = ranking;
mFeedbackController = controller;
mAppName = mPkg;
mStatusBarService = statusBarService;
@@ -143,7 +140,7 @@ public class FeedbackInfo extends LinearLayout implements NotificationGuts.GutsC
@SuppressLint("DefaultLocale")
private String getPrompt() {
StringBuilder sb = new StringBuilder();
- int status = mFeedbackController.getFeedbackStatus(mEntry);
+ int status = mFeedbackController.getFeedbackStatus(mRanking);
if (DEBUG) {
sb.append(String.format(
"[DEBUG]: oldImportance=%d, newImportance=%d, ranking=%f\n\n",
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridConversationNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridConversationNotificationView.java
index ba80f016cad4..4c7c46dfae94 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridConversationNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridConversationNotificationView.java
@@ -51,7 +51,7 @@ import java.util.Objects;
*/
public class HybridConversationNotificationView extends HybridNotificationView {
- private static final int MAX_SUMMARIZATION_LINES = 2;
+ private static final int MAX_SUMMARIZATION_LINES = 1;
private ImageView mConversationIconView;
private TextView mConversationSenderName;
private ViewStub mConversationFacePileStub;
@@ -295,7 +295,6 @@ public class HybridConversationNotificationView extends HybridNotificationView {
if (AsyncHybridViewInflation.isUnexpectedlyInLegacyMode()) return;
if (!TextUtils.isEmpty(summarization)) {
mConversationSenderName.setVisibility(GONE);
- titleText = null;
contentText = summarization;
mTextView.setSingleLine(false);
mTextView.setMaxLines(MAX_SUMMARIZATION_LINES);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/MagicActionBackgroundDrawable.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/MagicActionBackgroundDrawable.kt
index fe3a856e711e..a9ca6359b570 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/MagicActionBackgroundDrawable.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/MagicActionBackgroundDrawable.kt
@@ -62,6 +62,7 @@ class BaseBackgroundDrawable(
private val buttonShape = Path()
// Color and style
+ private val outlineStaticColor = context.getColor(R.color.magic_action_button_stroke_color)
private val bgPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
val bgColor =
context.getColor(
@@ -70,15 +71,17 @@ class BaseBackgroundDrawable(
color = bgColor
style = Paint.Style.FILL
}
- private val outlinePaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
- val outlineColor =
- context.getColor(
- com.android.internal.R.color.materialColorOutlineVariant
- )
- color = outlineColor
+ private val outlineGradientPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
+ color = outlineStaticColor
+ style = Paint.Style.STROKE
+ strokeWidth = outlineStrokeWidth
+ }
+ private val outlineSolidPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
+ color = outlineStaticColor
style = Paint.Style.STROKE
strokeWidth = outlineStrokeWidth
}
+
private val outlineStartColor =
context.getColor(
com.android.internal.R.color.materialColorTertiaryContainer
@@ -91,21 +94,35 @@ class BaseBackgroundDrawable(
context.getColor(
com.android.internal.R.color.materialColorPrimary
)
+
// Animation
private var gradientAnimator: ValueAnimator
private var rotationAngle = 20f // Start rotation at 20 degrees
+ private var fadeAnimator: ValueAnimator? = null
+ private var gradientAlpha = 255 // Fading out gradient
+ private var solidAlpha = 0 // Fading in solid color
init {
gradientAnimator = ValueAnimator.ofFloat(0f, 1f).apply {
- duration = 5000 // 5 seconds
+ duration = 1500
interpolator = Interpolators.LINEAR
- repeatCount = 1
+ repeatCount = 0
addUpdateListener { animator ->
val animatedValue = animator.animatedValue as Float
rotationAngle = 20f + animatedValue * 360f // Rotate in a spiral
invalidateSelf()
}
- // TODO: Reset the outline color when animation ends.
+ start()
+ }
+ fadeAnimator = ValueAnimator.ofFloat(0f, 1f).apply {
+ duration = 500
+ startDelay = 1000
+ addUpdateListener { animator ->
+ val progress = animator.animatedValue as Float
+ gradientAlpha = ((1 - progress) * 255).toInt() // Fade out gradient
+ solidAlpha = (progress * 255).toInt() // Fade in color
+ invalidateSelf()
+ }
start()
}
}
@@ -120,14 +137,9 @@ class BaseBackgroundDrawable(
// Draw background
canvas.clipPath(buttonShape)
canvas.drawPath(buttonShape, bgPaint)
- // Apply gradient to outline
- canvas.drawPath(buttonShape, outlinePaint)
- updateGradient(boundsF)
- canvas.restore()
- }
- private fun updateGradient(boundsF: RectF) {
- val gradient = LinearGradient(
+ // Set up outline gradient
+ val gradientShader = LinearGradient(
boundsF.left, boundsF.top,
boundsF.right, boundsF.bottom,
intArrayOf(outlineStartColor, outlineMiddleColor, outlineEndColor),
@@ -137,9 +149,17 @@ class BaseBackgroundDrawable(
// Create a rotation matrix for the spiral effect
val matrix = Matrix()
matrix.setRotate(rotationAngle, boundsF.centerX(), boundsF.centerY())
- gradient.setLocalMatrix(matrix)
+ gradientShader.setLocalMatrix(matrix)
+
+ // Apply gradient to outline
+ outlineGradientPaint.shader = gradientShader
+ outlineGradientPaint.alpha = gradientAlpha
+ canvas.drawPath(buttonShape, outlineGradientPaint)
+ // Apply solid color to outline
+ outlineSolidPaint.alpha = solidAlpha
+ canvas.drawPath(buttonShape, outlineSolidPaint)
- outlinePaint.shader = gradient
+ canvas.restore()
}
override fun onBoundsChange(bounds: Rect) {
@@ -149,13 +169,15 @@ class BaseBackgroundDrawable(
override fun setAlpha(alpha: Int) {
bgPaint.alpha = alpha
- outlinePaint.alpha = alpha
+ outlineGradientPaint.alpha = alpha
+ outlineSolidPaint.alpha = alpha
invalidateSelf()
}
override fun setColorFilter(colorFilter: ColorFilter?) {
bgPaint.colorFilter = colorFilter
- outlinePaint.colorFilter = colorFilter
+ outlineGradientPaint.colorFilter = colorFilter
+ outlineSolidPaint.colorFilter = colorFilter
invalidateSelf()
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java
index d97e25fdfa22..f17ae571d3ee 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java
@@ -60,6 +60,7 @@ import com.android.systemui.statusbar.notification.NotificationUtils;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.promoted.PromotedNotificationContentExtractor;
import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel;
+import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModels;
import com.android.systemui.statusbar.notification.row.shared.AsyncGroupHeaderViewInflation;
import com.android.systemui.statusbar.notification.row.shared.AsyncHybridViewInflation;
import com.android.systemui.statusbar.notification.row.shared.ImageModelProvider;
@@ -1003,7 +1004,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder
row.mImageModelIndex = result.mRowImageInflater.getNewImageIndex();
if (PromotedNotificationContentModel.featureFlagEnabled()) {
- entry.setPromotedNotificationContentModel(result.mPromotedContent);
+ entry.setPromotedNotificationContentModels(result.mPromotedContent);
}
boolean setRepliesAndActions = true;
@@ -1387,11 +1388,11 @@ public class NotificationContentInflater implements NotificationRowContentBinder
mLogger.logAsyncTaskProgress(logKey, "extracting promoted notification content");
final ImageModelProvider imageModelProvider =
result.mRowImageInflater.useForContentModel();
- final PromotedNotificationContentModel promotedContent =
+ final PromotedNotificationContentModels promotedContent =
mPromotedNotificationContentExtractor.extractContent(mEntry,
- recoveredBuilder, imageModelProvider);
+ recoveredBuilder, mBindParams.redactionType, imageModelProvider);
mLogger.logAsyncTaskProgress(logKey, "extracted promoted notification content: "
- + promotedContent);
+ + (promotedContent != null ? promotedContent.toRedactedString() : null));
result.mPromotedContent = promotedContent;
}
@@ -1503,7 +1504,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder
static class InflationProgress {
RowImageInflater mRowImageInflater;
- PromotedNotificationContentModel mPromotedContent;
+ PromotedNotificationContentModels mPromotedContent;
private RemoteViews newContentView;
private RemoteViews newHeadsUpView;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
index 26d318bea5cc..488aa44ddd3b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
@@ -1604,12 +1604,15 @@ public class NotificationContentView extends FrameLayout implements Notification
}
if (shouldShowBubbleButton(entry)) {
+ boolean isBubble = NotificationBundleUi.isEnabled()
+ ? mContainingNotification.getEntryAdapter().isBubble()
+ : entry.isBubble();
// explicitly resolve drawable resource using SystemUI's theme
- Drawable d = mContext.getDrawable(entry.isBubble()
+ Drawable d = mContext.getDrawable(isBubble
? com.android.wm.shell.R.drawable.bubble_ic_stop_bubble
: com.android.wm.shell.R.drawable.bubble_ic_create_bubble);
- String contentDescription = mContext.getResources().getString(entry.isBubble()
+ String contentDescription = mContext.getResources().getString(isBubble
? R.string.notification_conversation_unbubble
: R.string.notification_conversation_bubble);
@@ -1652,12 +1655,18 @@ public class NotificationContentView extends FrameLayout implements Notification
@VisibleForTesting
boolean shouldShowBubbleButton(NotificationEntry entry) {
- boolean isPersonWithShortcut =
- mPeopleIdentifier.getPeopleNotificationType(entry)
- >= PeopleNotificationIdentifier.TYPE_FULL_PERSON;
+ int peopleType = NotificationBundleUi.isEnabled()
+ ? mContainingNotification.getEntryAdapter().getPeopleNotificationType()
+ : mPeopleIdentifier.getPeopleNotificationType(entry);
+ Notification.BubbleMetadata bubbleMetadata = NotificationBundleUi.isEnabled()
+ ? mContainingNotification.getEntryAdapter().getSbn().getNotification()
+ .getBubbleMetadata()
+ : entry.getBubbleMetadata();
+ boolean isPersonWithShortcut = peopleType
+ >= PeopleNotificationIdentifier.TYPE_FULL_PERSON;
return mBubblesEnabledForUser
&& isPersonWithShortcut
- && entry.getBubbleMetadata() != null;
+ && bubbleMetadata != null;
}
private void applySnoozeAction(View layout) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java
index 3c7d9ef8e71d..2cf3b14bb8c5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java
@@ -49,6 +49,7 @@ import android.os.Handler;
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
+import android.service.notification.NotificationListenerService;
import android.service.notification.StatusBarNotification;
import android.text.TextUtils;
import android.transition.ChangeBounds;
@@ -72,7 +73,9 @@ import com.android.systemui.res.R;
import com.android.systemui.shade.ShadeController;
import com.android.systemui.statusbar.notification.NmSummarizationUiFlag;
import com.android.systemui.statusbar.notification.NotificationChannelHelper;
+import com.android.systemui.statusbar.notification.collection.EntryAdapter;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.shared.NotificationBundleUi;
import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
import com.android.systemui.wmshell.BubblesManager;
@@ -104,6 +107,8 @@ public class NotificationConversationInfo extends LinearLayout implements
private ShortcutInfo mShortcutInfo;
private NotificationEntry mEntry;
private StatusBarNotification mSbn;
+ private EntryAdapter mEntryAdapter;
+ private NotificationListenerService.Ranking mRanking;
@Nullable private Notification.BubbleMetadata mBubbleMetadata;
private Context mUserContext;
private boolean mIsDeviceProvisioned;
@@ -200,9 +205,10 @@ public class NotificationConversationInfo extends LinearLayout implements
INotificationManager iNotificationManager,
OnUserInteractionCallback onUserInteractionCallback,
String pkg,
- NotificationChannel notificationChannel,
NotificationEntry entry,
- Notification.BubbleMetadata bubbleMetadata,
+ EntryAdapter entryAdapter,
+ NotificationListenerService.Ranking ranking,
+ StatusBarNotification sbn,
OnSettingsClickListener onSettingsClick,
NotificationInfo.OnFeedbackClickListener onFeedbackClickListener,
ConversationIconFactory conversationIconFactory,
@@ -218,25 +224,27 @@ public class NotificationConversationInfo extends LinearLayout implements
mOnUserInteractionCallback = onUserInteractionCallback;
mPackageName = pkg;
mEntry = entry;
- mSbn = entry.getSbn();
+ mSbn = sbn;
+ mRanking = ranking;
+ mEntryAdapter = entryAdapter;
mPm = pm;
mUm = um;
mAppName = mPackageName;
mOnSettingsClickListener = onSettingsClick;
- mNotificationChannel = notificationChannel;
+ mNotificationChannel = ranking.getChannel();
mAppUid = mSbn.getUid();
mDelegatePkg = mSbn.getOpPkg();
mIsDeviceProvisioned = isDeviceProvisioned;
mOnConversationSettingsClickListener = onConversationSettingsClickListener;
mIconFactory = conversationIconFactory;
mUserContext = userContext;
- mBubbleMetadata = bubbleMetadata;
+ mBubbleMetadata = sbn.getNotification().getBubbleMetadata();
mBubblesManagerOptional = bubblesManagerOptional;
mShadeController = shadeController;
mMainHandler = mainHandler;
mBgHandler = bgHandler;
mShortcutManager = shortcutManager;
- mShortcutInfo = entry.getRanking().getConversationShortcutInfo();
+ mShortcutInfo = ranking.getConversationShortcutInfo();
mFeedbackClickListener = onFeedbackClickListener;
if (mShortcutInfo == null) {
throw new IllegalArgumentException("Does not have required information");
@@ -308,11 +316,11 @@ public class NotificationConversationInfo extends LinearLayout implements
private void bindFeedback() {
View feedbackButton = findViewById(R.id.feedback);
if (!NmSummarizationUiFlag.isEnabled()
- || TextUtils.isEmpty(mEntry.getRanking().getSummarization())) {
+ || TextUtils.isEmpty(mRanking.getSummarization())) {
feedbackButton.setVisibility(GONE);
} else {
Intent intent = NotificationInfo.getAssistantFeedbackIntent(
- mINotificationManager, mPm, mEntry);
+ mINotificationManager, mPm, mSbn.getKey(), mRanking);
if (intent == null) {
feedbackButton.setVisibility(GONE);
} else {
@@ -551,10 +559,17 @@ public class NotificationConversationInfo extends LinearLayout implements
mBgHandler.post(
new UpdateChannelRunnable(mINotificationManager, mPackageName,
mAppUid, mSelectedAction, mNotificationChannel));
- mEntry.markForUserTriggeredMovement(true);
- mMainHandler.postDelayed(
- () -> mOnUserInteractionCallback.onImportanceChanged(mEntry),
- StackStateAnimator.ANIMATION_DURATION_STANDARD);
+ if (NotificationBundleUi.isEnabled()) {
+ mEntryAdapter.markForUserTriggeredMovement();
+ mMainHandler.postDelayed(
+ () -> mEntryAdapter.onImportanceChanged(),
+ StackStateAnimator.ANIMATION_DURATION_STANDARD);
+ } else {
+ mEntry.markForUserTriggeredMovement(true);
+ mMainHandler.postDelayed(
+ () -> mOnUserInteractionCallback.onImportanceChanged(mEntry),
+ StackStateAnimator.ANIMATION_DURATION_STANDARD);
+ }
}
private boolean willBypassDnd() {
@@ -658,8 +673,13 @@ public class NotificationConversationInfo extends LinearLayout implements
BUBBLE_PREFERENCE_SELECTED);
}
if (mBubblesManagerOptional.isPresent()) {
- post(() -> mBubblesManagerOptional.get()
- .onUserSetImportantConversation(mEntry));
+ if (NotificationBundleUi.isEnabled()) {
+ post(() -> mBubblesManagerOptional.get()
+ .onUserSetImportantConversation(mEntryAdapter));
+ } else {
+ post(() -> mBubblesManagerOptional.get()
+ .onUserSetImportantConversation(mEntry));
+ }
}
}
mChannelToUpdate.setImportance(Math.max(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
index cdb78d99538b..6c7c7a79348f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
@@ -29,9 +29,11 @@ import android.content.pm.ShortcutManager;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
+import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.Settings;
+import android.service.notification.NotificationListenerService;
import android.service.notification.StatusBarNotification;
import android.util.ArraySet;
import android.util.IconDrawableFactory;
@@ -65,7 +67,6 @@ import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.StatusBarStateControllerImpl;
import com.android.systemui.statusbar.notification.AssistantFeedbackController;
import com.android.systemui.statusbar.notification.NotificationActivityStarter;
-import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider;
import com.android.systemui.statusbar.notification.collection.render.NotifGutsViewListener;
import com.android.systemui.statusbar.notification.collection.render.NotifGutsViewManager;
@@ -288,10 +289,21 @@ public class NotificationGutsManager implements NotifGutsViewManager, CoreStarta
@VisibleForTesting
protected boolean bindGuts(final ExpandableNotificationRow row,
NotificationMenuRowPlugin.MenuItem item) {
- NotificationEntry entry = row.getEntry();
+
+ StatusBarNotification sbn = NotificationBundleUi.isEnabled()
+ ? row.getEntryAdapter().getSbn()
+ : row.getEntryLegacy().getSbn();
+ NotificationListenerService.Ranking ranking = NotificationBundleUi.isEnabled()
+ ? row.getEntryAdapter().getRanking()
+ : row.getEntryLegacy().getRanking();
+
+ if (sbn == null || ranking == null) {
+ // only valid for notification rows
+ return false;
+ }
row.setGutsView(item);
- row.setTag(entry.getSbn().getPackageName());
+ row.setTag(sbn.getPackageName());
row.getGuts().setClosedListener((NotificationGuts g) -> {
row.onGutsClosed();
if (!g.willBeRemoved() && !row.isRemoved()) {
@@ -303,25 +315,37 @@ public class NotificationGutsManager implements NotifGutsViewManager, CoreStarta
mGutsMenuItem = null;
}
if (mGutsListener != null) {
- mGutsListener.onGutsClose(entry);
+ if (NotificationBundleUi.isEnabled()) {
+ mGutsListener.onGutsClose(row.getEntryAdapter());
+ row.updateBubbleButton();
+ } else {
+ mGutsListener.onGutsClose(row.getEntryLegacy());
+ }
+ }
+ if(NotificationBundleUi.isEnabled()) {
+ row.getEntryAdapter().setInlineControlsShown(false);
+ } else {
+ mHeadsUpManager.setGutsShown(row.getEntryLegacy(), false);
}
- mHeadsUpManager.setGutsShown(row.getEntry(), false);
});
View gutsView = item.getGutsView();
+
try {
if (gutsView instanceof NotificationSnooze) {
- initializeSnoozeView(row, (NotificationSnooze) gutsView);
+ initializeSnoozeView(row, sbn, ranking, (NotificationSnooze) gutsView);
} else if (gutsView instanceof NotificationInfo) {
- initializeNotificationInfo(row, (NotificationInfo) gutsView);
+ initializeNotificationInfo(row, sbn, ranking, (NotificationInfo) gutsView);
} else if (gutsView instanceof NotificationConversationInfo) {
initializeConversationNotificationInfo(
- row, (NotificationConversationInfo) gutsView);
+ row, sbn, ranking, (NotificationConversationInfo) gutsView);
} else if (gutsView instanceof PartialConversationInfo) {
- initializePartialConversationNotificationInfo(row,
+ initializePartialConversationNotificationInfo(row, sbn, ranking,
(PartialConversationInfo) gutsView);
} else if (gutsView instanceof FeedbackInfo) {
- initializeFeedbackInfo(row, (FeedbackInfo) gutsView);
+ initializeFeedbackInfo(row, sbn, ranking, (FeedbackInfo) gutsView);
+ } else if (gutsView instanceof PromotedPermissionGutsContent) {
+ initializeDemoteView(row, sbn, (PromotedPermissionGutsContent) gutsView);
}
return true;
} catch (Exception e) {
@@ -338,19 +362,45 @@ public class NotificationGutsManager implements NotifGutsViewManager, CoreStarta
*/
private void initializeSnoozeView(
final ExpandableNotificationRow row,
+ final StatusBarNotification sbn,
+ final NotificationListenerService.Ranking ranking,
NotificationSnooze notificationSnoozeView) {
NotificationGuts guts = row.getGuts();
- StatusBarNotification sbn = row.getEntry().getSbn();
notificationSnoozeView.setSnoozeListener(mListContainer.getSwipeActionHelper());
notificationSnoozeView.setStatusBarNotification(sbn);
- notificationSnoozeView.setSnoozeOptions(row.getEntry().getSnoozeCriteria());
+ notificationSnoozeView.setSnoozeOptions(ranking.getSnoozeCriteria());
guts.setHeightChangedListener((NotificationGuts g) -> {
mListContainer.onHeightChanged(row, row.isShown() /* needsAnimation */);
});
}
/**
+ * Sets up the {@link NotificationSnooze} inside the notification row's guts.
+ *
+ * @param row view to set up the guts for
+ * @param demoteGuts view to set up/bind within {@code row}
+ */
+ private void initializeDemoteView(
+ final ExpandableNotificationRow row,
+ StatusBarNotification sbn,
+ PromotedPermissionGutsContent demoteGuts) {
+ demoteGuts.setStatusBarNotification(sbn);
+ demoteGuts.setOnDemoteAction(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ try {
+ // TODO(b/391661009): Signal AutomaticPromotionCoordinator here
+ mNotificationManager.setCanBePromoted(
+ sbn.getPackageName(), sbn.getUid(), false, true);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Couldn't revoke live update permission", e);
+ }
+ }
+ });
+ }
+
+ /**
* Sets up the {@link FeedbackInfo} inside the notification row's guts.
*
* @param row view to set up the guts for
@@ -358,16 +408,17 @@ public class NotificationGutsManager implements NotifGutsViewManager, CoreStarta
*/
private void initializeFeedbackInfo(
final ExpandableNotificationRow row,
+ final StatusBarNotification sbn,
+ final NotificationListenerService.Ranking ranking,
FeedbackInfo feedbackInfo) {
- if (mAssistantFeedbackController.getFeedbackIcon(row.getEntry()) == null) {
+ if (mAssistantFeedbackController.getFeedbackIcon(ranking) == null) {
return;
}
- StatusBarNotification sbn = row.getEntry().getSbn();
UserHandle userHandle = sbn.getUser();
PackageManager pmUser = CentralSurfaces.getPackageManagerForUser(mContext,
userHandle.getIdentifier());
- feedbackInfo.bindGuts(pmUser, sbn, row.getEntry(), row, mAssistantFeedbackController,
+ feedbackInfo.bindGuts(pmUser, sbn, ranking, row, mAssistantFeedbackController,
mStatusBarService, this);
}
@@ -379,9 +430,10 @@ public class NotificationGutsManager implements NotifGutsViewManager, CoreStarta
@VisibleForTesting
void initializeNotificationInfo(
final ExpandableNotificationRow row,
+ final StatusBarNotification sbn,
+ final NotificationListenerService.Ranking ranking,
NotificationInfo notificationInfoView) throws Exception {
NotificationGuts guts = row.getGuts();
- StatusBarNotification sbn = row.getEntry().getSbn();
String packageName = sbn.getPackageName();
// Settings link is only valid for notifications that specify a non-system user
NotificationInfo.OnSettingsClickListener onSettingsClick = null;
@@ -420,18 +472,22 @@ public class NotificationGutsManager implements NotifGutsViewManager, CoreStarta
mChannelEditorDialogController,
mPackageDemotionInteractor,
packageName,
- row.getEntry().getChannel(),
- row.getEntry(),
+ ranking,
+ sbn,
+ NotificationBundleUi.isEnabled() ? null : row.getEntryLegacy(),
+ NotificationBundleUi.isEnabled() ? row.getEntryAdapter() : null,
onSettingsClick,
onAppSettingsClick,
onNasFeedbackClick,
mUiEventLogger,
mDeviceProvisionedController.isDeviceProvisioned(),
NotificationBundleUi.isEnabled()
- ? !row.getEntry().isBlockable()
+ ? !row.getEntryAdapter().isBlockable()
: row.getIsNonblockable(),
row.canViewBeDismissed(),
- mHighPriorityProvider.isHighPriority(row.getEntry()),
+ NotificationBundleUi.isEnabled()
+ ? row.getEntryAdapter().isHighPriority()
+ : mHighPriorityProvider.isHighPriority(row.getEntryLegacy()),
mAssistantFeedbackController,
mMetricsLogger,
row.getCloseButtonOnClickListener(row));
@@ -445,9 +501,10 @@ public class NotificationGutsManager implements NotifGutsViewManager, CoreStarta
@VisibleForTesting
void initializePartialConversationNotificationInfo(
final ExpandableNotificationRow row,
+ final StatusBarNotification sbn,
+ final NotificationListenerService.Ranking ranking,
PartialConversationInfo notificationInfoView) throws Exception {
NotificationGuts guts = row.getGuts();
- StatusBarNotification sbn = row.getEntry().getSbn();
String packageName = sbn.getPackageName();
// Settings link is only valid for notifications that specify a non-system user
NotificationInfo.OnSettingsClickListener onSettingsClick = null;
@@ -470,12 +527,12 @@ public class NotificationGutsManager implements NotifGutsViewManager, CoreStarta
mNotificationManager,
mChannelEditorDialogController,
packageName,
- row.getEntry().getChannel(),
- row.getEntry(),
+ ranking,
+ sbn,
onSettingsClick,
mDeviceProvisionedController.isDeviceProvisioned(),
NotificationBundleUi.isEnabled()
- ? !row.getEntry().isBlockable()
+ ? !row.getEntryAdapter().isBlockable()
: row.getIsNonblockable());
}
@@ -487,10 +544,10 @@ public class NotificationGutsManager implements NotifGutsViewManager, CoreStarta
@VisibleForTesting
void initializeConversationNotificationInfo(
final ExpandableNotificationRow row,
+ final StatusBarNotification sbn,
+ final NotificationListenerService.Ranking ranking,
NotificationConversationInfo notificationInfoView) throws Exception {
NotificationGuts guts = row.getGuts();
- NotificationEntry entry = row.getEntry();
- StatusBarNotification sbn = entry.getSbn();
String packageName = sbn.getPackageName();
// Settings link is only valid for notifications that specify a non-system user
NotificationConversationInfo.OnSettingsClickListener onSettingsClick = null;
@@ -538,9 +595,10 @@ public class NotificationGutsManager implements NotifGutsViewManager, CoreStarta
mNotificationManager,
mOnUserInteractionCallback,
packageName,
- entry.getChannel(),
- entry,
- entry.getBubbleMetadata(),
+ NotificationBundleUi.isEnabled() ? null : row.getEntryLegacy(),
+ NotificationBundleUi.isEnabled() ? row.getEntryAdapter() : null,
+ ranking,
+ sbn,
onSettingsClick,
onNasFeedbackClick,
iconFactoryLoader,
@@ -717,13 +775,21 @@ public class NotificationGutsManager implements NotifGutsViewManager, CoreStarta
row::onGutsOpened);
if (mGutsListener != null) {
- mGutsListener.onGutsOpen(row.getEntry(), guts);
+ if(NotificationBundleUi.isEnabled()) {
+ mGutsListener.onGutsOpen(row.getEntryAdapter(), guts);
+ } else {
+ mGutsListener.onGutsOpen(row.getEntryLegacy(), guts);
+ }
}
row.closeRemoteInput();
mListContainer.onHeightChanged(row, true /* needsAnimation */);
mGutsMenuItem = menuItem;
- mHeadsUpManager.setGutsShown(row.getEntry(), true);
+ if(NotificationBundleUi.isEnabled()) {
+ row.getEntryAdapter().setInlineControlsShown(true);
+ } else {
+ mHeadsUpManager.setGutsShown(row.getEntryLegacy(), true);
+ }
}
};
guts.post(mOpenRunnable);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java
index b6f4ffce8e00..571006bc21e3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java
@@ -50,6 +50,7 @@ import android.metrics.LogMaker;
import android.os.Handler;
import android.os.RemoteException;
import android.service.notification.NotificationAssistantService;
+import android.service.notification.NotificationListenerService;
import android.service.notification.StatusBarNotification;
import android.text.Html;
import android.text.TextUtils;
@@ -73,10 +74,12 @@ import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.systemui.Dependency;
import com.android.systemui.res.R;
import com.android.systemui.statusbar.notification.AssistantFeedbackController;
+import com.android.systemui.statusbar.notification.collection.EntryAdapter;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.promoted.domain.interactor.PackageDemotionInteractor;
import com.android.systemui.statusbar.notification.row.icon.AppIconProvider;
import com.android.systemui.statusbar.notification.row.icon.NotificationIconStyleProvider;
+import com.android.systemui.statusbar.notification.shared.NotificationBundleUi;
import java.lang.annotation.Retention;
import java.util.List;
@@ -124,6 +127,8 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G
private boolean mIsDismissable;
private NotificationEntry mEntry;
private StatusBarNotification mSbn;
+ private NotificationListenerService.Ranking mRanking;
+ private EntryAdapter mEntryAdapter;
private boolean mIsDeviceProvisioned;
private boolean mIsSystemRegisteredCall;
@@ -198,8 +203,10 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G
ChannelEditorDialogController channelEditorDialogController,
PackageDemotionInteractor packageDemotionInteractor,
String pkg,
- NotificationChannel notificationChannel,
+ NotificationListenerService.Ranking ranking,
+ StatusBarNotification sbn,
NotificationEntry entry,
+ EntryAdapter entryAdapter,
OnSettingsClickListener onSettingsClick,
OnAppSettingsClickListener onAppSettingsClick,
OnFeedbackClickListener onFeedbackClickListener,
@@ -220,14 +227,16 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G
mChannelEditorDialogController = channelEditorDialogController;
mAssistantFeedbackController = assistantFeedbackController;
mPackageName = pkg;
+ mSbn = sbn;
+ mRanking = ranking;
mEntry = entry;
- mSbn = entry.getSbn();
+ mEntryAdapter = entryAdapter;
mPm = pm;
mAppSettingsClickListener = onAppSettingsClick;
mFeedbackClickListener = onFeedbackClickListener;
mAppName = mPackageName;
mOnSettingsClickListener = onSettingsClick;
- mSingleNotificationChannel = notificationChannel;
+ mSingleNotificationChannel = ranking.getChannel();
mStartingChannelImportance = mSingleNotificationChannel.getImportance();
mWasShownHighPriority = wasShownHighPriority;
mIsNonblockable = isNonblockable;
@@ -301,7 +310,8 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G
View automatic = findViewById(R.id.automatic);
if (mShowAutomaticSetting) {
mAutomaticDescriptionView.setText(Html.fromHtml(mContext.getText(
- mAssistantFeedbackController.getInlineDescriptionResource(mEntry)).toString()));
+ mAssistantFeedbackController.getInlineDescriptionResource(mRanking))
+ .toString()));
automatic.setVisibility(VISIBLE);
automatic.setOnClickListener(mOnAutomatic);
} else {
@@ -381,7 +391,8 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G
private void bindFeedback() {
View feedbackButton = findViewById(R.id.feedback);
- Intent intent = getAssistantFeedbackIntent(mINotificationManager, mPm, mEntry);
+ Intent intent = getAssistantFeedbackIntent(
+ mINotificationManager, mPm, mSbn.getKey(), mRanking);
if (!android.app.Flags.notificationClassificationUi() || intent == null) {
feedbackButton.setVisibility(GONE);
} else {
@@ -395,7 +406,7 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G
}
public static Intent getAssistantFeedbackIntent(INotificationManager inm, PackageManager pm,
- NotificationEntry entry) {
+ String key, NotificationListenerService.Ranking ranking) {
try {
ComponentName assistant = inm.getAllowedNotificationAssistant();
if (assistant == null) {
@@ -414,9 +425,9 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G
final ActivityInfo activityInfo = resolveInfos.get(0).activityInfo;
intent.setClassName(activityInfo.packageName, activityInfo.name);
- intent.putExtra(NotificationAssistantService.EXTRA_NOTIFICATION_KEY, entry.getKey());
+ intent.putExtra(NotificationAssistantService.EXTRA_NOTIFICATION_KEY, key);
intent.putExtra(NotificationAssistantService.EXTRA_NOTIFICATION_ADJUSTMENT,
- entry.getRanking().getSummarization() != null ? KEY_SUMMARIZATION : KEY_TYPE);
+ ranking.getSummarization() != null ? KEY_SUMMARIZATION : KEY_TYPE);
return intent;
} catch (Exception e) {
Slog.d(TAG, "no assistant?", e);
@@ -526,7 +537,11 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G
new UpdateImportanceRunnable(mINotificationManager, mPackageName, mAppUid,
mSingleNotificationChannel,
mStartingChannelImportance, newImportance, mIsAutomaticChosen));
- mOnUserInteractionCallback.onImportanceChanged(mEntry);
+ if (NotificationBundleUi.isEnabled()) {
+ mEntryAdapter.onImportanceChanged();
+ } else {
+ mOnUserInteractionCallback.onImportanceChanged(mEntry);
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java
index e89a76fd5a69..f494a4ce40dd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java
@@ -46,9 +46,9 @@ import com.android.systemui.Flags;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
import com.android.systemui.res.R;
import com.android.systemui.statusbar.AlphaOptimizedImageView;
-import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier;
import com.android.systemui.statusbar.notification.row.NotificationGuts.GutsContent;
+import com.android.systemui.statusbar.notification.shared.NotificationBundleUi;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
import java.util.ArrayList;
@@ -261,15 +261,20 @@ public class NotificationMenuRow implements NotificationMenuRowPlugin, View.OnCl
mSnoozeItem = createSnoozeItem(mContext);
}
mFeedbackItem = createFeedbackItem(mContext);
- NotificationEntry entry = mParent.getEntry();
- int personNotifType = mPeopleNotificationIdentifier.getPeopleNotificationType(entry);
+ int personNotifType = NotificationBundleUi.isEnabled()
+ ? mParent.getEntryAdapter().getPeopleNotificationType()
+ : mPeopleNotificationIdentifier.getPeopleNotificationType(mParent.getEntryLegacy());
+ StatusBarNotification sbn = NotificationBundleUi.isEnabled()
+ ? mParent.getEntryAdapter().getSbn()
+ : mParent.getEntryLegacy().getSbn();
if (personNotifType == PeopleNotificationIdentifier.TYPE_PERSON) {
mInfoItem = createPartialConversationItem(mContext);
} else if (personNotifType >= PeopleNotificationIdentifier.TYPE_FULL_PERSON) {
mInfoItem = createConversationItem(mContext);
} else if (android.app.Flags.uiRichOngoing()
+ && android.app.Flags.apiRichOngoing()
&& Flags.permissionHelperUiRichOngoing()
- && entry.getSbn().getNotification().isPromotedOngoing()) {
+ && sbn.getNotification().isPromotedOngoing()) {
mInfoItem = createPromotedItem(mContext);
} else {
mInfoItem = createInfoItem(mContext);
@@ -280,6 +285,15 @@ public class NotificationMenuRow implements NotificationMenuRowPlugin, View.OnCl
}
mRightMenuItems.add(mInfoItem);
mRightMenuItems.add(mFeedbackItem);
+ boolean isPromotedOngoing = NotificationBundleUi.isEnabled()
+ ? mParent.getEntryAdapter().isPromotedOngoing()
+ : mParent.getEntryLegacy().isPromotedOngoing();
+ if (android.app.Flags.uiRichOngoing() && Flags.permissionHelperInlineUiRichOngoing()
+ && isPromotedOngoing) {
+ mRightMenuItems.add(createDemoteItem(mContext));
+ }
+
+
mLeftMenuItems.addAll(mRightMenuItems);
populateMenuViews();
@@ -301,15 +315,19 @@ public class NotificationMenuRow implements NotificationMenuRowPlugin, View.OnCl
} else {
mMenuContainer = new FrameLayout(mContext);
}
+
final int showDismissSetting = Settings.Global.getInt(mContext.getContentResolver(),
Settings.Global.SHOW_NEW_NOTIF_DISMISS, /* default = */ 1);
final boolean newFlowHideShelf = showDismissSetting == 1;
- if (newFlowHideShelf) {
- return;
- }
- List<MenuItem> menuItems = mOnLeft ? mLeftMenuItems : mRightMenuItems;
- for (int i = 0; i < menuItems.size(); i++) {
- addMenuView(menuItems.get(i), mMenuContainer);
+
+ // Populate menu items if we are using the new permission helper (U+) or if we are using
+ // the very old dismiss setting (SC-).
+ // TODO: SHOW_NEW_NOTIF_DISMISS==0 case can likely be removed.
+ if (Flags.permissionHelperInlineUiRichOngoing() || !newFlowHideShelf) {
+ List<MenuItem> menuItems = mOnLeft ? mLeftMenuItems : mRightMenuItems;
+ for (int i = 0; i < menuItems.size(); i++) {
+ addMenuView(menuItems.get(i), mMenuContainer);
+ }
}
}
@@ -358,7 +376,9 @@ public class NotificationMenuRow implements NotificationMenuRowPlugin, View.OnCl
final float dismissThreshold = getDismissThreshold();
final boolean snappingToDismiss = delta < -dismissThreshold || delta > dismissThreshold;
if (mSnappingToDismiss != snappingToDismiss) {
- getMenuView().performHapticFeedback(CLOCK_TICK);
+ if (!Flags.magneticNotificationSwipes()) {
+ getMenuView().performHapticFeedback(CLOCK_TICK);
+ }
}
mSnappingToDismiss = snappingToDismiss;
}
@@ -673,6 +693,15 @@ public class NotificationMenuRow implements NotificationMenuRowPlugin, View.OnCl
return snooze;
}
+ static MenuItem createDemoteItem(Context context) {
+ PromotedPermissionGutsContent demoteContent =
+ (PromotedPermissionGutsContent) LayoutInflater.from(context).inflate(
+ R.layout.promoted_permission_guts, null, false);
+ MenuItem info = new NotificationMenuItem(context, null, demoteContent,
+ R.drawable.unpin_icon);
+ return info;
+ }
+
static NotificationMenuItem createConversationItem(Context context) {
Resources res = context.getResources();
String infoDescription = res.getString(R.string.notification_menu_gear_description);
@@ -680,7 +709,7 @@ public class NotificationMenuRow implements NotificationMenuRowPlugin, View.OnCl
(NotificationConversationInfo) LayoutInflater.from(context).inflate(
R.layout.notification_conversation_info, null, false);
return new NotificationMenuItem(context, infoDescription, infoContent,
- R.drawable.ic_settings);
+ NotificationMenuItem.OMIT_FROM_SWIPE_MENU);
}
static NotificationMenuItem createPromotedItem(Context context) {
@@ -690,7 +719,7 @@ public class NotificationMenuRow implements NotificationMenuRowPlugin, View.OnCl
(PromotedNotificationInfo) LayoutInflater.from(context).inflate(
R.layout.promoted_notification_info, null, false);
return new NotificationMenuItem(context, infoDescription, infoContent,
- R.drawable.ic_settings);
+ NotificationMenuItem.OMIT_FROM_SWIPE_MENU);
}
static NotificationMenuItem createPartialConversationItem(Context context) {
@@ -700,7 +729,7 @@ public class NotificationMenuRow implements NotificationMenuRowPlugin, View.OnCl
(PartialConversationInfo) LayoutInflater.from(context).inflate(
R.layout.partial_conversation_info, null, false);
return new NotificationMenuItem(context, infoDescription, infoContent,
- R.drawable.ic_settings);
+ NotificationMenuItem.OMIT_FROM_SWIPE_MENU);
}
static NotificationMenuItem createInfoItem(Context context) {
@@ -712,14 +741,14 @@ public class NotificationMenuRow implements NotificationMenuRowPlugin, View.OnCl
NotificationInfo infoContent = (NotificationInfo) LayoutInflater.from(context).inflate(
layoutId, null, false);
return new NotificationMenuItem(context, infoDescription, infoContent,
- R.drawable.ic_settings);
+ NotificationMenuItem.OMIT_FROM_SWIPE_MENU);
}
static MenuItem createFeedbackItem(Context context) {
FeedbackInfo feedbackContent = (FeedbackInfo) LayoutInflater.from(context).inflate(
R.layout.feedback_info, null, false);
MenuItem info = new NotificationMenuItem(context, null, feedbackContent,
- -1 /*don't show in slow swipe menu */);
+ NotificationMenuItem.OMIT_FROM_SWIPE_MENU);
return info;
}
@@ -756,6 +785,10 @@ public class NotificationMenuRow implements NotificationMenuRowPlugin, View.OnCl
@Override
public boolean isWithinSnapMenuThreshold() {
+ if (getSpaceForMenu() == 0) {
+ // don't snap open if there are no items
+ return false;
+ }
float translation = getTranslation();
float snapBackThreshold = getSnapBackThreshold();
float targetRight = getDismissThreshold();
@@ -797,6 +830,10 @@ public class NotificationMenuRow implements NotificationMenuRowPlugin, View.OnCl
}
public static class NotificationMenuItem implements MenuItem {
+
+ // Constant signaling that this MenuItem should not appear in slow swipe.
+ public static final int OMIT_FROM_SWIPE_MENU = -1;
+
View mMenuView;
GutsContent mGutsContent;
String mContentDescription;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImpl.kt
index ae52db88358a..efc90c91092a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImpl.kt
@@ -53,6 +53,7 @@ import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.logKey
import com.android.systemui.statusbar.notification.promoted.PromotedNotificationContentExtractor
import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel
+import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModels
import com.android.systemui.statusbar.notification.row.NotificationContentView.VISIBLE_TYPE_CONTRACTED
import com.android.systemui.statusbar.notification.row.NotificationContentView.VISIBLE_TYPE_EXPANDED
import com.android.systemui.statusbar.notification.row.NotificationContentView.VISIBLE_TYPE_HEADSUP
@@ -595,7 +596,7 @@ constructor(
val rowImageInflater: RowImageInflater,
val remoteViews: NewRemoteViews,
val contentModel: NotificationContentModel,
- val promotedContent: PromotedNotificationContentModel?,
+ val promotedContent: PromotedNotificationContentModels?,
) {
var inflatedContentView: View? = null
@@ -700,11 +701,16 @@ constructor(
)
val imageModelProvider = rowImageInflater.useForContentModel()
promotedNotificationContentExtractor
- .extractContent(entry, builder, imageModelProvider)
+ .extractContent(
+ entry,
+ builder,
+ bindParams.redactionType,
+ imageModelProvider,
+ )
.also {
logger.logAsyncTaskProgress(
entry.logKey,
- "extracted promoted notification content: $it",
+ "extracted promoted notification content: ${it?.toRedactedString()}",
)
}
} else {
@@ -1519,7 +1525,7 @@ constructor(
entry.setContentModel(result.contentModel)
if (PromotedNotificationContentModel.featureFlagEnabled()) {
- entry.promotedNotificationContentModel = result.promotedContent
+ entry.promotedNotificationContentModels = result.promotedContent
}
result.inflatedSmartReplyState?.let { row.privateLayout.setInflatedSmartReplyState(it) }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PartialConversationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PartialConversationInfo.java
index 60e98a5c317a..3b795796020c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PartialConversationInfo.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PartialConversationInfo.java
@@ -24,6 +24,7 @@ import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.graphics.drawable.Drawable;
import android.os.RemoteException;
+import android.service.notification.NotificationListenerService;
import android.service.notification.StatusBarNotification;
import android.text.TextUtils;
import android.util.AttributeSet;
@@ -35,6 +36,7 @@ import android.widget.TextView;
import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.res.R;
+import com.android.systemui.statusbar.notification.collection.EntryAdapter;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
/**
@@ -79,18 +81,18 @@ public class PartialConversationInfo extends LinearLayout implements
INotificationManager iNotificationManager,
ChannelEditorDialogController channelEditorDialogController,
String pkg,
- NotificationChannel notificationChannel,
- NotificationEntry entry,
+ NotificationListenerService.Ranking ranking,
+ StatusBarNotification sbn,
NotificationInfo.OnSettingsClickListener onSettingsClick,
boolean isDeviceProvisioned,
boolean isNonBlockable) {
mINotificationManager = iNotificationManager;
mPackageName = pkg;
- mSbn = entry.getSbn();
+ mSbn = sbn;
mPm = pm;
mAppName = mPackageName;
mOnSettingsClickListener = onSettingsClick;
- mNotificationChannel = notificationChannel;
+ mNotificationChannel = ranking.getChannel();
mAppUid = mSbn.getUid();
mDelegatePkg = mSbn.getOpPkg();
mIsDeviceProvisioned = isDeviceProvisioned;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PromotedNotificationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PromotedNotificationInfo.java
index 01ee788f7fd7..2cb208932943 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PromotedNotificationInfo.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PromotedNotificationInfo.java
@@ -21,6 +21,7 @@ import android.app.NotificationChannel;
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.RemoteException;
+import android.service.notification.NotificationListenerService;
import android.service.notification.StatusBarNotification;
import android.util.AttributeSet;
import android.util.Log;
@@ -30,6 +31,7 @@ import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.UiEventLogger;
import com.android.systemui.res.R;
import com.android.systemui.statusbar.notification.AssistantFeedbackController;
+import com.android.systemui.statusbar.notification.collection.EntryAdapter;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.promoted.domain.interactor.PackageDemotionInteractor;
import com.android.systemui.statusbar.notification.row.icon.AppIconProvider;
@@ -60,8 +62,10 @@ public class PromotedNotificationInfo extends NotificationInfo {
ChannelEditorDialogController channelEditorDialogController,
PackageDemotionInteractor packageDemotionInteractor,
String pkg,
- NotificationChannel notificationChannel,
+ NotificationListenerService.Ranking ranking,
+ StatusBarNotification sbn,
NotificationEntry entry,
+ EntryAdapter entryAdapter,
OnSettingsClickListener onSettingsClick,
OnAppSettingsClickListener onAppSettingsClick,
OnFeedbackClickListener feedbackClickListener,
@@ -73,16 +77,17 @@ public class PromotedNotificationInfo extends NotificationInfo {
AssistantFeedbackController assistantFeedbackController,
MetricsLogger metricsLogger, OnClickListener onCloseClick) throws RemoteException {
super.bindNotification(pm, iNotificationManager, appIconProvider, iconStyleProvider,
- onUserInteractionCallback, channelEditorDialogController, packageDemotionInteractor,
- pkg, notificationChannel,
- entry, onSettingsClick, onAppSettingsClick, feedbackClickListener, uiEventLogger,
- isDeviceProvisioned, isNonblockable, isDismissable, wasShownHighPriority,
- assistantFeedbackController, metricsLogger, onCloseClick);
+ onUserInteractionCallback, channelEditorDialogController,
+ packageDemotionInteractor,pkg, ranking, sbn,
+ entry, entryAdapter, onSettingsClick, onAppSettingsClick, feedbackClickListener,
+ uiEventLogger, isDeviceProvisioned, isDismissable, isNonblockable,
+ wasShownHighPriority, assistantFeedbackController, metricsLogger, onCloseClick);
mNotificationManager = iNotificationManager;
+
mPackageDemotionInteractor = packageDemotionInteractor;
- bindDemote(entry.getSbn(), pkg);
+ bindDemote(sbn, pkg);
}
protected void bindDemote(StatusBarNotification sbn, String packageName) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PromotedPermissionGutsContent.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PromotedPermissionGutsContent.java
new file mode 100644
index 000000000000..222a1f4d8adf
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PromotedPermissionGutsContent.java
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2017 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.notification.row;
+
+import android.content.Context;
+import android.os.Bundle;
+import android.service.notification.StatusBarNotification;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import com.android.internal.logging.MetricsLogger;
+import com.android.systemui.res.R;
+
+/**
+ * This GutsContent shows an explanatory interstitial telling the user they've just revoked this
+ * app's permission to post Promoted/Live notifications.
+ * If the guts are dismissed without further action, the revocation is committed.
+ * If the user hits undo, the permission is not revoked.
+ */
+public class PromotedPermissionGutsContent extends LinearLayout
+ implements NotificationGuts.GutsContent, View.OnClickListener {
+
+ private static final String TAG = "SnoozyPromotedGuts";
+
+ private NotificationGuts mGutsContainer;
+ private StatusBarNotification mSbn;
+
+ private TextView mUndoButton;
+
+ private MetricsLogger mMetricsLogger = new MetricsLogger();
+ private OnClickListener mDemoteAction;
+
+ public PromotedPermissionGutsContent(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+ mUndoButton = (TextView) findViewById(R.id.undo);
+ mUndoButton.setOnClickListener(this);
+ mUndoButton.setContentDescription(
+ getContext().getString(R.string.snooze_undo_content_description));
+
+ }
+
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ dispatchConfigurationChanged(getResources().getConfiguration());
+ }
+
+ /**
+ * Update the content description of the snooze view based on the snooze option and whether the
+ * snooze options are expanded or not.
+ * For example, this will be something like "Collapsed\u2029Snooze for 1 hour". The paragraph
+ * separator is added to introduce a break in speech, to match what TalkBack does by default
+ * when you e.g. press on a notification.
+ */
+ private void updateContentDescription() {
+ //
+ }
+
+
+ @Override
+ public boolean performAccessibilityActionInternal(int action, Bundle arguments) {
+ if (super.performAccessibilityActionInternal(action, arguments)) {
+ return true;
+ }
+ if (action == R.id.action_snooze_undo) {
+ undoDemote(mUndoButton);
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * TODO docs
+ * @param sbn
+ */
+ public void setStatusBarNotification(StatusBarNotification sbn) {
+ mSbn = sbn;
+ TextView demoteExplanation = (TextView) findViewById(R.id.demote_explain);
+ demoteExplanation.setText(mContext.getResources().getString(R.string.demote_explain_text,
+ mSbn.getPackageName()));
+ }
+
+ @Override
+ public void onClick(View v) {
+ if (mGutsContainer != null) {
+ mGutsContainer.resetFalsingCheck();
+ }
+ final int id = v.getId();
+ if (id == R.id.undo) {
+ undoDemote(v);
+ }
+
+ }
+
+ private void undoDemote(View v) {
+ // Don't commit the demote action, instead log the undo and dismiss the view.
+ mGutsContainer.closeControls(v, /* save= */ false);
+ }
+
+ @Override
+ public int getActualHeight() {
+ return getHeight();
+ }
+
+ @Override
+ public boolean willBeRemoved() {
+ return false;
+ }
+
+ @Override
+ public View getContentView() {
+ return this;
+ }
+
+ @Override
+ public void setGutsParent(NotificationGuts guts) {
+ mGutsContainer = guts;
+ }
+
+ @Override
+ public boolean handleCloseControls(boolean save, boolean force) {
+ if (!save) {
+ // Undo changes and let the guts handle closing the view
+ return false;
+ } else {
+ // Commit demote action.
+ mDemoteAction.onClick(this);
+ return false;
+ }
+ }
+
+ @Override
+ public boolean isLeavebehind() {
+ return true;
+ }
+
+ @Override
+ public boolean shouldBeSavedOnClose() {
+ return true;
+ }
+
+ @Override
+ public boolean needsFalsingProtection() {
+ return false;
+ }
+
+ public void setOnDemoteAction(OnClickListener demoteAction) {
+ mDemoteAction = demoteAction;
+ }
+
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/icon/NotificationIconStyleProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/icon/NotificationIconStyleProvider.kt
index 08c1d71b86c9..03990bf3af84 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/icon/NotificationIconStyleProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/icon/NotificationIconStyleProvider.kt
@@ -87,9 +87,15 @@ constructor(private val userManager: UserManager, dumpManager: DumpManager) :
// It's not a system app at all.
return false
} else {
- // If there's no launch intent, it's probably a headless app.
- val pm = context.packageManager
- return (pm.getLaunchIntentForPackage(info.packageName) == null)
+ // If there's no launch intent, it's probably a headless app. Check for both
+ // direct-aware and -unaware intents; otherwise this will almost certainly fail
+ // for notifications posted before unlocking.
+ val packageLaunchIntent =
+ context.packageManager.getLaunchIntentForPackage(
+ info.packageName,
+ /* includeDirectBootUnaware= */ true,
+ )
+ return packageLaunchIntent == null
}
} else {
// If for some reason we don't have the app info, we don't know; best assume it's
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/ActiveNotificationModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/ActiveNotificationModel.kt
index 53728c7da62d..3c8ebfd695a6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/ActiveNotificationModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/ActiveNotificationModel.kt
@@ -18,8 +18,10 @@ package com.android.systemui.statusbar.notification.shared
import android.app.PendingIntent
import android.graphics.drawable.Icon
import android.util.Log
+import com.android.internal.logging.InstanceId
import com.android.systemui.statusbar.StatusBarIconView
import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel
+import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModels
import com.android.systemui.statusbar.notification.stack.PriorityBucket
/**
@@ -38,6 +40,10 @@ data class ActiveNotificationModel(
val groupKey: String?,
/** When this notification was posted. */
val whenTime: Long,
+ /** True if this is a foreground service notification. */
+ val isForegroundService: Boolean,
+ /** True if this notification is for an ongoing event. */
+ val isOngoingEvent: Boolean,
/** Is this entry in the ambient / minimized section (lowest priority)? */
val isAmbient: Boolean,
/**
@@ -73,7 +79,7 @@ data class ActiveNotificationModel(
/** The intent to execute if UI related to this notification is clicked. */
val contentIntent: PendingIntent?,
/** A small per-notification ID, used for statsd logging. */
- val instanceId: Int?,
+ val instanceId: InstanceId?,
/** If this notification is the group summary for a group of notifications. */
val isGroupSummary: Boolean,
/** Indicates in which section the notification is displayed in. @see [PriorityBucket]. */
@@ -84,7 +90,7 @@ data class ActiveNotificationModel(
* The content needed to render this as a promoted notification on various surfaces, or null if
* this notification cannot be rendered as a promoted notification.
*/
- val promotedContent: PromotedNotificationContentModel?,
+ val promotedContent: PromotedNotificationContentModels?,
) : ActiveNotificationEntryModel() {
init {
if (!PromotedNotificationContentModel.featureFlagEnabled()) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MagneticNotificationRowManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MagneticNotificationRowManager.kt
index aa6951715755..48cff7497e3c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MagneticNotificationRowManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MagneticNotificationRowManager.kt
@@ -33,12 +33,12 @@ import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
interface MagneticNotificationRowManager {
/**
- * Set the swipe threshold in pixels. After crossing the threshold, the magnetic target detaches
- * and the magnetic neighbors snap back.
+ * Notifies a change in the device density. The density can be used to compute the values of
+ * thresholds in pixels.
*
- * @param[threshold] Swipe threshold in pixels.
+ * @param[density] The device density.
*/
- fun setSwipeThresholdPx(thresholdPx: Float)
+ fun onDensityChange(density: Float)
/**
* Set the magnetic and roundable targets of a magnetic swipe interaction.
@@ -87,6 +87,9 @@ interface MagneticNotificationRowManager {
*/
fun onMagneticInteractionEnd(row: ExpandableNotificationRow, velocity: Float? = null)
+ /** Determine if the given [ExpandableNotificationRow] has been magnetically detached. */
+ fun isMagneticRowSwipeDetached(row: ExpandableNotificationRow): Boolean
+
/* Reset any roundness that magnetic targets may have */
fun resetRoundness()
@@ -104,12 +107,15 @@ interface MagneticNotificationRowManager {
/** Detaching threshold in dp */
const val MAGNETIC_DETACH_THRESHOLD_DP = 56
+ /** Re-attaching threshold in dp */
+ const val MAGNETIC_ATTACH_THRESHOLD_DP = 40
+
/* An empty implementation of a manager */
@JvmStatic
val Empty: MagneticNotificationRowManager
get() =
object : MagneticNotificationRowManager {
- override fun setSwipeThresholdPx(thresholdPx: Float) {}
+ override fun onDensityChange(density: Float) {}
override fun setMagneticAndRoundableTargets(
swipingRow: ExpandableNotificationRow,
@@ -127,6 +133,10 @@ interface MagneticNotificationRowManager {
velocity: Float?,
) {}
+ override fun isMagneticRowSwipeDetached(
+ row: ExpandableNotificationRow
+ ): Boolean = false
+
override fun resetRoundness() {}
override fun reset() {}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MagneticNotificationRowManagerImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MagneticNotificationRowManagerImpl.kt
index 5a23f7cc2861..5c52500b7f70 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MagneticNotificationRowManagerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MagneticNotificationRowManagerImpl.kt
@@ -47,6 +47,7 @@ constructor(
private set
private var magneticDetachThreshold = Float.POSITIVE_INFINITY
+ private var magneticAttachThreshold = 0f
// Has the roundable target been set for the magnetic view that is being swiped.
val isSwipedViewRoundableSet: Boolean
@@ -57,13 +58,25 @@ constructor(
SpringForce().setStiffness(DETACH_STIFFNESS).setDampingRatio(DETACH_DAMPING_RATIO)
private val snapForce =
SpringForce().setStiffness(SNAP_BACK_STIFFNESS).setDampingRatio(SNAP_BACK_DAMPING_RATIO)
+ private val attachForce =
+ SpringForce().setStiffness(ATTACH_STIFFNESS).setDampingRatio(ATTACH_DAMPING_RATIO)
// Multiplier applied to the translation of a row while swiped
val swipedRowMultiplier =
MAGNETIC_TRANSLATION_MULTIPLIERS[MAGNETIC_TRANSLATION_MULTIPLIERS.size / 2]
- override fun setSwipeThresholdPx(thresholdPx: Float) {
- magneticDetachThreshold = thresholdPx
+ /**
+ * An offset applied to input translation that increases on subsequent re-attachments of a
+ * detached magnetic view. This helps keep computations consistent when the drag gesture input
+ * and the swiped notification don't share the same origin point after a re-attaching animation.
+ */
+ private var translationOffset = 0f
+
+ override fun onDensityChange(density: Float) {
+ magneticDetachThreshold =
+ density * MagneticNotificationRowManager.MAGNETIC_DETACH_THRESHOLD_DP
+ magneticAttachThreshold =
+ density * MagneticNotificationRowManager.MAGNETIC_ATTACH_THRESHOLD_DP
}
override fun setMagneticAndRoundableTargets(
@@ -72,6 +85,7 @@ constructor(
sectionsManager: NotificationSectionsManager,
) {
if (currentState == State.IDLE) {
+ translationOffset = 0f
updateMagneticAndRoundableTargets(swipingRow, stackScrollLayout, sectionsManager)
currentState = State.TARGETS_SET
} else {
@@ -121,42 +135,41 @@ constructor(
val canTargetBeDismissed =
currentMagneticListeners.swipedListener()?.canRowBeDismissed() ?: false
+ val correctedTranslation = translation - translationOffset
when (currentState) {
State.IDLE -> {
logger.logMagneticRowTranslationNotSet(currentState, row.getLoggingKey())
return false
}
State.TARGETS_SET -> {
- pullTargets(translation, canTargetBeDismissed)
+ pullTargets(correctedTranslation, canTargetBeDismissed)
currentState = State.PULLING
}
State.PULLING -> {
- updateRoundness(translation)
+ updateRoundness(correctedTranslation)
if (canTargetBeDismissed) {
- pullDismissibleRow(translation)
+ pullDismissibleRow(correctedTranslation)
} else {
- pullTargets(translation, canSwipedBeDismissed = false)
+ pullTargets(correctedTranslation, canSwipedBeDismissed = false)
}
}
State.DETACHED -> {
- val swiped = currentMagneticListeners.swipedListener()
- swiped?.setMagneticTranslation(translation)
+ translateDetachedRow(correctedTranslation)
}
}
return true
}
- private fun updateRoundness(translation: Float) {
+ private fun updateRoundness(translation: Float, animate: Boolean = false) {
val normalizedTranslation = abs(swipedRowMultiplier * translation) / magneticDetachThreshold
notificationRoundnessManager.setRoundnessForAffectedViews(
/* roundness */ normalizedTranslation.coerceIn(0f, MAX_PRE_DETACH_ROUNDNESS),
- /* animate */ false,
+ animate,
)
}
private fun pullDismissibleRow(translation: Float) {
- val targetTranslation = swipedRowMultiplier * translation
- val crossedThreshold = abs(targetTranslation) >= magneticDetachThreshold
+ val crossedThreshold = abs(translation) >= magneticDetachThreshold
if (crossedThreshold) {
snapNeighborsBack()
currentMagneticListeners.swipedListener()?.let { detach(it, translation) }
@@ -232,9 +245,30 @@ constructor(
)
}
+ private fun translateDetachedRow(translation: Float) {
+ val crossedThreshold = abs(translation) <= magneticAttachThreshold
+ if (crossedThreshold) {
+ translationOffset += translation
+ updateRoundness(translation = 0f, animate = true)
+ currentMagneticListeners.swipedListener()?.let { attach(it) }
+ currentState = State.PULLING
+ } else {
+ val swiped = currentMagneticListeners.swipedListener()
+ swiped?.setMagneticTranslation(translation, trackEagerly = false)
+ }
+ }
+
+ private fun attach(listener: MagneticRowListener) {
+ listener.cancelMagneticAnimations()
+ listener.triggerMagneticForce(endTranslation = 0f, attachForce)
+ msdlPlayer.playToken(MSDLToken.SWIPE_THRESHOLD_INDICATOR)
+ }
+
override fun onMagneticInteractionEnd(row: ExpandableNotificationRow, velocity: Float?) {
+ translationOffset = 0f
if (row.isSwipedTarget()) {
when (currentState) {
+ State.TARGETS_SET -> currentState = State.IDLE
State.PULLING -> {
snapNeighborsBack(velocity)
currentState = State.IDLE
@@ -254,9 +288,13 @@ constructor(
}
}
+ override fun isMagneticRowSwipeDetached(row: ExpandableNotificationRow): Boolean =
+ row.isSwipedTarget() && currentState == State.DETACHED
+
override fun resetRoundness() = notificationRoundnessManager.clear()
override fun reset() {
+ translationOffset = 0f
currentMagneticListeners.forEach {
it?.cancelMagneticAnimations()
it?.cancelTranslationAnimations()
@@ -300,6 +338,8 @@ constructor(
private const val DETACH_DAMPING_RATIO = 0.95f
private const val SNAP_BACK_STIFFNESS = 550f
private const val SNAP_BACK_DAMPING_RATIO = 0.6f
+ private const val ATTACH_STIFFNESS = 800f
+ private const val ATTACH_DAMPING_RATIO = 0.95f
// Maximum value of corner roundness that gets applied during the pre-detach dragging
private const val MAX_PRE_DETACH_ROUNDNESS = 0.8f
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MagneticRowListener.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MagneticRowListener.kt
index 5959ef1e093b..344dab4369f2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MagneticRowListener.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MagneticRowListener.kt
@@ -21,8 +21,17 @@ import androidx.dynamicanimation.animation.SpringForce
/** A listener that responds to magnetic forces applied to an [ExpandableNotificationRow] */
interface MagneticRowListener {
- /** Set a translation due to a magnetic attachment. */
- fun setMagneticTranslation(translation: Float)
+ /**
+ * Set a translation due to a magnetic attachment.
+ *
+ * If a magnetic animation is running, [trackEagerly] decides if the new translation is applied
+ * immediately or if the animation finishes first. When applying the translation immediately,
+ * the change in translation must be greater than a touch slop threshold.
+ *
+ * @param[translation] Incoming gesture translation.
+ * @param[trackEagerly] Whether we eagerly track the incoming translation or not.
+ */
+ fun setMagneticTranslation(translation: Float, trackEagerly: Boolean = true)
/**
* Trigger the magnetic behavior when the row detaches or snaps back from its magnetic
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index 9fea75048e3e..afa988dd8e89 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -3220,8 +3220,7 @@ public class NotificationStackScrollLayout
updateAnimationState(child);
updateChronometerForChild(child);
if (child instanceof ExpandableNotificationRow row) {
- row.setDismissUsingRowTranslationX(mDismissUsingRowTranslationX);
-
+ row.setDismissUsingRowTranslationX(mDismissUsingRowTranslationX, /* force= */ true);
}
}
@@ -6077,7 +6076,9 @@ public class NotificationStackScrollLayout
if (mBlurRadius > 0) {
mBlurEffect =
RenderEffect.createBlurEffect(mBlurRadius, mBlurRadius, Shader.TileMode.CLAMP);
+ spewLog("Setting up blur RenderEffect for NotificationStackScrollLayout");
} else {
+ spewLog("Clearing the blur RenderEffect setup for NotificationStackScrollLayout");
mBlurEffect = null;
}
}
@@ -6157,7 +6158,7 @@ public class NotificationStackScrollLayout
View child = getChildAt(i);
if (child instanceof ExpandableNotificationRow) {
((ExpandableNotificationRow) child).setDismissUsingRowTranslationX(
- dismissUsingRowTranslationX);
+ dismissUsingRowTranslationX, /* force= */ false);
}
}
}
@@ -6253,6 +6254,7 @@ public class NotificationStackScrollLayout
@Override
protected void dispatchDraw(@NonNull Canvas canvas) {
if (mBlurEffect != null) {
+ spewLog("Applying blur RenderEffect to NotificationStackScrollLayout");
// reuse the cached RenderNode to blur
mBlurNode.setPosition(0, 0, canvas.getWidth(), canvas.getHeight());
mBlurNode.setRenderEffect(mBlurEffect);
@@ -7026,4 +7028,10 @@ public class NotificationStackScrollLayout
SceneContainerFlag.assertInLegacyMode();
mMaxTopPadding = maxTopPadding;
}
+
+ private void spewLog(String logMsg) {
+ if (SPEW) {
+ Log.v(TAG, logMsg);
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
index f3d8ee245540..66c9b17ef235 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
@@ -420,10 +420,15 @@ public class NotificationStackScrollLayoutController implements Dumpable {
return;
}
if (view instanceof ExpandableNotificationRow row) {
- mMetricsLogger.write(row.getEntry().getSbn().getLogMaker()
- .setCategory(MetricsEvent.ACTION_TOUCH_GEAR)
- .setType(MetricsEvent.TYPE_ACTION)
- );
+ StatusBarNotification sbn = NotificationBundleUi.isEnabled()
+ ? row.getEntryAdapter().getSbn()
+ : row.getEntryLegacy().getSbn();
+ if (sbn != null) {
+ mMetricsLogger.write(row.getEntry().getSbn().getLogMaker()
+ .setCategory(MetricsEvent.ACTION_TOUCH_GEAR)
+ .setType(MetricsEvent.TYPE_ACTION)
+ );
+ }
}
mNotificationGutsManager.openGuts(view, x, y, item);
}
@@ -440,9 +445,14 @@ public class NotificationStackScrollLayoutController implements Dumpable {
@Override
public void onMenuShown(View row) {
if (row instanceof ExpandableNotificationRow notificationRow) {
- mMetricsLogger.write(notificationRow.getEntry().getSbn().getLogMaker()
- .setCategory(MetricsEvent.ACTION_REVEAL_GEAR)
- .setType(MetricsEvent.TYPE_ACTION));
+ StatusBarNotification sbn = NotificationBundleUi.isEnabled()
+ ? notificationRow.getEntryAdapter().getSbn()
+ : notificationRow.getEntryLegacy().getSbn();
+ if (sbn != null) {
+ mMetricsLogger.write(notificationRow.getEntry().getSbn().getLogMaker()
+ .setCategory(MetricsEvent.ACTION_REVEAL_GEAR)
+ .setType(MetricsEvent.TYPE_ACTION));
+ }
mSwipeHelper.onMenuShown(row);
mNotificationGutsManager.closeAndSaveGuts(true /* removeLeavebehind */,
false /* force */, false /* removeControls */, -1 /* x */, -1 /* y */,
@@ -486,15 +496,22 @@ public class NotificationStackScrollLayoutController implements Dumpable {
}
@Override
+ public boolean isMagneticViewDetached(View view) {
+ if (view instanceof ExpandableNotificationRow row) {
+ return mMagneticNotificationRowManager.isMagneticRowSwipeDetached(row);
+ } else {
+ return false;
+ }
+ }
+
+ @Override
public float getTotalTranslationLength(View animView) {
return mView.getTotalTranslationLength(animView);
}
@Override
public void onDensityScaleChange(float density) {
- mMagneticNotificationRowManager.setSwipeThresholdPx(
- density * MagneticNotificationRowManager.MAGNETIC_DETACH_THRESHOLD_DP
- );
+ mMagneticNotificationRowManager.onDensityChange(density);
}
@Override
@@ -1348,11 +1365,15 @@ public class NotificationStackScrollLayoutController implements Dumpable {
*/
public void setBlurRadius(float blurRadius) {
if (blurRadius > 0.0f) {
+ debugLog(
+ "Setting blur RenderEffect for NotificationStackScrollLayoutController with "
+ + "radius " + blurRadius);
mView.setRenderEffect(RenderEffect.createBlurEffect(
blurRadius,
blurRadius,
Shader.TileMode.CLAMP));
} else {
+ debugLog("Resetting blur RenderEffect for NotificationStackScrollLayoutController");
mView.setRenderEffect(null);
}
}
@@ -2168,4 +2189,10 @@ public class NotificationStackScrollLayoutController implements Dumpable {
&& !mSwipeHelper.isSwiping();
}
}
+
+ private void debugLog(String msg) {
+ if (DEBUG) {
+ Log.d(TAG, msg);
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java
index c5a846e1da05..5105e55b0a5c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java
@@ -255,12 +255,13 @@ class NotificationSwipeHelper extends SwipeHelper implements NotificationSwipeAc
int menuSnapTarget = menuRow.getMenuSnapTarget();
boolean isNonFalseMenuRevealingGesture =
isMenuRevealingGestureAwayFromMenu && !isFalseGesture();
+ boolean isMagneticViewDetached = mCallback.isMagneticViewDetached(animView);
if ((isNonDismissGestureTowardsMenu || isNonFalseMenuRevealingGesture)
&& menuSnapTarget != 0) {
// Menu has not been snapped to previously and this is menu revealing gesture
snapOpen(animView, menuSnapTarget, velocity);
menuRow.onSnapOpen();
- } else if (isDismissGesture && !gestureTowardsMenu) {
+ } else if (isDismissGesture && (!gestureTowardsMenu || isMagneticViewDetached)) {
dismiss(animView, velocity);
menuRow.onDismiss();
} else {
@@ -272,6 +273,7 @@ class NotificationSwipeHelper extends SwipeHelper implements NotificationSwipeAc
private void handleSwipeFromOpenState(MotionEvent ev, View animView, float velocity,
NotificationMenuRowPlugin menuRow) {
boolean isDismissGesture = isDismissGesture(ev);
+ boolean isMagneticViewDetached = mCallback.isMagneticViewDetached(animView);
final boolean withinSnapMenuThreshold =
menuRow.isWithinSnapMenuThreshold();
@@ -280,7 +282,7 @@ class NotificationSwipeHelper extends SwipeHelper implements NotificationSwipeAc
// Haven't moved enough to unsnap from the menu
menuRow.onSnapOpen();
snapOpen(animView, menuRow.getMenuSnapTarget(), velocity);
- } else if (isDismissGesture && !menuRow.shouldSnapBack()) {
+ } else if (isDismissGesture && (!menuRow.shouldSnapBack() || isMagneticViewDetached)) {
// Only dismiss if we're not moving towards the menu
dismiss(animView, velocity);
menuRow.onDismiss();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/view/NotificationStatsLoggerImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/view/NotificationStatsLoggerImpl.kt
index 5689230f6bed..fdf06e1e1770 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/view/NotificationStatsLoggerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/view/NotificationStatsLoggerImpl.kt
@@ -319,7 +319,7 @@ private fun List<ActiveNotificationModel>.toNotificationProto(): Notifications.N
Notifications.Notification().apply {
uid = notification.uid
packageName = notification.packageName
- notification.instanceId?.let { instanceId = it }
+ notification.instanceId?.let { instanceId = it.id }
// TODO(b/308623704) check if we can set groupInstanceId as well
isGroupSummary = notification.isGroupSummary
section = NotificationPanelLogger.toNotificationSection(notification.bucket)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModel.kt
index 000b3f643e9a..12b48eba7a96 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModel.kt
@@ -89,6 +89,17 @@ constructor(
source = shadeModeInteractor.shadeMode.map { getQuickSettingsShadeContentKey(it) },
)
+ /**
+ * Whether the current touch gesture is overscroll. If true, it means the NSSL has already
+ * consumed part of the gesture.
+ */
+ val isCurrentGestureOverscroll: Boolean by
+ hydrator.hydratedStateOf(
+ traceName = "isCurrentGestureOverscroll",
+ initialValue = false,
+ source = interactor.isCurrentGestureOverscroll
+ )
+
/** DEBUG: whether the placeholder should be made slightly visible for positional debugging. */
val isVisualDebuggingEnabled: Boolean = featureFlags.isEnabled(Flags.NSSL_DEBUG_LINES)
@@ -157,13 +168,6 @@ constructor(
val syntheticScroll: Flow<Float> =
interactor.syntheticScroll.dumpWhileCollecting("syntheticScroll")
- /**
- * Whether the current touch gesture is overscroll. If true, it means the NSSL has already
- * consumed part of the gesture.
- */
- val isCurrentGestureOverscroll: Flow<Boolean> =
- interactor.isCurrentGestureOverscroll.dumpWhileCollecting("isCurrentGestureOverScroll")
-
/** Whether remote input is currently active for any notification. */
val isRemoteInputActive = remoteInputInteractor.isRemoteInputActive
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
index e4e56c5de65b..8a5b22183563 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
@@ -527,7 +527,8 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp
break;
case MODE_SHOW_BOUNCER:
Trace.beginSection("MODE_SHOW_BOUNCER");
- mKeyguardViewController.showPrimaryBouncer(true);
+ mKeyguardViewController.showPrimaryBouncer(true,
+ "BiometricUnlockController#MODE_SHOW_BOUNCER");
Trace.endSection();
break;
case MODE_WAKE_AND_UNLOCK_FROM_DREAM:
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
index a4ee4ad6f6ec..fe367a3927ed 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
@@ -31,11 +31,11 @@ import static androidx.lifecycle.Lifecycle.State.RESUMED;
import static com.android.systemui.Dependency.TIME_TICK_HANDLER_NAME;
import static com.android.systemui.Flags.keyboardShortcutHelperRewrite;
-import static com.android.systemui.Flags.lightRevealMigration;
import static com.android.systemui.Flags.relockWithPowerButtonImmediately;
import static com.android.systemui.Flags.statusBarSignalPolicyRefactor;
import static com.android.systemui.charging.WirelessChargingAnimation.UNKNOWN_BATTERY_LEVEL;
import static com.android.systemui.flags.Flags.SHORTCUT_LIST_SEARCH_LAYOUT;
+import static com.android.systemui.shared.Flags.ambientAod;
import static com.android.systemui.statusbar.StatusBarState.SHADE;
import android.annotation.Nullable;
@@ -90,7 +90,6 @@ import androidx.annotation.NonNull;
import androidx.lifecycle.Lifecycle;
import androidx.lifecycle.LifecycleRegistry;
-import com.android.app.viewcapture.ViewCaptureAwareWindowManager;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.colorextraction.ColorExtractor;
import com.android.internal.logging.MetricsLogger;
@@ -233,6 +232,7 @@ import com.android.systemui.util.WallpaperController;
import com.android.systemui.util.concurrency.DelayableExecutor;
import com.android.systemui.util.concurrency.MessageRouter;
import com.android.systemui.util.kotlin.JavaAdapter;
+import com.android.systemui.utils.windowmanager.WindowManagerProvider;
import com.android.systemui.volume.VolumeComponent;
import com.android.systemui.wallet.controller.QuickAccessWalletController;
import com.android.wm.shell.bubbles.Bubbles;
@@ -597,7 +597,6 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
private final EmergencyGestureIntentFactory mEmergencyGestureIntentFactory;
- private final ViewCaptureAwareWindowManager mViewCaptureAwareWindowManager;
private final QuickAccessWalletController mWalletController;
/**
@@ -713,8 +712,9 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
BrightnessMirrorShowingRepository brightnessMirrorShowingRepository,
GlanceableHubContainerController glanceableHubContainerController,
EmergencyGestureIntentFactory emergencyGestureIntentFactory,
- ViewCaptureAwareWindowManager viewCaptureAwareWindowManager,
- QuickAccessWalletController walletController
+ QuickAccessWalletController walletController,
+ WindowManager windowManager,
+ WindowManagerProvider windowManagerProvider
) {
mContext = context;
mNotificationsController = notificationsController;
@@ -852,7 +852,8 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
mLightRevealScrim = lightRevealScrim;
- mViewCaptureAwareWindowManager = viewCaptureAwareWindowManager;
+ mWindowManager = windowManager;
+ mWindowManagerProvider = windowManagerProvider;
}
private void initBubbles(Bubbles bubbles) {
@@ -880,8 +881,6 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
mColorExtractor.addOnColorsChangedListener(mOnColorsChangedListener);
- mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
-
mDisplay = mContext.getDisplay();
mDisplayId = mDisplay.getDisplayId();
updateDisplaySize();
@@ -980,7 +979,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
@Override
public void onKeyguardGoingAwayChanged() {
- if (lightRevealMigration()) {
+ if (ambientAod()) {
// This code path is not used if the KeyguardTransitionRepository is managing
// the lightreveal scrim.
return;
@@ -1716,7 +1715,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
mNotificationShadeWindowController.setRequestTopUi(false, TAG);
}
}, /* isDozing= */ false, RippleShape.CIRCLE,
- sUiEventLogger, mViewCaptureAwareWindowManager).show(animationDelay);
+ sUiEventLogger, mWindowManager, mWindowManagerProvider).show(animationDelay);
}
@Override
@@ -2407,11 +2406,12 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
}
if (needsBouncer) {
- Log.d(TAG, "showBouncerOrLockScreenIfKeyguard, showingBouncer");
+ var reason = "CentralSurfacesImpl#showBouncerOrLockScreenIfKeyguard";
if (SceneContainerFlag.isEnabled()) {
- mStatusBarKeyguardViewManager.showPrimaryBouncer(true /* scrimmed */);
+ mStatusBarKeyguardViewManager.showPrimaryBouncer(true /* scrimmed */,
+ reason);
} else {
- mStatusBarKeyguardViewManager.showBouncer(true /* scrimmed */);
+ mStatusBarKeyguardViewManager.showBouncer(true /* scrimmed */, reason);
}
}
}
@@ -2446,7 +2446,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
return;
}
- if (lightRevealMigration()) {
+ if (ambientAod()) {
return;
}
@@ -2875,6 +2875,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
protected WindowManager mWindowManager;
protected IWindowManager mWindowManagerService;
private final IDreamManager mDreamManager;
+ private final WindowManagerProvider mWindowManagerProvider;
protected Display mDisplay;
private int mDisplayId;
@@ -2911,9 +2912,9 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
protected void toggleKeyboardShortcuts(int deviceId) {
if (shouldUseTabletKeyboardShortcuts()) {
- KeyboardShortcutListSearch.toggle(mContext, deviceId);
+ KeyboardShortcutListSearch.toggle(mContext, deviceId, mWindowManagerProvider);
} else {
- KeyboardShortcuts.toggle(mContext, deviceId);
+ KeyboardShortcuts.toggle(mContext, deviceId, mWindowManagerProvider);
}
}
@@ -2927,7 +2928,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
private boolean shouldUseTabletKeyboardShortcuts() {
return mFeatureFlags.isEnabled(SHORTCUT_LIST_SEARCH_LAYOUT)
- && Utilities.isLargeScreen(mContext);
+ && Utilities.isLargeScreen(mWindowManager, mContext.getResources());
}
private void clearNotificationEffects() {
@@ -3103,7 +3104,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
@Override
public void onDozeAmountChanged(float linear, float eased) {
- if (!lightRevealMigration()
+ if (!ambientAod()
&& !(mLightRevealScrim.getRevealEffect() instanceof CircleReveal)) {
// If wakeAndUnlocking, this is handled in AuthRippleInteractor
if (!mBiometricUnlockController.isWakeAndUnlock()) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java
index 0ba4aabcb0e2..34c193d18814 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java
@@ -27,13 +27,19 @@ import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
+import androidx.collection.MutableIntObjectMap;
+
import com.android.internal.statusbar.StatusBarIcon;
+import com.android.systemui.Flags;
import com.android.systemui.demomode.DemoMode;
+import com.android.systemui.kairos.ExperimentalKairosApi;
+import com.android.systemui.kairos.KairosNetwork;
import com.android.systemui.plugins.DarkIconDispatcher;
import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver;
import com.android.systemui.res.R;
import com.android.systemui.statusbar.StatusBarIconView;
import com.android.systemui.statusbar.StatusIconDisplayable;
+import com.android.systemui.statusbar.pipeline.mobile.ui.MobileUiAdapterKairos;
import com.android.systemui.statusbar.pipeline.mobile.ui.MobileViewLogger;
import com.android.systemui.statusbar.pipeline.mobile.ui.view.ModernStatusBarMobileView;
import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.MobileIconsViewModel;
@@ -41,10 +47,20 @@ import com.android.systemui.statusbar.pipeline.shared.ui.view.ModernStatusBarVie
import com.android.systemui.statusbar.pipeline.wifi.ui.view.ModernStatusBarWifiView;
import com.android.systemui.statusbar.pipeline.wifi.ui.viewmodel.LocationBasedWifiViewModel;
+import dagger.Lazy;
+
+import kotlin.OptIn;
+import kotlin.Pair;
+
+import kotlinx.coroutines.CoroutineScope;
+import kotlinx.coroutines.Job;
+
import java.util.ArrayList;
import java.util.List;
+import java.util.concurrent.CancellationException;
//TODO: This should be a controller, not its own view
+@OptIn(markerClass = ExperimentalKairosApi.class)
public class DemoStatusIcons extends StatusIconContainer implements DemoMode, DarkReceiver {
private static final String TAG = "DemoStatusIcons";
@@ -60,15 +76,27 @@ public class DemoStatusIcons extends StatusIconContainer implements DemoMode, Da
private final MobileIconsViewModel mMobileIconsViewModel;
private final StatusBarLocation mLocation;
+ private final Lazy<MobileUiAdapterKairos> mMobileUiAdapterKairos;
+ private final KairosNetwork mKairosNetwork;
+ private final CoroutineScope mAppScope;
+
+ private final MutableIntObjectMap<Job> mBindingJobs = new MutableIntObjectMap<>();
+
public DemoStatusIcons(
LinearLayout statusIcons,
MobileIconsViewModel mobileIconsViewModel,
StatusBarLocation location,
- int iconSize
+ int iconSize,
+ Lazy<MobileUiAdapterKairos> mobileUiAdapterKairos,
+ KairosNetwork kairosNetwork,
+ CoroutineScope appScope
) {
super(statusIcons.getContext());
mStatusIcons = statusIcons;
mIconSize = iconSize;
+ mMobileUiAdapterKairos = mobileUiAdapterKairos;
+ mKairosNetwork = kairosNetwork;
+ mAppScope = appScope;
mColor = DarkIconDispatcher.DEFAULT_ICON_TINT;
mContrastColor = DarkIconDispatcher.DEFAULT_INVERSE_ICON_TINT;
mMobileIconsViewModel = mobileIconsViewModel;
@@ -236,24 +264,46 @@ public class DemoStatusIcons extends StatusIconContainer implements DemoMode, Da
/**
* Add a {@link ModernStatusBarMobileView}
+ *
* @param mobileContext possibly mcc/mnc overridden mobile context
- * @param subId the subscriptionId for this mobile view
+ * @param subId the subscriptionId for this mobile view
*/
public void addModernMobileView(
Context mobileContext,
MobileViewLogger mobileViewLogger,
int subId) {
Log.d(TAG, "addModernMobileView (subId=" + subId + ")");
- ModernStatusBarMobileView view = ModernStatusBarMobileView.constructAndBind(
- mobileContext,
- mobileViewLogger,
- "mobile",
- mMobileIconsViewModel.viewModelForSub(subId, mLocation)
- );
-
- // mobile always goes at the end
- mModernMobileViews.add(view);
- addView(view, getChildCount(), createLayoutParams());
+ if (Flags.statusBarMobileIconKairos()) {
+ Pair<ModernStatusBarMobileView, Job> viewAndJob =
+ ModernStatusBarMobileView.constructAndBind(
+ mobileContext,
+ mobileViewLogger,
+ "mobile",
+ mMobileUiAdapterKairos.get().getMobileIconsViewModel().viewModelForSub(
+ subId, mLocation),
+ mAppScope,
+ subId,
+ mLocation,
+ mKairosNetwork
+ );
+ ModernStatusBarMobileView view = viewAndJob.getFirst();
+ mBindingJobs.put(subId, viewAndJob.getSecond());
+ // mobile always goes at the end
+ mModernMobileViews.add(view);
+ addView(view, getChildCount(), createLayoutParams());
+ } else {
+ ModernStatusBarMobileView view =
+ ModernStatusBarMobileView.constructAndBind(
+ mobileContext,
+ mobileViewLogger,
+ "mobile",
+ mMobileIconsViewModel.viewModelForSub(subId, mLocation)
+ );
+
+ // mobile always goes at the end
+ mModernMobileViews.add(view);
+ addView(view, getChildCount(), createLayoutParams());
+ }
}
/**
@@ -299,6 +349,12 @@ public class DemoStatusIcons extends StatusIconContainer implements DemoMode, Da
ModernStatusBarMobileView mobileView = matchingModernMobileView(
(ModernStatusBarMobileView) view);
if (mobileView != null) {
+ if (Flags.statusBarMobileIconKairos()) {
+ Job job = mBindingJobs.remove(mobileView.getSubId());
+ if (job != null) {
+ job.cancel(new CancellationException());
+ }
+ }
removeView(mobileView);
mModernMobileViews.remove(mobileView);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java
index 36193bd87ce2..3c144625b685 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java
@@ -49,6 +49,7 @@ import com.android.keyguard.CarrierTextController;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.KeyguardUpdateMonitorCallback;
import com.android.keyguard.logging.KeyguardLogger;
+import com.android.systemui.Flags;
import com.android.systemui.battery.BatteryMeterViewController;
import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor;
import com.android.systemui.dagger.qualifiers.Background;
@@ -475,12 +476,14 @@ public class KeyguardStatusBarViewController extends ViewController<KeyguardStat
UserHandle.USER_ALL);
updateUserSwitcher();
onThemeChanged();
- collectFlow(mView, mCommunalSceneInteractor.isCommunalVisible(), mCommunalConsumer,
- mCoroutineDispatcher);
- collectFlow(mView, mLockscreenToHubTransitionViewModel.getStatusBarAlpha(),
- mToGlanceableHubStatusBarAlphaConsumer, mCoroutineDispatcher);
- collectFlow(mView, mHubToLockscreenTransitionViewModel.getStatusBarAlpha(),
- mFromGlanceableHubStatusBarAlphaConsumer, mCoroutineDispatcher);
+ if (!Flags.glanceableHubV2()) {
+ collectFlow(mView, mCommunalSceneInteractor.isCommunalVisible(), mCommunalConsumer,
+ mCoroutineDispatcher);
+ collectFlow(mView, mLockscreenToHubTransitionViewModel.getStatusBarAlpha(),
+ mToGlanceableHubStatusBarAlphaConsumer, mCoroutineDispatcher);
+ collectFlow(mView, mHubToLockscreenTransitionViewModel.getStatusBarAlpha(),
+ mFromGlanceableHubStatusBarAlphaConsumer, mCoroutineDispatcher);
+ }
if (NewStatusBarIcons.isEnabled()) {
ComposeView batteryComposeView = new ComposeView(mContext);
UnifiedBatteryViewBinder.bind(
@@ -645,7 +648,7 @@ public class KeyguardStatusBarViewController extends ViewController<KeyguardStat
&& !mDozing
&& !hideForBypass
&& !mDisableStateTracker.isDisabled()
- && (!mCommunalShowing || mExplicitAlpha != -1)
+ && (Flags.glanceableHubV2() || (!mCommunalShowing || mExplicitAlpha != -1))
? View.VISIBLE : View.INVISIBLE;
updateViewState(newAlpha, newVisibility);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt
index caf8a43b2aaf..67a7eee2977a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt
@@ -104,6 +104,7 @@ private constructor(
// intercepted. See [View.OnTouchEvent]
if (event.source == InputDevice.SOURCE_MOUSE) {
if (event.action == MotionEvent.ACTION_UP) {
+ dispatchEventToShadeDisplayPolicy(event)
v.performClick()
shadeController.animateExpandShade()
}
@@ -113,6 +114,15 @@ private constructor(
}
}
+ private fun dispatchEventToShadeDisplayPolicy(event: MotionEvent) {
+ if (ShadeWindowGoesAround.isEnabled) {
+ // Notify the shade display policy that the status bar was touched. This may cause
+ // the shade to change display if the touch was in a display different than the shade
+ // one.
+ lazyStatusBarShadeDisplayPolicy.get().onStatusBarTouched(event, mView.width)
+ }
+ }
+
private val configurationListener =
object : ConfigurationController.ConfigurationListener {
override fun onDensityOrFontScaleChanged() {
@@ -232,9 +242,6 @@ private constructor(
!upOrCancel || shadeController.isExpandedVisible,
)
}
- if (ShadeWindowGoesAround.isEnabled && event.action == MotionEvent.ACTION_DOWN) {
- lazyStatusBarShadeDisplayPolicy.get().onStatusBarTouched(event, mView.width)
- }
}
private fun addDarkReceivers() {
@@ -249,6 +256,9 @@ private constructor(
inner class PhoneStatusBarViewTouchHandler : Gefingerpoken {
override fun onInterceptTouchEvent(event: MotionEvent): Boolean {
+ if (event.action == MotionEvent.ACTION_DOWN) {
+ dispatchEventToShadeDisplayPolicy(event)
+ }
return if (Flags.statusBarSwipeOverChip()) {
shadeViewController.handleExternalInterceptTouch(event)
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
index f3d72027238f..d68f7df79cd5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -538,10 +538,7 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump
private void handleBlurSupportedChanged(boolean isBlurSupported) {
this.mIsBlurSupported = isBlurSupported;
if (Flags.bouncerUiRevamp()) {
- // TODO: animate blur fallback when the bouncer is pulled up.
- for (ScrimState state : ScrimState.values()) {
- state.setDefaultScrimAlpha(getDefaultScrimAlpha(true));
- }
+ updateDefaultScrimAlphas();
if (isBlurSupported) {
ScrimState.BOUNCER_SCRIMMED.setNotifBlurRadius(mBlurConfig.getMaxBlurRadiusPx());
} else {
@@ -549,17 +546,7 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump
}
}
if (Flags.notificationShadeBlur()) {
- float inFrontAlpha = mInFrontAlpha;
- float behindAlpha = mBehindAlpha;
- float notifAlpha = mNotificationsAlpha;
-
mState.prepare(mState);
- applyState();
- startScrimAnimation(mScrimBehind, behindAlpha);
- startScrimAnimation(mNotificationsScrim, notifAlpha);
- startScrimAnimation(mScrimInFront, inFrontAlpha);
- dispatchBackScrimState(mScrimBehind.getViewAlpha());
- } else if (Flags.bouncerUiRevamp()) {
applyAndDispatchState();
}
}
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 8c44fe56d269..512340913de2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -669,7 +669,8 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
* show if any subsequent events are to be handled.
*/
if (!SceneContainerFlag.isEnabled() && beginShowingBouncer(event)) {
- mPrimaryBouncerInteractor.show(/* isScrimmed= */false);
+ mPrimaryBouncerInteractor.show(/* isScrimmed= */false,
+ TAG + "#onPanelExpansionChanged");
}
if (!primaryBouncerIsOrWillBeShowing()) {
@@ -714,7 +715,8 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
* Shows the notification keyguard or the bouncer depending on
* {@link #needsFullscreenBouncer()}.
*/
- protected void showBouncerOrKeyguard(boolean hideBouncerWhenShowing, boolean isFalsingReset) {
+ protected void showBouncerOrKeyguard(boolean hideBouncerWhenShowing, boolean isFalsingReset,
+ String reason) {
boolean showBouncer = needsFullscreenBouncer() && !mDozing;
if (Flags.simPinRaceConditionOnRestart()) {
showBouncer = showBouncer && !mIsSleeping;
@@ -726,11 +728,11 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
mCentralSurfaces.hideKeyguard();
mSceneInteractorLazy.get().showOverlay(
Overlays.Bouncer,
- "StatusBarKeyguardViewManager.showBouncerOrKeyguard"
+ TAG + "#showBouncerOrKeyguard"
);
} else {
if (Flags.simPinRaceConditionOnRestart()) {
- if (mPrimaryBouncerInteractor.show(/* isScrimmed= */ true)) {
+ if (mPrimaryBouncerInteractor.show(/* isScrimmed= */ true, reason)) {
mAttemptsToShowBouncer = 0;
mCentralSurfaces.hideKeyguard();
} else {
@@ -744,19 +746,19 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
+ mAttemptsToShowBouncer++);
mExecutor.executeDelayed(() ->
showBouncerOrKeyguard(hideBouncerWhenShowing,
- isFalsingReset),
+ isFalsingReset, reason),
500);
}
}
} else {
mCentralSurfaces.hideKeyguard();
- mPrimaryBouncerInteractor.show(/* isScrimmed= */ true);
+ mPrimaryBouncerInteractor.show(/* isScrimmed= */ true, reason);
}
}
} else if (!isFalsingReset) {
// Falsing resets can cause this to flicker, so don't reset in this case
Log.i(TAG, "Sim bouncer is already showing, issuing a refresh");
- mPrimaryBouncerInteractor.show(/* isScrimmed= */ true);
+ mPrimaryBouncerInteractor.show(/* isScrimmed= */ true, reason);
}
} else {
@@ -776,7 +778,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
* false when the user will be dragging it and translation should be deferred
* {@see KeyguardBouncer#show(boolean, boolean)}
*/
- public void showBouncer(boolean scrimmed) {
+ public void showBouncer(boolean scrimmed, String reason) {
if (SceneContainerFlag.isEnabled()) {
mDeviceEntryInteractorLazy.get().attemptDeviceEntry();
return;
@@ -787,7 +789,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
mAlternateBouncerInteractor.forceShow();
updateAlternateBouncerShowing(mAlternateBouncerInteractor.isVisibleState());
} else {
- showPrimaryBouncer(scrimmed);
+ showPrimaryBouncer(scrimmed, reason);
}
}
@@ -810,8 +812,10 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
*
* @param scrimmed true when the bouncer should show scrimmed, false when the user will be
* dragging it and translation should be deferred {@see KeyguardBouncer#show(boolean, boolean)}
+ * @param reason string description for what is causing the bouncer to be requested
*/
- public void showPrimaryBouncer(boolean scrimmed) {
+ @Override
+ public void showPrimaryBouncer(boolean scrimmed, String reason) {
hideAlternateBouncer(
/* updateScrim= */ false,
// When the scene framework is on, don't ever clear the pending dismiss action from
@@ -823,7 +827,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
"primary bouncer requested"
);
} else {
- mPrimaryBouncerInteractor.show(scrimmed);
+ mPrimaryBouncerInteractor.show(scrimmed, reason);
}
}
updateStates();
@@ -870,7 +874,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
);
}
- showBouncer(true);
+ showBouncer(true, TAG + "#dismissWithAction");
Trace.endSection();
return;
}
@@ -919,10 +923,11 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
if (SceneContainerFlag.isEnabled()) {
mSceneInteractorLazy.get().showOverlay(
Overlays.Bouncer,
- "StatusBarKeyguardViewManager.dismissWithAction"
+ TAG + "#dismissWithAction"
);
} else {
- mPrimaryBouncerInteractor.show(/* isScrimmed= */ true);
+ mPrimaryBouncerInteractor.show(/* isScrimmed= */ true,
+ TAG + "#dismissWithAction, afterKeyguardGone");
}
} else {
// after authentication success, run dismiss action with the option to defer
@@ -932,10 +937,11 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
if (SceneContainerFlag.isEnabled()) {
mSceneInteractorLazy.get().showOverlay(
Overlays.Bouncer,
- "StatusBarKeyguardViewManager.dismissWithAction"
+ TAG + "#dismissWithAction"
);
} else {
- mPrimaryBouncerInteractor.show(/* isScrimmed= */ true);
+ mPrimaryBouncerInteractor.show(/* isScrimmed= */ true,
+ TAG + "#dismissWithAction");
}
// bouncer will handle the dismiss action, so we no longer need to track it here
mAfterKeyguardGoneAction = null;
@@ -992,7 +998,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
}
}
} else {
- showBouncerOrKeyguard(hideBouncerWhenShowing, isFalsingReset);
+ showBouncerOrKeyguard(hideBouncerWhenShowing, isFalsingReset, "reset");
}
if (!SceneContainerFlag.isEnabled() && hideBouncerWhenShowing && isBouncerShowing()) {
hideAlternateBouncer(true);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java
index 8389aab4aac8..85fc9d4589c0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java
@@ -156,7 +156,8 @@ public class StatusBarRemoteInputCallback implements Callback, Callbacks,
if (!row.isPinned()) {
mStatusBarStateController.setLeaveOpenOnKeyguardHide(true);
}
- mStatusBarKeyguardViewManager.showBouncer(true /* scrimmed */);
+ mStatusBarKeyguardViewManager.showBouncer(true /* scrimmed */,
+ "StatusBarRemoteInputCallback#onLockedRemoteInput");
mPendingRemoteInputView = clicked;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt
index 0d43789e95a8..8890db3cd943 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt
@@ -18,7 +18,6 @@ import com.android.internal.jank.InteractionJankMonitor
import com.android.internal.jank.InteractionJankMonitor.CUJ_SCREEN_OFF
import com.android.internal.jank.InteractionJankMonitor.CUJ_SCREEN_OFF_SHOW_AOD
import com.android.systemui.DejankUtils
-import com.android.systemui.Flags.lightRevealMigration
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.keyguard.KeyguardViewMediator
@@ -26,6 +25,7 @@ import com.android.systemui.keyguard.WakefulnessLifecycle
import com.android.systemui.shade.ShadeViewController
import com.android.systemui.shade.domain.interactor.PanelExpansionInteractor
import com.android.systemui.shade.domain.interactor.ShadeLockscreenInteractor
+import com.android.systemui.shared.Flags.ambientAod
import com.android.systemui.statusbar.CircleReveal
import com.android.systemui.statusbar.LightRevealScrim
import com.android.systemui.statusbar.NotificationShadeWindowController
@@ -100,7 +100,7 @@ constructor(
duration = LIGHT_REVEAL_ANIMATION_DURATION
interpolator = Interpolators.LINEAR
addUpdateListener {
- if (lightRevealMigration()) return@addUpdateListener
+ if (ambientAod()) return@addUpdateListener
if (lightRevealScrim.revealEffect !is CircleReveal) {
lightRevealScrim.revealAmount = it.animatedValue as Float
}
@@ -116,7 +116,7 @@ constructor(
addListener(
object : AnimatorListenerAdapter() {
override fun onAnimationCancel(animation: Animator) {
- if (lightRevealMigration()) return
+ if (ambientAod()) return
if (lightRevealScrim.revealEffect !is CircleReveal) {
lightRevealScrim.revealAmount = 1f
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt
index 61b7d80a8c85..207f0ecfb7ed 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt
@@ -24,6 +24,7 @@ import android.content.Context
import android.view.View
import androidx.annotation.VisibleForTesting
import com.android.app.tracing.coroutines.launchTraced as launch
+import com.android.internal.logging.InstanceId
import com.android.systemui.CoreStartable
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
@@ -38,7 +39,7 @@ import com.android.systemui.statusbar.chips.ui.view.ChipChronometer
import com.android.systemui.statusbar.data.repository.StatusBarModeRepositoryStore
import com.android.systemui.statusbar.gesture.SwipeStatusBarAwayGestureHandler
import com.android.systemui.statusbar.notification.domain.interactor.ActiveNotificationsInteractor
-import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel
+import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModels
import com.android.systemui.statusbar.notification.shared.ActiveNotificationModel
import com.android.systemui.statusbar.notification.shared.CallType
import com.android.systemui.statusbar.phone.ongoingcall.data.repository.OngoingCallRepository
@@ -165,6 +166,7 @@ constructor(
// is visible (we issue [OngoingCallModel.NoCall] below in that case), so this can
// be safely made false.
isAppVisible = false,
+ notificationInstanceId = currentInfo.instanceId,
)
} else {
return OngoingCallModel.NoCall
@@ -222,6 +224,7 @@ constructor(
notifModel.contentIntent,
notifModel.uid,
notifModel.appName,
+ notifModel.instanceId,
notifModel.promotedContent,
isOngoing = true,
statusBarSwipedAway = callNotificationInfo?.statusBarSwipedAway ?: false,
@@ -343,11 +346,12 @@ constructor(
val intent: PendingIntent?,
val uid: Int,
val appName: String,
+ val instanceId: InstanceId?,
/**
* If the call notification also meets promoted notification criteria, this field is filled
* in with the content related to promotion. Otherwise null.
*/
- val promotedContent: PromotedNotificationContentModel?,
+ val promotedContent: PromotedNotificationContentModels?,
/** True if the call is currently ongoing (as opposed to incoming, screening, etc.). */
val isOngoing: Boolean,
/** True if the user has swiped away the status bar while in this phone call. */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/domain/interactor/OngoingCallInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/domain/interactor/OngoingCallInteractor.kt
index bed9e7cf4646..ff9b7d5cf513 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/domain/interactor/OngoingCallInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/domain/interactor/OngoingCallInteractor.kt
@@ -166,6 +166,7 @@ constructor(
appName = model.appName,
promotedContent = model.promotedContent,
isAppVisible = isVisible,
+ notificationInstanceId = model.instanceId,
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/shared/model/OngoingCallModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/shared/model/OngoingCallModel.kt
index 322dfff8f144..e20d76453cd6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/shared/model/OngoingCallModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/shared/model/OngoingCallModel.kt
@@ -17,8 +17,9 @@
package com.android.systemui.statusbar.phone.ongoingcall.shared.model
import android.app.PendingIntent
+import com.android.internal.logging.InstanceId
import com.android.systemui.statusbar.StatusBarIconView
-import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel
+import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModels
/** Represents the state of any ongoing calls. */
sealed interface OngoingCallModel {
@@ -40,6 +41,8 @@ sealed interface OngoingCallModel {
* @property promotedContent if the call notification also meets promoted notification criteria,
* this field is filled in with the content related to promotion. Otherwise null.
* @property isAppVisible whether the app to which the call belongs is currently visible.
+ * @property notificationInstanceId an optional per-chip ID used for logging. Should stay the
+ * same throughout the lifetime of a single chip.
*/
data class InCall(
val startTimeMs: Long,
@@ -47,7 +50,8 @@ sealed interface OngoingCallModel {
val intent: PendingIntent?,
val notificationKey: String,
val appName: String,
- val promotedContent: PromotedNotificationContentModel?,
+ val promotedContent: PromotedNotificationContentModels?,
val isAppVisible: Boolean,
+ val notificationInstanceId: InstanceId?,
) : OngoingCallModel
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ui/DarkIconManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ui/DarkIconManager.java
index 8d314eeb8493..6dcc0ada8c65 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ui/DarkIconManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ui/DarkIconManager.java
@@ -19,6 +19,9 @@ package com.android.systemui.statusbar.phone.ui;
import android.widget.LinearLayout;
import com.android.internal.statusbar.StatusBarIcon;
+import com.android.systemui.dagger.qualifiers.Application;
+import com.android.systemui.kairos.ExperimentalKairosApi;
+import com.android.systemui.kairos.KairosNetwork;
import com.android.systemui.plugins.DarkIconDispatcher;
import com.android.systemui.statusbar.StatusIconDisplayable;
import com.android.systemui.statusbar.connectivity.ui.MobileContextProvider;
@@ -26,13 +29,20 @@ import com.android.systemui.statusbar.phone.DemoStatusIcons;
import com.android.systemui.statusbar.phone.StatusBarIconHolder;
import com.android.systemui.statusbar.phone.StatusBarLocation;
import com.android.systemui.statusbar.pipeline.mobile.ui.MobileUiAdapter;
+import com.android.systemui.statusbar.pipeline.mobile.ui.MobileUiAdapterKairos;
import com.android.systemui.statusbar.pipeline.wifi.ui.WifiUiAdapter;
+import dagger.Lazy;
import dagger.assisted.Assisted;
import dagger.assisted.AssistedFactory;
import dagger.assisted.AssistedInject;
+import kotlin.OptIn;
+
+import kotlinx.coroutines.CoroutineScope;
+
/** Version of {@link IconManager} that observes state from the {@link DarkIconDispatcher}. */
+@OptIn(markerClass = ExperimentalKairosApi.class)
public class DarkIconManager extends IconManager {
private final DarkIconDispatcher mDarkIconDispatcher;
private final int mIconHorizontalMargin;
@@ -43,13 +53,21 @@ public class DarkIconManager extends IconManager {
@Assisted StatusBarLocation location,
WifiUiAdapter wifiUiAdapter,
MobileUiAdapter mobileUiAdapter,
+ Lazy<MobileUiAdapterKairos> mobileUiAdapterKairos,
MobileContextProvider mobileContextProvider,
+ KairosNetwork kairosNetwork,
+ @Application CoroutineScope appScope,
@Assisted DarkIconDispatcher darkIconDispatcher) {
- super(linearLayout, location, wifiUiAdapter, mobileUiAdapter, mobileContextProvider);
- mIconHorizontalMargin =
- mContext.getResources()
- .getDimensionPixelSize(
- com.android.systemui.res.R.dimen.status_bar_icon_horizontal_margin);
+ super(linearLayout,
+ location,
+ wifiUiAdapter,
+ mobileUiAdapter,
+ mobileUiAdapterKairos,
+ mobileContextProvider,
+ kairosNetwork,
+ appScope);
+ mIconHorizontalMargin = mContext.getResources().getDimensionPixelSize(
+ com.android.systemui.res.R.dimen.status_bar_icon_horizontal_margin);
mDarkIconDispatcher = darkIconDispatcher;
}
@@ -104,7 +122,7 @@ public class DarkIconManager extends IconManager {
super.exitDemoMode();
}
- /** */
+ /** */
@AssistedFactory
public interface Factory {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ui/IconManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ui/IconManager.java
index fd16c6090cb1..862931af7504 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ui/IconManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ui/IconManager.java
@@ -24,12 +24,19 @@ import static com.android.systemui.statusbar.phone.StatusBarIconHolder.TYPE_WIFI
import android.annotation.Nullable;
import android.content.Context;
import android.os.Bundle;
+import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
+import androidx.annotation.OptIn;
+import androidx.collection.MutableIntObjectMap;
+
import com.android.internal.statusbar.StatusBarIcon;
import com.android.internal.statusbar.StatusBarIcon.Shape;
+import com.android.systemui.Flags;
import com.android.systemui.demomode.DemoModeCommandReceiver;
+import com.android.systemui.kairos.ExperimentalKairosApi;
+import com.android.systemui.kairos.KairosNetwork;
import com.android.systemui.modes.shared.ModesUiIcons;
import com.android.systemui.statusbar.BaseStatusBarFrameLayout;
import com.android.systemui.statusbar.StatusBarIconView;
@@ -40,6 +47,7 @@ import com.android.systemui.statusbar.phone.StatusBarIconHolder;
import com.android.systemui.statusbar.phone.StatusBarIconHolder.BindableIconHolder;
import com.android.systemui.statusbar.phone.StatusBarLocation;
import com.android.systemui.statusbar.pipeline.mobile.ui.MobileUiAdapter;
+import com.android.systemui.statusbar.pipeline.mobile.ui.MobileUiAdapterKairos;
import com.android.systemui.statusbar.pipeline.mobile.ui.binder.MobileIconsBinder;
import com.android.systemui.statusbar.pipeline.mobile.ui.view.ModernStatusBarMobileView;
import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.MobileIconsViewModel;
@@ -49,20 +57,34 @@ import com.android.systemui.statusbar.pipeline.wifi.ui.view.ModernStatusBarWifiV
import com.android.systemui.statusbar.pipeline.wifi.ui.viewmodel.LocationBasedWifiViewModel;
import com.android.systemui.util.Assert;
+import dagger.Lazy;
+
+import kotlin.Pair;
+
+import kotlinx.coroutines.CoroutineScope;
+import kotlinx.coroutines.Job;
+
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.concurrent.CancellationException;
/**
* Turns info from StatusBarIconController into ImageViews in a ViewGroup.
*/
+@OptIn(markerClass = ExperimentalKairosApi.class)
public class IconManager implements DemoModeCommandReceiver {
protected final ViewGroup mGroup;
private final MobileContextProvider mMobileContextProvider;
private final LocationBasedWifiViewModel mWifiViewModel;
private final MobileIconsViewModel mMobileIconsViewModel;
+ private final Lazy<MobileUiAdapterKairos> mMobileUiAdapterKairos;
+ private final KairosNetwork mKairosNetwork;
+ private final CoroutineScope mAppScope;
+ private final MutableIntObjectMap<Job> mBindingJobs = new MutableIntObjectMap<>();
+
/**
* Stores the list of bindable icons that have been added, keyed on slot name. This ensures
* we don't accidentally add the same bindable icon twice.
@@ -87,12 +109,17 @@ public class IconManager implements DemoModeCommandReceiver {
StatusBarLocation location,
WifiUiAdapter wifiUiAdapter,
MobileUiAdapter mobileUiAdapter,
- MobileContextProvider mobileContextProvider
+ Lazy<MobileUiAdapterKairos> mobileUiAdapterKairos,
+ MobileContextProvider mobileContextProvider,
+ KairosNetwork kairosNetwork,
+ CoroutineScope appScope
) {
mGroup = group;
mMobileContextProvider = mobileContextProvider;
mContext = group.getContext();
mLocation = location;
+ mKairosNetwork = kairosNetwork;
+ mAppScope = appScope;
reloadDimens();
@@ -101,6 +128,9 @@ public class IconManager implements DemoModeCommandReceiver {
mMobileIconsViewModel = mobileUiAdapter.getMobileIconsViewModel();
MobileIconsBinder.bind(mGroup, mMobileIconsViewModel);
+
+ mMobileUiAdapterKairos = mobileUiAdapterKairos;
+
mWifiViewModel = wifiUiAdapter.bindGroup(mGroup, mLocation);
}
@@ -150,7 +180,7 @@ public class IconManager implements DemoModeCommandReceiver {
case TYPE_MOBILE_NEW -> addNewMobileIcon(index, slot, holder.getTag());
case TYPE_BINDABLE ->
// Safe cast, since only BindableIconHolders can set this tag on themselves
- addBindableIcon((BindableIconHolder) holder, index);
+ addBindableIcon((BindableIconHolder) holder, index);
default -> null;
};
}
@@ -223,13 +253,30 @@ public class IconManager implements DemoModeCommandReceiver {
private ModernStatusBarMobileView onCreateModernStatusBarMobileView(
String slot, int subId) {
Context mobileContext = mMobileContextProvider.getMobileContextForSub(subId, mContext);
- return ModernStatusBarMobileView
- .constructAndBind(
- mobileContext,
- mMobileIconsViewModel.getLogger(),
- slot,
- mMobileIconsViewModel.viewModelForSub(subId, mLocation)
- );
+ if (Flags.statusBarMobileIconKairos()) {
+ Pair<ModernStatusBarMobileView, Job> viewAndJob =
+ ModernStatusBarMobileView.constructAndBind(
+ mobileContext,
+ mMobileUiAdapterKairos.get().getMobileIconsViewModel().getLogger(),
+ slot,
+ mMobileUiAdapterKairos.get().getMobileIconsViewModel()
+ .viewModelForSub(subId, mLocation),
+ mAppScope,
+ subId,
+ mLocation,
+ mKairosNetwork
+ );
+ mBindingJobs.put(subId, viewAndJob.getSecond());
+ return viewAndJob.getFirst();
+ } else {
+ return ModernStatusBarMobileView
+ .constructAndBind(
+ mobileContext,
+ mMobileIconsViewModel.getLogger(),
+ slot,
+ mMobileIconsViewModel.viewModelForSub(subId, mLocation)
+ );
+ }
}
protected LinearLayout.LayoutParams onCreateLayoutParams(Shape shape) {
@@ -253,6 +300,15 @@ public class IconManager implements DemoModeCommandReceiver {
if (mIsInDemoMode) {
mDemoStatusIcons.onRemoveIcon((StatusIconDisplayable) mGroup.getChildAt(viewIndex));
}
+ if (Flags.statusBarMobileIconKairos()) {
+ View view = mGroup.getChildAt(viewIndex);
+ if (view instanceof ModernStatusBarMobileView) {
+ Job bindingJob = mBindingJobs.remove(((ModernStatusBarMobileView) view).getSubId());
+ if (bindingJob != null) {
+ bindingJob.cancel(new CancellationException());
+ }
+ }
+ }
mGroup.removeViewAt(viewIndex);
}
@@ -326,7 +382,10 @@ public class IconManager implements DemoModeCommandReceiver {
(LinearLayout) mGroup,
mMobileIconsViewModel,
mLocation,
- mIconSize
+ mIconSize,
+ mMobileUiAdapterKairos,
+ mKairosNetwork,
+ mAppScope
);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ui/TintedIconManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ui/TintedIconManager.java
index e520148e925a..da59fc073c86 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ui/TintedIconManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ui/TintedIconManager.java
@@ -20,19 +20,30 @@ import android.view.View;
import android.view.ViewGroup;
import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.dagger.qualifiers.Application;
+import com.android.systemui.kairos.ExperimentalKairosApi;
+import com.android.systemui.kairos.KairosNetwork;
import com.android.systemui.statusbar.StatusIconDisplayable;
import com.android.systemui.statusbar.connectivity.ui.MobileContextProvider;
import com.android.systemui.statusbar.phone.DemoStatusIcons;
import com.android.systemui.statusbar.phone.StatusBarIconHolder;
import com.android.systemui.statusbar.phone.StatusBarLocation;
import com.android.systemui.statusbar.pipeline.mobile.ui.MobileUiAdapter;
+import com.android.systemui.statusbar.pipeline.mobile.ui.MobileUiAdapterKairos;
import com.android.systemui.statusbar.pipeline.wifi.ui.WifiUiAdapter;
+import dagger.Lazy;
+
+import kotlin.OptIn;
+
+import kotlinx.coroutines.CoroutineScope;
+
import javax.inject.Inject;
/**
* Version of {@link IconManager} that can tint the icons to a particular color.
*/
+@OptIn(markerClass = ExperimentalKairosApi.class)
public class TintedIconManager extends IconManager {
// The main tint, used as the foreground in non layer drawables
private int mColor;
@@ -44,13 +55,17 @@ public class TintedIconManager extends IconManager {
StatusBarLocation location,
WifiUiAdapter wifiUiAdapter,
MobileUiAdapter mobileUiAdapter,
- MobileContextProvider mobileContextProvider
+ Lazy<MobileUiAdapterKairos> mobileUiAdapterKairos,
+ MobileContextProvider mobileContextProvider,
+ KairosNetwork kairosNetwork,
+ CoroutineScope appScope
) {
super(group,
location,
wifiUiAdapter,
mobileUiAdapter,
- mobileContextProvider);
+ mobileUiAdapterKairos,
+ mobileContextProvider, kairosNetwork, appScope);
}
@Override
@@ -99,16 +114,25 @@ public class TintedIconManager extends IconManager {
private final WifiUiAdapter mWifiUiAdapter;
private final MobileContextProvider mMobileContextProvider;
private final MobileUiAdapter mMobileUiAdapter;
+ private final Lazy<MobileUiAdapterKairos> mMobileUiAdapterKairos;
+ private final KairosNetwork mKairosNetwork;
+ private final CoroutineScope mAppScope;
@Inject
public Factory(
WifiUiAdapter wifiUiAdapter,
MobileUiAdapter mobileUiAdapter,
- MobileContextProvider mobileContextProvider
+ MobileContextProvider mobileContextProvider,
+ Lazy<MobileUiAdapterKairos> mobileUiAdapterKairos,
+ KairosNetwork kairosNetwork,
+ @Application CoroutineScope appScope
) {
mWifiUiAdapter = wifiUiAdapter;
mMobileUiAdapter = mobileUiAdapter;
mMobileContextProvider = mobileContextProvider;
+ mMobileUiAdapterKairos = mobileUiAdapterKairos;
+ mKairosNetwork = kairosNetwork;
+ mAppScope = appScope;
}
/** Creates a new {@link TintedIconManager} for the given view group and location. */
@@ -118,7 +142,10 @@ public class TintedIconManager extends IconManager {
location,
mWifiUiAdapter,
mMobileUiAdapter,
- mMobileContextProvider);
+ mMobileUiAdapterKairos,
+ mMobileContextProvider,
+ mKairosNetwork,
+ mAppScope);
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt
index db1977b3ff45..93489e90fb85 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt
@@ -49,6 +49,7 @@ import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIc
import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconsInteractorKairosImpl
import com.android.systemui.statusbar.pipeline.mobile.ui.MobileUiAdapter
import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.MobileIconsViewModel
+import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.MobileIconsViewModelKairos
import com.android.systemui.statusbar.pipeline.mobile.util.MobileMappingsProxy
import com.android.systemui.statusbar.pipeline.mobile.util.MobileMappingsProxyImpl
import com.android.systemui.statusbar.pipeline.mobile.util.SubscriptionManagerProxy
@@ -94,6 +95,7 @@ import kotlinx.coroutines.flow.Flow
MobileRepositorySwitcherKairos.Module::class,
MobileConnectionsRepositoryKairosImpl.Module::class,
MobileIconsInteractorKairosImpl.Module::class,
+ MobileIconsViewModelKairos.Module::class,
MobileConnectionRepositoryKairosFactoryImpl.Module::class,
MobileConnectionsRepositoryKairosAdapter.Module::class,
MobileIconsInteractorKairosAdapter.Module::class,
@@ -217,6 +219,7 @@ abstract class StatusBarPipelineModule {
fun provideFirstMobileSubShowingNetworkTypeIconProvider(
mobileIconsViewModel: MobileIconsViewModel
): Supplier<Flow<Boolean>> {
+ // TODO: kairos-ify
return Supplier<Flow<Boolean>> {
mobileIconsViewModel.firstMobileSubShowingNetworkTypeIcon
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepositoryKairos.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepositoryKairos.kt
index 1a8ca9577bd7..f4afc248d11a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepositoryKairos.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/FullMobileConnectionRepositoryKairos.kt
@@ -22,6 +22,7 @@ import com.android.systemui.KairosBuilder
import com.android.systemui.kairos.BuildSpec
import com.android.systemui.kairos.ExperimentalKairosApi
import com.android.systemui.kairos.State
+import com.android.systemui.kairos.combine
import com.android.systemui.kairos.flatMap
import com.android.systemui.kairosBuilder
import com.android.systemui.log.table.TableLogBuffer
@@ -55,9 +56,15 @@ constructor(
@Assisted private val isCarrierMerged: State<Boolean>,
) : MobileConnectionRepositoryKairos, KairosBuilder by kairosBuilder() {
+ private var dumpCache: DumpCache? = null
+
init {
onActivated {
logDiffsForTable(isCarrierMerged, tableLogBuffer, columnName = "isCarrierMerged")
+ combine(isCarrierMerged, activeRepo) { isCarrierMerged, activeRepo ->
+ DumpCache(isCarrierMerged, activeRepo)
+ }
+ .observe { dumpCache = it }
}
}
@@ -198,13 +205,6 @@ constructor(
override val isInEcmMode: State<Boolean> = activeRepo.flatMap { it.isInEcmMode }
- private var dumpCache: DumpCache? = null
-
- private data class DumpCache(
- val isCarrierMerged: Boolean,
- val activeRepo: MobileConnectionRepositoryKairos,
- )
-
fun dump(pw: PrintWriter) {
val cache = dumpCache ?: return
val ipw = IndentingPrintWriter(pw, " ")
@@ -227,6 +227,11 @@ constructor(
ipw.decreaseIndent()
}
+ private data class DumpCache(
+ val isCarrierMerged: Boolean,
+ val activeRepo: MobileConnectionRepositoryKairos,
+ )
+
@AssistedFactory
interface Factory {
fun create(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryKairosImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryKairosImpl.kt
index e46815954e64..e6c29214e3d6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryKairosImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryKairosImpl.kt
@@ -131,6 +131,8 @@ constructor(
private val mobileRepoFactory: Lazy<ConnectionRepoFactory>,
) : MobileConnectionsRepositoryKairos, Dumpable, KairosBuilder by kairosBuilder() {
+ private var dumpCache: DumpCache? = null
+
init {
dumpManager.registerNormalDumpable("MobileConnectionsRepositoryKairos", this)
}
@@ -253,6 +255,7 @@ constructor(
.asIncremental()
.mapValues { (subId, sub) -> mobileRepoFactory.get().create(subId) }
.applyLatestSpecForKey()
+ .apply { observe { dumpCache = DumpCache(it) } }
}
private val telephonyManagerState: State<Pair<Int?, Set<Int>>> = buildState {
@@ -479,10 +482,6 @@ constructor(
profileClass = profileClass,
)
- private var dumpCache: DumpCache? = null
-
- private data class DumpCache(val repos: Map<Int, FullMobileConnectionRepositoryKairos>)
-
override fun dump(pw: PrintWriter, args: Array<String>) {
val cache = dumpCache ?: return
val ipw = IndentingPrintWriter(pw, " ")
@@ -494,10 +493,16 @@ constructor(
ipw.println("Connections (${cache.repos.size} total):")
ipw.increaseIndent()
- cache.repos.values.forEach { it.dump(ipw) }
+ cache.repos.values.forEach {
+ if (it is FullMobileConnectionRepositoryKairos) {
+ it.dump(ipw)
+ }
+ }
ipw.decreaseIndent()
}
+ private data class DumpCache(val repos: Map<Int, MobileConnectionRepositoryKairos>)
+
fun interface ConnectionRepoFactory {
fun create(subId: Int): BuildSpec<MobileConnectionRepositoryKairos>
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractor.kt
index 0eabb4ecee84..af4e61afaaaa 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractor.kt
@@ -61,31 +61,31 @@ interface MobileIconInteractor {
* consider this connection to be serving data, and thus want to show a network type icon, when
* data is connected. Other data connection states would typically cause us not to show the icon
*/
- val isDataConnected: StateFlow<Boolean>
+ val isDataConnected: Flow<Boolean>
/** True if we consider this connection to be in service, i.e. can make calls */
- val isInService: StateFlow<Boolean>
+ val isInService: Flow<Boolean>
/** True if this connection is emergency only */
- val isEmergencyOnly: StateFlow<Boolean>
+ val isEmergencyOnly: Flow<Boolean>
/** Observable for the data enabled state of this connection */
- val isDataEnabled: StateFlow<Boolean>
+ val isDataEnabled: Flow<Boolean>
/** True if the RAT icon should always be displayed and false otherwise. */
- val alwaysShowDataRatIcon: StateFlow<Boolean>
+ val alwaysShowDataRatIcon: Flow<Boolean>
/** Canonical representation of the current mobile signal strength as a triangle. */
- val signalLevelIcon: StateFlow<SignalIconModel>
+ val signalLevelIcon: Flow<SignalIconModel>
/** Observable for RAT type (network type) indicator */
- val networkTypeIconGroup: StateFlow<NetworkTypeIconModel>
+ val networkTypeIconGroup: Flow<NetworkTypeIconModel>
/** Whether or not to show the slice attribution */
- val showSliceAttribution: StateFlow<Boolean>
+ val showSliceAttribution: Flow<Boolean>
/** True if this connection is satellite-based */
- val isNonTerrestrial: StateFlow<Boolean>
+ val isNonTerrestrial: Flow<Boolean>
/**
* Provider name for this network connection. The name can be one of 3 values:
@@ -95,7 +95,7 @@ interface MobileIconInteractor {
* override in [connectionInfo.operatorAlphaShort], a value that is derived from
* [ServiceState]
*/
- val networkName: StateFlow<NetworkNameModel>
+ val networkName: Flow<NetworkNameModel>
/**
* Provider name for this network connection. The name can be one of 3 values:
@@ -108,26 +108,26 @@ interface MobileIconInteractor {
* TODO(b/296600321): De-duplicate this field with [networkName] after determining the data
* provided is identical
*/
- val carrierName: StateFlow<String>
+ val carrierName: Flow<String>
/** True if there is only one active subscription. */
- val isSingleCarrier: StateFlow<Boolean>
+ val isSingleCarrier: Flow<Boolean>
/**
* True if this connection is considered roaming. The roaming bit can come from [ServiceState],
* or directly from the telephony manager's CDMA ERI number value. Note that we don't consider a
* connection to be roaming while carrier network change is active
*/
- val isRoaming: StateFlow<Boolean>
+ val isRoaming: Flow<Boolean>
/** See [MobileIconsInteractor.isForceHidden]. */
val isForceHidden: Flow<Boolean>
/** See [MobileConnectionRepository.isAllowedDuringAirplaneMode]. */
- val isAllowedDuringAirplaneMode: StateFlow<Boolean>
+ val isAllowedDuringAirplaneMode: Flow<Boolean>
/** True when in carrier network change mode */
- val carrierNetworkChangeActive: StateFlow<Boolean>
+ val carrierNetworkChangeActive: Flow<Boolean>
}
/** Interactor for a single mobile connection. This connection _should_ have one subscription ID */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractorKairos.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractorKairos.kt
index a9399593973b..3d58f84e1f91 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractorKairos.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractorKairos.kt
@@ -48,6 +48,8 @@ interface MobileIconInteractorKairos {
/** The table log created for this connection */
val tableLogBuffer: TableLogBuffer
+ val subscriptionId: Int
+
/** The current mobile data activity */
val activity: State<DataActivityModel>
@@ -146,6 +148,9 @@ class MobileIconInteractorKairosImpl(
private val carrierIdOverrides: MobileIconCarrierIdOverrides =
MobileIconCarrierIdOverridesImpl(),
) : MobileIconInteractorKairos, KairosBuilder by kairosBuilder() {
+ override val subscriptionId: Int
+ get() = connectionRepository.subId
+
override val tableLogBuffer: TableLogBuffer
get() = connectionRepository.tableLogBuffer
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorKairosAdapter.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorKairosAdapter.kt
index 87877b3e9f43..6b9c5374ef2e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorKairosAdapter.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorKairosAdapter.kt
@@ -32,11 +32,20 @@ import com.android.systemui.kairos.map
import com.android.systemui.kairos.mapValues
import com.android.systemui.kairos.toColdConflatedFlow
import com.android.systemui.kairosBuilder
+import com.android.systemui.log.table.TableLogBuffer
+import com.android.systemui.log.table.TableLogBufferFactory
+import com.android.systemui.statusbar.pipeline.mobile.data.model.NetworkNameModel
import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel
import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionsRepository
import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionsRepositoryKairos
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.prod.FullMobileConnectionRepository.Factory.Companion.MOBILE_CONNECTION_BUFFER_SIZE
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.prod.FullMobileConnectionRepository.Factory.Companion.tableBufferLogName
+import com.android.systemui.statusbar.pipeline.mobile.domain.model.NetworkTypeIconModel
+import com.android.systemui.statusbar.pipeline.mobile.domain.model.SignalIconModel
import com.android.systemui.statusbar.pipeline.mobile.util.MobileMappingsProxy
+import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityModel
import com.android.systemui.statusbar.policy.data.repository.UserSetupRepository
+import com.android.systemui.utils.coroutines.flow.flatMapLatestConflated
import dagger.Provides
import dagger.multibindings.ElementsIntoSet
import javax.inject.Inject
@@ -45,6 +54,8 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.emptyFlow
+import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
@ExperimentalKairosApi
@@ -60,6 +71,7 @@ constructor(
context: Context,
mobileMappingsProxy: MobileMappingsProxy,
private val userSetupRepo: UserSetupRepository,
+ private val logFactory: TableLogBufferFactory,
) : MobileIconsInteractor, KairosBuilder by kairosBuilder() {
private val interactorsBySubIdK = buildIncremental {
@@ -158,7 +170,37 @@ constructor(
get() = repo.isDeviceEmergencyCallCapable
override fun getMobileConnectionInteractorForSubId(subId: Int): MobileIconInteractor =
- interactorsBySubId.value[subId] ?: error("Unknown subscription id: $subId")
+ object : MobileIconInteractor {
+ override val tableLogBuffer: TableLogBuffer =
+ logFactory.getOrCreate(tableBufferLogName(subId), MOBILE_CONNECTION_BUFFER_SIZE)
+ override val activity: Flow<DataActivityModel> = latest { activity }
+ override val mobileIsDefault: Flow<Boolean> = latest { mobileIsDefault }
+ override val isDataConnected: Flow<Boolean> = latest { isDataConnected }
+ override val isInService: Flow<Boolean> = latest { isInService }
+ override val isEmergencyOnly: Flow<Boolean> = latest { isEmergencyOnly }
+ override val isDataEnabled: Flow<Boolean> = latest { isDataEnabled }
+ override val alwaysShowDataRatIcon: Flow<Boolean> = latest { alwaysShowDataRatIcon }
+ override val signalLevelIcon: Flow<SignalIconModel> = latest { signalLevelIcon }
+ override val networkTypeIconGroup: Flow<NetworkTypeIconModel> = latest {
+ networkTypeIconGroup
+ }
+ override val showSliceAttribution: Flow<Boolean> = latest { showSliceAttribution }
+ override val isNonTerrestrial: Flow<Boolean> = latest { isNonTerrestrial }
+ override val networkName: Flow<NetworkNameModel> = latest { networkName }
+ override val carrierName: Flow<String> = latest { carrierName }
+ override val isSingleCarrier: Flow<Boolean> = latest { isSingleCarrier }
+ override val isRoaming: Flow<Boolean> = latest { isRoaming }
+ override val isForceHidden: Flow<Boolean> = latest { isForceHidden }
+ override val isAllowedDuringAirplaneMode: Flow<Boolean> = latest {
+ isAllowedDuringAirplaneMode
+ }
+ override val carrierNetworkChangeActive: Flow<Boolean> = latest {
+ carrierNetworkChangeActive
+ }
+
+ private fun <T> latest(block: MobileIconInteractor.() -> Flow<T>): Flow<T> =
+ interactorsBySubId.flatMapLatestConflated { it[subId]?.block() ?: emptyFlow() }
+ }
@dagger.Module
object Module {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/MobileUiAdapterKairos.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/MobileUiAdapterKairos.kt
new file mode 100644
index 000000000000..9881b354d8d9
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/MobileUiAdapterKairos.kt
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.pipeline.mobile.ui
+
+import com.android.systemui.Dumpable
+import com.android.systemui.KairosActivatable
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dump.DumpManager
+import com.android.systemui.kairos.BuildScope
+import com.android.systemui.kairos.ExperimentalKairosApi
+import com.android.systemui.kairos.awaitClose
+import com.android.systemui.kairos.combine
+import com.android.systemui.kairos.launchEffect
+import com.android.systemui.shade.carrier.ShadeCarrierGroupController
+import com.android.systemui.statusbar.phone.ui.StatusBarIconController
+import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconsInteractorKairos
+import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.MobileIconsViewModelKairos
+import java.io.PrintWriter
+import javax.inject.Inject
+
+/**
+ * This class is intended to provide a context to collect on the
+ * [MobileIconsInteractorKairos.filteredSubscriptions] data source and supply a state flow that can
+ * control [StatusBarIconController] to keep the old UI in sync with the new data source.
+ *
+ * It also provides a mechanism to create a top-level view model for each IconManager to know about
+ * the list of available mobile lines of service for which we want to show icons.
+ */
+@ExperimentalKairosApi
+@SysUISingleton
+class MobileUiAdapterKairos
+@Inject
+constructor(
+ private val iconController: StatusBarIconController,
+ val mobileIconsViewModel: MobileIconsViewModelKairos,
+ private val logger: MobileViewLogger,
+ dumpManager: DumpManager,
+) : KairosActivatable, Dumpable {
+
+ init {
+ dumpManager.registerNormalDumpable(this)
+ }
+
+ private var isCollecting: Boolean = false
+ private var lastValue: List<Int>? = null
+
+ private var shadeCarrierGroupController: ShadeCarrierGroupController? = null
+
+ override fun BuildScope.activate() {
+ launchEffect {
+ isCollecting = true
+ awaitClose { isCollecting = false }
+ }
+ // Start notifying the icon controller of subscriptions
+ combine(mobileIconsViewModel.subscriptionIds, mobileIconsViewModel.isStackable) { a, b ->
+ Pair(a, b)
+ }
+ .observe { (subIds, isStackable) ->
+ logger.logUiAdapterSubIdsSentToIconController(subIds, isStackable)
+ lastValue = subIds
+ if (isStackable) {
+ // Passing an empty list to remove pre-existing mobile icons.
+ // StackedMobileBindableIcon will show the stacked icon instead.
+ iconController.setNewMobileIconSubIds(emptyList())
+ } else {
+ iconController.setNewMobileIconSubIds(subIds)
+ }
+ shadeCarrierGroupController?.updateModernMobileIcons(subIds)
+ }
+ }
+
+ /** Set the [ShadeCarrierGroupController] to notify of subscription updates */
+ fun setShadeCarrierGroupController(controller: ShadeCarrierGroupController) {
+ shadeCarrierGroupController = controller
+ }
+
+ override fun dump(pw: PrintWriter, args: Array<out String>) {
+ pw.println("isCollecting=$isCollecting")
+ pw.println("Last values sent to icon controller: $lastValue")
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/MobileViewLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/MobileViewLogger.kt
index 4c2849de34ee..dec26886e063 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/MobileViewLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/MobileViewLogger.kt
@@ -20,10 +20,12 @@ import android.view.View
import com.android.systemui.Dumpable
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dump.DumpManager
+import com.android.systemui.kairos.ExperimentalKairosApi
import com.android.systemui.log.LogBuffer
import com.android.systemui.log.core.LogLevel
import com.android.systemui.statusbar.pipeline.dagger.MobileViewLog
import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.LocationBasedMobileViewModel
+import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.LocationBasedMobileViewModelKairos
import java.io.PrintWriter
import javax.inject.Inject
@@ -52,14 +54,17 @@ constructor(@MobileViewLog private val buffer: LogBuffer, dumpManager: DumpManag
)
}
- fun logNewViewBinding(view: View, viewModel: LocationBasedMobileViewModel) {
+ fun logNewViewBinding(view: View, viewModel: LocationBasedMobileViewModel) =
+ logNewViewBinding(view, viewModel, viewModel.location.name)
+
+ fun logNewViewBinding(view: View, viewModel: Any, location: String) {
buffer.log(
TAG,
LogLevel.INFO,
{
str1 = view.getIdForLogging()
str2 = viewModel.getIdForLogging()
- str3 = viewModel.location.name
+ str3 = location
},
{ "New view binding. viewId=$str1, viewModelId=$str2, viewModelLocation=$str3" },
)
@@ -93,6 +98,36 @@ constructor(@MobileViewLog private val buffer: LogBuffer, dumpManager: DumpManag
)
}
+ @OptIn(ExperimentalKairosApi::class)
+ fun logCollectionStarted(view: View, viewModel: LocationBasedMobileViewModelKairos) {
+ collectionStatuses[view.getIdForLogging()] = true
+ buffer.log(
+ TAG,
+ LogLevel.INFO,
+ {
+ str1 = view.getIdForLogging()
+ str2 = viewModel.getIdForLogging()
+ str3 = viewModel.location.name
+ },
+ { "Collection started. viewId=$str1, viewModelId=$str2, viewModelLocation=$str3" },
+ )
+ }
+
+ @OptIn(ExperimentalKairosApi::class)
+ fun logCollectionStopped(view: View, viewModel: LocationBasedMobileViewModelKairos) {
+ collectionStatuses[view.getIdForLogging()] = false
+ buffer.log(
+ TAG,
+ LogLevel.INFO,
+ {
+ str1 = view.getIdForLogging()
+ str2 = viewModel.getIdForLogging()
+ str3 = viewModel.location.name
+ },
+ { "Collection stopped. viewId=$str1, viewModelId=$str2, viewModelLocation=$str3" },
+ )
+ }
+
override fun dump(pw: PrintWriter, args: Array<out String>) {
pw.println("Collection statuses per view:---")
collectionStatuses.forEach { viewId, isCollecting ->
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/StackedMobileBindableIcon.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/StackedMobileBindableIcon.kt
index fa9fa4c1366f..bda76b72c08a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/StackedMobileBindableIcon.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/StackedMobileBindableIcon.kt
@@ -19,22 +19,28 @@ package com.android.systemui.statusbar.pipeline.mobile.ui
import android.content.Context
import com.android.settingslib.flags.Flags
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.kairos.ExperimentalKairosApi
+import com.android.systemui.kairos.KairosNetwork
import com.android.systemui.statusbar.core.StatusBarRootModernization
import com.android.systemui.statusbar.pipeline.icons.shared.model.BindableIcon
import com.android.systemui.statusbar.pipeline.icons.shared.model.ModernStatusBarViewCreator
import com.android.systemui.statusbar.pipeline.mobile.ui.binder.StackedMobileIconBinder
import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.MobileIconsViewModel
-import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.StackedMobileIconViewModel
+import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.StackedMobileIconViewModelImpl
+import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.StackedMobileIconViewModelKairos
import com.android.systemui.statusbar.pipeline.shared.ui.view.SingleBindableStatusBarComposeIconView
import javax.inject.Inject
+@OptIn(ExperimentalKairosApi::class)
@SysUISingleton
class StackedMobileBindableIcon
@Inject
constructor(
context: Context,
mobileIconsViewModel: MobileIconsViewModel,
- viewModelFactory: StackedMobileIconViewModel.Factory,
+ viewModelFactory: StackedMobileIconViewModelImpl.Factory,
+ kairosViewModelFactory: StackedMobileIconViewModelKairos.Factory,
+ kairosNetwork: KairosNetwork,
) : BindableIcon {
override val slot: String =
context.getString(com.android.internal.R.string.status_bar_stacked_mobile)
@@ -42,7 +48,13 @@ constructor(
override val initializer = ModernStatusBarViewCreator { context ->
SingleBindableStatusBarComposeIconView.createView(context).also { view ->
view.initView(slot) {
- StackedMobileIconBinder.bind(view, mobileIconsViewModel, viewModelFactory)
+ StackedMobileIconBinder.bind(
+ view,
+ mobileIconsViewModel,
+ viewModelFactory,
+ kairosViewModelFactory,
+ kairosNetwork,
+ )
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/binder/MobileIconBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/binder/MobileIconBinder.kt
index 0eef2e1ca685..0abd6d8d66b3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/binder/MobileIconBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/binder/MobileIconBinder.kt
@@ -50,7 +50,7 @@ import kotlinx.coroutines.awaitCancellation
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.distinctUntilChanged
-private data class Colors(@ColorInt val tint: Int, @ColorInt val contrast: Int)
+data class MobileIconColors(@ColorInt val tint: Int, @ColorInt val contrast: Int)
object MobileIconBinder {
/** Binds the view to the view-model, continuing to update the former based on the latter */
@@ -80,9 +80,9 @@ object MobileIconBinder {
@StatusBarIconView.VisibleState
val visibilityState: MutableStateFlow<Int> = MutableStateFlow(initialVisibilityState)
- val iconTint: MutableStateFlow<Colors> =
+ val iconTint: MutableStateFlow<MobileIconColors> =
MutableStateFlow(
- Colors(
+ MobileIconColors(
tint = DarkIconDispatcher.DEFAULT_ICON_TINT,
contrast = DarkIconDispatcher.DEFAULT_INVERSE_ICON_TINT,
)
@@ -291,7 +291,7 @@ object MobileIconBinder {
}
override fun onIconTintChanged(newTint: Int, contrastTint: Int) {
- iconTint.value = Colors(tint = newTint, contrast = contrastTint)
+ iconTint.value = MobileIconColors(tint = newTint, contrast = contrastTint)
}
override fun onDecorTintChanged(newTint: Int) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/binder/MobileIconBinderKairos.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/binder/MobileIconBinderKairos.kt
new file mode 100644
index 000000000000..1078ae343572
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/binder/MobileIconBinderKairos.kt
@@ -0,0 +1,290 @@
+/*
+ * Copyright (C) 2025 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.pipeline.mobile.ui.binder
+
+import android.content.res.ColorStateList
+import android.graphics.Color
+import android.view.View
+import android.view.ViewGroup
+import android.widget.FrameLayout
+import android.widget.ImageView
+import android.widget.Space
+import androidx.core.view.isVisible
+import com.android.settingslib.graph.SignalDrawable
+import com.android.systemui.Flags
+import com.android.systemui.common.ui.binder.IconViewBinder
+import com.android.systemui.kairos.BuildScope
+import com.android.systemui.kairos.BuildSpec
+import com.android.systemui.kairos.ExperimentalKairosApi
+import com.android.systemui.kairos.KairosNetwork
+import com.android.systemui.kairos.MutableState
+import com.android.systemui.kairos.effect
+import com.android.systemui.lifecycle.repeatWhenAttachedToWindow
+import com.android.systemui.lifecycle.repeatWhenWindowIsVisible
+import com.android.systemui.plugins.DarkIconDispatcher
+import com.android.systemui.res.R
+import com.android.systemui.statusbar.StatusBarIconView
+import com.android.systemui.statusbar.pipeline.mobile.domain.model.SignalIconModel
+import com.android.systemui.statusbar.pipeline.mobile.ui.MobileViewLogger
+import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.LocationBasedMobileViewModelKairos
+import com.android.systemui.statusbar.pipeline.shared.ui.binder.ModernStatusBarViewBinding
+import com.android.systemui.statusbar.pipeline.shared.ui.binder.ModernStatusBarViewVisibilityHelper
+import com.android.systemui.statusbar.pipeline.shared.ui.binder.StatusBarViewBinderConstants
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.awaitCancellation
+import kotlinx.coroutines.launch
+
+object MobileIconBinderKairos {
+
+ @ExperimentalKairosApi
+ fun bind(
+ view: ViewGroup,
+ viewModel: BuildSpec<LocationBasedMobileViewModelKairos>,
+ @StatusBarIconView.VisibleState
+ initialVisibilityState: Int = StatusBarIconView.STATE_HIDDEN,
+ logger: MobileViewLogger,
+ scope: CoroutineScope,
+ kairosNetwork: KairosNetwork,
+ ): Pair<ModernStatusBarViewBinding, Job> {
+ val binding = ModernStatusBarViewBindingKairosImpl(kairosNetwork, initialVisibilityState)
+ return binding to
+ scope.launch {
+ view.repeatWhenAttachedToWindow {
+ kairosNetwork.activateSpec {
+ bind(
+ view = view,
+ viewModel = viewModel.applySpec(),
+ logger = logger,
+ binding = binding,
+ )
+ }
+ }
+ }
+ }
+
+ @ExperimentalKairosApi
+ private class ModernStatusBarViewBindingKairosImpl(
+ kairosNetwork: KairosNetwork,
+ initialVisibilityState: Int,
+ ) : ModernStatusBarViewBinding {
+
+ @JvmField var shouldIconBeVisible: Boolean = false
+ @JvmField var isCollecting: Boolean = false
+
+ // TODO(b/238425913): We should log this visibility state.
+ val visibility = MutableState(kairosNetwork, initialVisibilityState)
+ val iconTint =
+ MutableState(
+ kairosNetwork,
+ MobileIconColors(
+ tint = DarkIconDispatcher.DEFAULT_ICON_TINT,
+ contrast = DarkIconDispatcher.DEFAULT_INVERSE_ICON_TINT,
+ ),
+ )
+ val decorTint = MutableState(kairosNetwork, Color.WHITE)
+
+ override fun getShouldIconBeVisible(): Boolean = shouldIconBeVisible
+
+ override fun onVisibilityStateChanged(state: Int) {
+ visibility.setValue(state)
+ }
+
+ override fun onIconTintChanged(newTint: Int, contrastTint: Int) {
+ iconTint.setValue(MobileIconColors(tint = newTint, contrast = contrastTint))
+ }
+
+ override fun onDecorTintChanged(newTint: Int) {
+ decorTint.setValue(newTint)
+ }
+
+ override fun isCollecting(): Boolean = isCollecting
+ }
+
+ @ExperimentalKairosApi
+ private fun BuildScope.bind(
+ view: ViewGroup,
+ viewModel: LocationBasedMobileViewModelKairos,
+ logger: MobileViewLogger,
+ binding: ModernStatusBarViewBindingKairosImpl,
+ ) {
+ viewModel.isVisible.observe { binding.shouldIconBeVisible = it }
+
+ val mobileGroupView = view.requireViewById<ViewGroup>(R.id.mobile_group)
+ val activityContainer = view.requireViewById<View>(R.id.inout_container)
+ val activityIn = view.requireViewById<ImageView>(R.id.mobile_in)
+ val activityOut = view.requireViewById<ImageView>(R.id.mobile_out)
+ val networkTypeView = view.requireViewById<ImageView>(R.id.mobile_type)
+ val networkTypeContainer = view.requireViewById<FrameLayout>(R.id.mobile_type_container)
+ val iconView = view.requireViewById<ImageView>(R.id.mobile_signal)
+ val mobileDrawable = SignalDrawable(view.context)
+ val roamingView = view.requireViewById<ImageView>(R.id.mobile_roaming)
+ val roamingSpace = view.requireViewById<Space>(R.id.mobile_roaming_space)
+ val dotView = view.requireViewById<StatusBarIconView>(R.id.status_bar_dot)
+
+ effect {
+ view.isVisible = viewModel.isVisible.sample()
+ iconView.isVisible = true
+ launch {
+ view.repeatWhenAttachedToWindow {
+ // isVisible controls the visibility state of the outer group, and thus it needs
+ // to run in the CREATED lifecycle so it can continue to watch while invisible
+ // See (b/291031862) for details
+ kairosNetwork.activateSpec {
+ viewModel.isVisible.observe { isVisible ->
+ viewModel.verboseLogger?.logBinderReceivedVisibility(
+ view,
+ viewModel.subscriptionId,
+ isVisible,
+ )
+ view.isVisible = isVisible
+ // [StatusIconContainer] can get out of sync sometimes. Make sure to
+ // request another layout when this changes.
+ view.requestLayout()
+ }
+ }
+ }
+ }
+ launch {
+ view.repeatWhenWindowIsVisible {
+ logger.logCollectionStarted(view, viewModel)
+ binding.isCollecting = true
+ kairosNetwork.activateSpec {
+ binding.visibility.observe { state ->
+ ModernStatusBarViewVisibilityHelper.setVisibilityState(
+ state,
+ mobileGroupView,
+ dotView,
+ )
+ view.requestLayout()
+ }
+
+ // Set the icon for the triangle
+ viewModel.icon.observe { icon ->
+ viewModel.verboseLogger?.logBinderReceivedSignalIcon(
+ view,
+ viewModel.subscriptionId,
+ icon,
+ )
+ if (icon is SignalIconModel.Cellular) {
+ iconView.setImageDrawable(mobileDrawable)
+ mobileDrawable.level = icon.toSignalDrawableState()
+ } else if (icon is SignalIconModel.Satellite) {
+ IconViewBinder.bind(icon.icon, iconView)
+ }
+ }
+
+ viewModel.contentDescription.observe {
+ MobileContentDescriptionViewBinder.bind(it, view)
+ }
+
+ // Set the network type icon
+ viewModel.networkTypeIcon.observe { dataTypeId ->
+ viewModel.verboseLogger?.logBinderReceivedNetworkTypeIcon(
+ view,
+ viewModel.subscriptionId,
+ dataTypeId,
+ )
+ dataTypeId?.let { IconViewBinder.bind(dataTypeId, networkTypeView) }
+ val prevVis = networkTypeContainer.visibility
+ networkTypeContainer.visibility =
+ if (dataTypeId != null) View.VISIBLE else View.GONE
+
+ if (prevVis != networkTypeContainer.visibility) {
+ view.requestLayout()
+ }
+ }
+
+ // Set the network type background
+ viewModel.networkTypeBackground.observe { background ->
+ networkTypeContainer.setBackgroundResource(background?.res ?: 0)
+
+ // Tint will invert when this bit changes
+ if (background?.res != null) {
+ networkTypeContainer.backgroundTintList =
+ ColorStateList.valueOf(binding.iconTint.sample().tint)
+ networkTypeView.imageTintList =
+ ColorStateList.valueOf(binding.iconTint.sample().contrast)
+ } else {
+ networkTypeView.imageTintList =
+ ColorStateList.valueOf(binding.iconTint.sample().tint)
+ }
+ }
+
+ // Set the roaming indicator
+ viewModel.roaming.observe { isRoaming ->
+ roamingView.isVisible = isRoaming
+ roamingSpace.isVisible = isRoaming
+ }
+
+ if (Flags.statusBarStaticInoutIndicators()) {
+ // Set the opacity of the activity indicators
+ viewModel.activityInVisible.observe { visible ->
+ activityIn.imageAlpha =
+ (if (visible) StatusBarViewBinderConstants.ALPHA_ACTIVE
+ else StatusBarViewBinderConstants.ALPHA_INACTIVE)
+ }
+ viewModel.activityOutVisible.observe { visible ->
+ activityOut.imageAlpha =
+ (if (visible) StatusBarViewBinderConstants.ALPHA_ACTIVE
+ else StatusBarViewBinderConstants.ALPHA_INACTIVE)
+ }
+ } else {
+ // Set the activity indicators
+ viewModel.activityInVisible.observe { activityIn.isVisible = it }
+ viewModel.activityOutVisible.observe { activityOut.isVisible = it }
+ }
+
+ viewModel.activityContainerVisible.observe {
+ activityContainer.isVisible = it
+ }
+
+ // Set the tint
+ binding.iconTint.observe { colors ->
+ val tint = ColorStateList.valueOf(colors.tint)
+ val contrast = ColorStateList.valueOf(colors.contrast)
+
+ iconView.imageTintList = tint
+
+ // If the bg is visible, tint it and use the contrast for the fg
+ if (viewModel.networkTypeBackground.sample() != null) {
+ networkTypeContainer.backgroundTintList = tint
+ networkTypeView.imageTintList = contrast
+ } else {
+ networkTypeView.imageTintList = tint
+ }
+
+ roamingView.imageTintList = tint
+ activityIn.imageTintList = tint
+ activityOut.imageTintList = tint
+ dotView.setDecorColor(colors.tint)
+ }
+
+ binding.decorTint.observe { tint -> dotView.setDecorColor(tint) }
+ }
+
+ try {
+ awaitCancellation()
+ } finally {
+ binding.isCollecting = false
+ logger.logCollectionStopped(view, viewModel)
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/binder/ShadeCarrierBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/binder/ShadeCarrierBinder.kt
index 5c80fda72373..5238f3ec800a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/binder/ShadeCarrierBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/binder/ShadeCarrierBinder.kt
@@ -19,10 +19,10 @@ package com.android.systemui.statusbar.pipeline.mobile.ui.binder
import androidx.core.view.isVisible
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.repeatOnLifecycle
+import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.ShadeCarrierGroupMobileIconViewModel
import com.android.systemui.util.AutoMarqueeTextView
-import com.android.app.tracing.coroutines.launchTraced as launch
object ShadeCarrierBinder {
/** Binds the view to the view-model, continuing to update the former based on the latter */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/binder/ShadeCarrierBinderKairos.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/binder/ShadeCarrierBinderKairos.kt
new file mode 100644
index 000000000000..a782116e669c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/binder/ShadeCarrierBinderKairos.kt
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2025 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.pipeline.mobile.ui.binder
+
+import androidx.core.view.isVisible
+import com.android.systemui.kairos.BuildSpec
+import com.android.systemui.kairos.ExperimentalKairosApi
+import com.android.systemui.kairos.KairosNetwork
+import com.android.systemui.lifecycle.repeatWhenWindowIsVisible
+import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.ShadeCarrierGroupMobileIconViewModelKairos
+import com.android.systemui.util.AutoMarqueeTextView
+
+object ShadeCarrierBinderKairos {
+ /** Binds the view to the view-model, continuing to update the former based on the latter */
+ @ExperimentalKairosApi
+ suspend fun bind(
+ carrierTextView: AutoMarqueeTextView,
+ viewModel: BuildSpec<ShadeCarrierGroupMobileIconViewModelKairos>,
+ kairosNetwork: KairosNetwork,
+ ) {
+ carrierTextView.isVisible = true
+ carrierTextView.repeatWhenWindowIsVisible {
+ kairosNetwork.activateSpec {
+ viewModel.applySpec().carrierName.observe { carrierTextView.text = it }
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/binder/StackedMobileIconBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/binder/StackedMobileIconBinder.kt
index c9fc53ecadc0..54cd8e3c46e4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/binder/StackedMobileIconBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/binder/StackedMobileIconBinder.kt
@@ -22,19 +22,28 @@ import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.ViewCompositionStrategy
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.repeatOnLifecycle
+import com.android.systemui.Flags
+import com.android.systemui.kairos.ExperimentalKairosApi
+import com.android.systemui.kairos.KairosNetwork
import com.android.systemui.lifecycle.rememberViewModel
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.MobileIconsViewModel
import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.StackedMobileIconViewModel
+import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.StackedMobileIconViewModelImpl
+import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.StackedMobileIconViewModelKairos
import com.android.systemui.statusbar.pipeline.shared.ui.binder.ModernStatusBarViewBinding
import com.android.systemui.statusbar.pipeline.shared.ui.composable.StackedMobileIcon
import com.android.systemui.statusbar.pipeline.shared.ui.view.SingleBindableStatusBarComposeIconView
+import com.android.systemui.util.composable.kairos.rememberKairosActivatable
object StackedMobileIconBinder {
+ @OptIn(ExperimentalKairosApi::class)
fun bind(
view: SingleBindableStatusBarComposeIconView,
mobileIconsViewModel: MobileIconsViewModel,
- viewModelFactory: StackedMobileIconViewModel.Factory,
+ viewModelFactory: StackedMobileIconViewModelImpl.Factory,
+ kairosViewModelFactory: StackedMobileIconViewModelKairos.Factory,
+ kairosNetwork: KairosNetwork,
): ModernStatusBarViewBinding {
return SingleBindableStatusBarComposeIconView.withDefaultBinding(
view = view,
@@ -47,9 +56,15 @@ object StackedMobileIconBinder {
ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed
)
setContent {
- val viewModel =
- rememberViewModel("StackedMobileIconBinder") {
- viewModelFactory.create()
+ val viewModel: StackedMobileIconViewModel =
+ if (Flags.statusBarMobileIconKairos()) {
+ rememberKairosActivatable(kairosNetwork) {
+ kairosViewModelFactory.create()
+ }
+ } else {
+ rememberViewModel("StackedMobileIconBinder") {
+ viewModelFactory.create()
+ }
}
if (viewModel.isIconVisible) {
CompositionLocalProvider(LocalContentColor provides Color(tint())) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/view/ModernShadeCarrierGroupMobileView.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/view/ModernShadeCarrierGroupMobileView.kt
index fbd074d5b003..f1c5fee808cf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/view/ModernShadeCarrierGroupMobileView.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/view/ModernShadeCarrierGroupMobileView.kt
@@ -20,22 +20,30 @@ import android.content.Context
import android.util.AttributeSet
import android.view.LayoutInflater
import android.widget.LinearLayout
+import com.android.systemui.kairos.BuildSpec
+import com.android.systemui.kairos.ExperimentalKairosApi
+import com.android.systemui.kairos.KairosNetwork
import com.android.systemui.res.R
import com.android.systemui.statusbar.StatusBarIconView.STATE_ICON
+import com.android.systemui.statusbar.phone.StatusBarLocation
import com.android.systemui.statusbar.pipeline.mobile.ui.MobileViewLogger
import com.android.systemui.statusbar.pipeline.mobile.ui.binder.MobileIconBinder
+import com.android.systemui.statusbar.pipeline.mobile.ui.binder.MobileIconBinderKairos
import com.android.systemui.statusbar.pipeline.mobile.ui.binder.ShadeCarrierBinder
+import com.android.systemui.statusbar.pipeline.mobile.ui.binder.ShadeCarrierBinderKairos
import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.ShadeCarrierGroupMobileIconViewModel
+import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.ShadeCarrierGroupMobileIconViewModelKairos
import com.android.systemui.util.AutoMarqueeTextView
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.launch
/**
* ViewGroup containing a mobile carrier name and icon in the Shade Header. Can be multiple
* instances as children under [ShadeCarrierGroup]
*/
-class ModernShadeCarrierGroupMobileView(
- context: Context,
- attrs: AttributeSet?,
-) : LinearLayout(context, attrs) {
+class ModernShadeCarrierGroupMobileView(context: Context, attrs: AttributeSet?) :
+ LinearLayout(context, attrs) {
var subId: Int = -1
@@ -73,5 +81,49 @@ class ModernShadeCarrierGroupMobileView(
ShadeCarrierBinder.bind(textView, viewModel)
}
}
+
+ /**
+ * Inflates a new instance of [ModernShadeCarrierGroupMobileView], binds it to [viewModel],
+ * and returns it.
+ */
+ @ExperimentalKairosApi
+ @JvmStatic
+ fun constructAndBind(
+ context: Context,
+ logger: MobileViewLogger,
+ slot: String,
+ viewModel: BuildSpec<ShadeCarrierGroupMobileIconViewModelKairos>,
+ scope: CoroutineScope,
+ subscriptionId: Int,
+ location: StatusBarLocation,
+ kairosNetwork: KairosNetwork,
+ ): Pair<ModernShadeCarrierGroupMobileView, Job> {
+ val view =
+ (LayoutInflater.from(context).inflate(R.layout.shade_carrier_new, null)
+ as ModernShadeCarrierGroupMobileView)
+ .apply { subId = subscriptionId }
+ return view to
+ scope.launch {
+ val iconView =
+ view.requireViewById<ModernStatusBarMobileView>(R.id.mobile_combo)
+ iconView.initView(slot) {
+ val (binding, _) =
+ MobileIconBinderKairos.bind(
+ view = iconView,
+ viewModel = viewModel,
+ initialVisibilityState = STATE_ICON,
+ logger = logger,
+ scope = this,
+ kairosNetwork = kairosNetwork,
+ )
+ binding
+ }
+ logger.logNewViewBinding(view, viewModel, location.name)
+
+ val textView =
+ view.requireViewById<AutoMarqueeTextView>(R.id.mobile_carrier_text)
+ launch { ShadeCarrierBinderKairos.bind(textView, viewModel, kairosNetwork) }
+ }
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/view/ModernStatusBarMobileView.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/view/ModernStatusBarMobileView.kt
index 7eda87f8418d..382af7e135ef 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/view/ModernStatusBarMobileView.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/view/ModernStatusBarMobileView.kt
@@ -21,13 +21,22 @@ import android.util.AttributeSet
import android.view.LayoutInflater
import android.widget.FrameLayout
import android.widget.ImageView
+import com.android.settingslib.flags.Flags.newStatusBarIcons
+import com.android.systemui.kairos.BuildSpec
+import com.android.systemui.kairos.ExperimentalKairosApi
+import com.android.systemui.kairos.KairosNetwork
import com.android.systemui.res.R
import com.android.systemui.statusbar.StatusBarIconView.getVisibleStateString
import com.android.systemui.statusbar.core.NewStatusBarIcons
+import com.android.systemui.statusbar.phone.StatusBarLocation
import com.android.systemui.statusbar.pipeline.mobile.ui.MobileViewLogger
import com.android.systemui.statusbar.pipeline.mobile.ui.binder.MobileIconBinder
+import com.android.systemui.statusbar.pipeline.mobile.ui.binder.MobileIconBinderKairos
import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.LocationBasedMobileViewModel
+import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.LocationBasedMobileViewModelKairos
import com.android.systemui.statusbar.pipeline.shared.ui.view.ModernStatusBarView
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Job
class ModernStatusBarMobileView(context: Context, attrs: AttributeSet?) :
ModernStatusBarView(context, attrs) {
@@ -98,5 +107,58 @@ class ModernStatusBarMobileView(context: Context, attrs: AttributeSet?) :
logger.logNewViewBinding(it, viewModel)
}
}
+
+ /**
+ * Inflates a new instance of [ModernStatusBarMobileView], binds it to [viewModel], and
+ * returns it.
+ */
+ @ExperimentalKairosApi
+ @JvmStatic
+ fun constructAndBind(
+ context: Context,
+ logger: MobileViewLogger,
+ slot: String,
+ viewModel: BuildSpec<LocationBasedMobileViewModelKairos>,
+ scope: CoroutineScope,
+ subscriptionId: Int,
+ location: StatusBarLocation,
+ kairosNetwork: KairosNetwork,
+ ): Pair<ModernStatusBarMobileView, Job> {
+ val view =
+ (LayoutInflater.from(context)
+ .inflate(R.layout.status_bar_mobile_signal_group_new, null)
+ as ModernStatusBarMobileView)
+ .apply {
+ // Flag-specific configuration
+ if (newStatusBarIcons()) {
+ // New icon (with no embedded whitespace) is slightly shorter
+ // (but actually taller)
+ val iconView = requireViewById<ImageView>(R.id.mobile_signal)
+ val lp = iconView.layoutParams
+ lp.height =
+ context.resources.getDimensionPixelSize(
+ R.dimen.status_bar_mobile_signal_size_updated
+ )
+ }
+
+ subId = subscriptionId
+ }
+
+ lateinit var jobResult: Job
+ view.initView(slot) {
+ val (binding, job) =
+ MobileIconBinderKairos.bind(
+ view = view,
+ viewModel = viewModel,
+ logger = logger,
+ scope = scope,
+ kairosNetwork = kairosNetwork,
+ )
+ jobResult = job
+ binding
+ }
+ logger.logNewViewBinding(view, viewModel, location.name)
+ return view to jobResult
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/LocationBasedMobileViewModelKairos.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/LocationBasedMobileViewModelKairos.kt
index fce8c85338f3..d2e3a1489040 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/LocationBasedMobileViewModelKairos.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/LocationBasedMobileViewModelKairos.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2022 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,14 +17,12 @@
package com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel
import android.graphics.Color
+import com.android.systemui.kairos.ExperimentalKairosApi
+import com.android.systemui.kairos.State
+import com.android.systemui.kairos.combine
import com.android.systemui.statusbar.phone.StatusBarLocation
-import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconInteractor
+import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconInteractorKairos
import com.android.systemui.statusbar.pipeline.mobile.ui.VerboseMobileViewLogger
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.flow.SharingStarted
-import kotlinx.coroutines.flow.StateFlow
-import kotlinx.coroutines.flow.combine
-import kotlinx.coroutines.flow.stateIn
/**
* A view model for an individual mobile icon that embeds the notion of a [StatusBarLocation]. This
@@ -35,45 +33,47 @@ import kotlinx.coroutines.flow.stateIn
* @property location the [StatusBarLocation] of this VM.
* @property verboseLogger an optional logger to log extremely verbose view updates.
*/
+@ExperimentalKairosApi
abstract class LocationBasedMobileViewModelKairos(
- val commonImpl: MobileIconViewModelCommonKairos,
+ val commonImpl: MobileIconViewModelKairosCommon,
val location: StatusBarLocation,
val verboseLogger: VerboseMobileViewLogger?,
-) : MobileIconViewModelCommonKairos by commonImpl {
+) : MobileIconViewModelKairosCommon by commonImpl {
val defaultColor: Int = Color.WHITE
companion object {
fun viewModelForLocation(
- commonImpl: MobileIconViewModelCommon,
- interactor: MobileIconInteractor,
+ commonImpl: MobileIconViewModelKairosCommon,
+ interactor: MobileIconInteractorKairos,
verboseMobileViewLogger: VerboseMobileViewLogger,
location: StatusBarLocation,
- scope: CoroutineScope,
- ): LocationBasedMobileViewModel =
+ ): LocationBasedMobileViewModelKairos =
when (location) {
StatusBarLocation.HOME ->
- HomeMobileIconViewModel(commonImpl, verboseMobileViewLogger)
- StatusBarLocation.KEYGUARD -> KeyguardMobileIconViewModel(commonImpl)
- StatusBarLocation.QS -> QsMobileIconViewModel(commonImpl)
+ HomeMobileIconViewModelKairos(commonImpl, verboseMobileViewLogger)
+ StatusBarLocation.KEYGUARD -> KeyguardMobileIconViewModelKairos(commonImpl)
+ StatusBarLocation.QS -> QsMobileIconViewModelKairos(commonImpl)
StatusBarLocation.SHADE_CARRIER_GROUP ->
- ShadeCarrierGroupMobileIconViewModel(commonImpl, interactor, scope)
+ ShadeCarrierGroupMobileIconViewModelKairos(commonImpl, interactor)
}
}
}
+@ExperimentalKairosApi
class HomeMobileIconViewModelKairos(
- commonImpl: MobileIconViewModelCommonKairos,
+ commonImpl: MobileIconViewModelKairosCommon,
verboseMobileViewLogger: VerboseMobileViewLogger,
) :
- MobileIconViewModelCommonKairos,
+ MobileIconViewModelKairosCommon,
LocationBasedMobileViewModelKairos(
commonImpl,
location = StatusBarLocation.HOME,
verboseMobileViewLogger,
)
-class QsMobileIconViewModelKairos(commonImpl: MobileIconViewModelCommonKairos) :
- MobileIconViewModelCommonKairos,
+@ExperimentalKairosApi
+class QsMobileIconViewModelKairos(commonImpl: MobileIconViewModelKairosCommon) :
+ MobileIconViewModelKairosCommon,
LocationBasedMobileViewModelKairos(
commonImpl,
location = StatusBarLocation.QS,
@@ -81,30 +81,34 @@ class QsMobileIconViewModelKairos(commonImpl: MobileIconViewModelCommonKairos) :
verboseLogger = null,
)
+@ExperimentalKairosApi
class ShadeCarrierGroupMobileIconViewModelKairos(
- commonImpl: MobileIconViewModelCommonKairos,
- interactor: MobileIconInteractor,
- scope: CoroutineScope,
+ commonImpl: MobileIconViewModelKairosCommon,
+ private val interactor: MobileIconInteractorKairos,
) :
- MobileIconViewModelCommonKairos,
+ MobileIconViewModelKairosCommon,
LocationBasedMobileViewModelKairos(
commonImpl,
location = StatusBarLocation.SHADE_CARRIER_GROUP,
// Only do verbose logging for the Home location.
verboseLogger = null,
) {
- private val isSingleCarrier = interactor.isSingleCarrier
- val carrierName = interactor.carrierName
- override val isVisible: StateFlow<Boolean> =
+ private val isSingleCarrier: State<Boolean>
+ get() = interactor.isSingleCarrier
+
+ val carrierName: State<String>
+ get() = interactor.carrierName
+
+ override val isVisible: State<Boolean> =
combine(super.isVisible, isSingleCarrier) { isVisible, isSingleCarrier ->
- if (isSingleCarrier) false else isVisible
- }
- .stateIn(scope, SharingStarted.WhileSubscribed(), super.isVisible.value)
+ !isSingleCarrier && isVisible
+ }
}
-class KeyguardMobileIconViewModelKairos(commonImpl: MobileIconViewModelCommonKairos) :
- MobileIconViewModelCommonKairos,
+@ExperimentalKairosApi
+class KeyguardMobileIconViewModelKairos(commonImpl: MobileIconViewModelKairosCommon) :
+ MobileIconViewModelKairosCommon,
LocationBasedMobileViewModelKairos(
commonImpl,
location = StatusBarLocation.KEYGUARD,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModelKairos.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModelKairos.kt
index cc7fc0964dae..bd42d5bf401f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModelKairos.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModelKairos.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2022 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,206 +17,187 @@
package com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel
import com.android.systemui.Flags.statusBarStaticInoutIndicators
+import com.android.systemui.KairosBuilder
+import com.android.systemui.activated
import com.android.systemui.common.shared.model.ContentDescription
import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.flags.FeatureFlagsClassic
+import com.android.systemui.kairos.ExperimentalKairosApi
+import com.android.systemui.kairos.State as KairosState
+import com.android.systemui.kairos.State
+import com.android.systemui.kairos.combine
+import com.android.systemui.kairos.flatMap
+import com.android.systemui.kairos.map
+import com.android.systemui.kairos.stateOf
+import com.android.systemui.kairosBuilder
import com.android.systemui.log.table.logDiffsForTable
import com.android.systemui.res.R
import com.android.systemui.statusbar.core.NewStatusBarIcons
import com.android.systemui.statusbar.pipeline.airplane.domain.interactor.AirplaneModeInteractor
-import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconInteractor
-import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconsInteractor
+import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconInteractorKairos
import com.android.systemui.statusbar.pipeline.mobile.domain.model.SignalIconModel
import com.android.systemui.statusbar.pipeline.mobile.ui.model.MobileContentDescription
import com.android.systemui.statusbar.pipeline.shared.ConnectivityConstants
import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityModel
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.SharingStarted
-import kotlinx.coroutines.flow.StateFlow
-import kotlinx.coroutines.flow.combine
-import kotlinx.coroutines.flow.distinctUntilChanged
-import kotlinx.coroutines.flow.flatMapLatest
-import kotlinx.coroutines.flow.flowOf
-import kotlinx.coroutines.flow.map
-import kotlinx.coroutines.flow.mapLatest
-import kotlinx.coroutines.flow.stateIn
/** Common interface for all of the location-based mobile icon view models. */
-interface MobileIconViewModelCommonKairos : MobileIconViewModelCommon {
- override val subscriptionId: Int
+@ExperimentalKairosApi
+interface MobileIconViewModelKairosCommon {
+ val subscriptionId: Int
+ val iconInteractor: MobileIconInteractorKairos
/** True if this view should be visible at all. */
- override val isVisible: StateFlow<Boolean>
- override val icon: Flow<SignalIconModel>
- override val contentDescription: Flow<MobileContentDescription?>
- override val roaming: Flow<Boolean>
+ val isVisible: KairosState<Boolean>
+ val icon: KairosState<SignalIconModel>
+ val contentDescription: KairosState<MobileContentDescription?>
+ val roaming: KairosState<Boolean>
/** The RAT icon (LTE, 3G, 5G, etc) to be displayed. Null if we shouldn't show anything */
- override val networkTypeIcon: Flow<Icon.Resource?>
+ val networkTypeIcon: KairosState<Icon.Resource?>
/** The slice attribution. Drawn as a background layer */
- override val networkTypeBackground: StateFlow<Icon.Resource?>
- override val activityInVisible: Flow<Boolean>
- override val activityOutVisible: Flow<Boolean>
- override val activityContainerVisible: Flow<Boolean>
+ val networkTypeBackground: KairosState<Icon.Resource?>
+ val activityInVisible: KairosState<Boolean>
+ val activityOutVisible: KairosState<Boolean>
+ val activityContainerVisible: KairosState<Boolean>
}
/**
- * View model for the state of a single mobile icon. Each [MobileIconViewModel] will keep watch over
- * a single line of service via [MobileIconInteractor] and update the UI based on that
- * subscription's information.
+ * View model for the state of a single mobile icon. Each [MobileIconViewModelKairos] will keep
+ * watch over a single line of service via [MobileIconInteractorKairos] and update the UI based on
+ * that subscription's information.
*
- * There will be exactly one [MobileIconViewModel] per filtered subscription offered from
- * [MobileIconsInteractor.filteredSubscriptions].
- *
- * For the sake of keeping log spam in check, every flow funding the [MobileIconViewModelCommon]
- * interface is implemented as a [StateFlow]. This ensures that each location-based mobile icon view
- * model gets the exact same information, as well as allows us to log that unified state only once
- * per icon.
+ * There will be exactly one [MobileIconViewModelKairos] per filtered subscription offered from
+ * [MobileIconsInteractorKairos.filteredSubscriptions].
*/
+@ExperimentalKairosApi
class MobileIconViewModelKairos(
override val subscriptionId: Int,
- iconInteractor: MobileIconInteractor,
- airplaneModeInteractor: AirplaneModeInteractor,
- constants: ConnectivityConstants,
- scope: CoroutineScope,
-) : MobileIconViewModelCommonKairos {
- private val cellProvider by lazy {
- CellularIconViewModelKairos(
- subscriptionId,
- iconInteractor,
- airplaneModeInteractor,
- constants,
- scope,
- )
+ override val iconInteractor: MobileIconInteractorKairos,
+ private val airplaneModeInteractor: AirplaneModeInteractor,
+ private val constants: ConnectivityConstants,
+ private val flags: FeatureFlagsClassic,
+) : MobileIconViewModelKairosCommon, KairosBuilder by kairosBuilder() {
+
+ private val isAirplaneMode: State<Boolean> = buildState {
+ airplaneModeInteractor.isAirplaneMode.toState()
}
private val satelliteProvider by lazy {
- CarrierBasedSatelliteViewModelKairosImpl(
- subscriptionId,
- airplaneModeInteractor,
- iconInteractor,
- scope,
- )
+ CarrierBasedSatelliteViewModelKairosImpl(subscriptionId, iconInteractor, isAirplaneMode)
}
/**
* Similar to repository switching, this allows us to split up the logic of satellite/cellular
* states, since they are different by nature
*/
- private val vmProvider: Flow<MobileIconViewModelCommon> =
- iconInteractor.isNonTerrestrial
- .mapLatest { nonTerrestrial ->
- if (nonTerrestrial) {
- satelliteProvider
- } else {
- cellProvider
+ private val vmProvider: KairosState<MobileIconViewModelKairosCommon> = buildState {
+ iconInteractor.isNonTerrestrial.mapLatestBuild { nonTerrestrial ->
+ if (nonTerrestrial) {
+ satelliteProvider
+ } else {
+ activated {
+ CellularIconViewModelKairos(
+ subscriptionId,
+ iconInteractor,
+ airplaneModeInteractor,
+ constants,
+ flags,
+ )
}
}
- .stateIn(scope, SharingStarted.WhileSubscribed(), cellProvider)
+ }
+ }
- override val isVisible: StateFlow<Boolean> =
- vmProvider
- .flatMapLatest { it.isVisible }
- .stateIn(scope, SharingStarted.WhileSubscribed(), false)
+ override val isVisible: KairosState<Boolean> = vmProvider.flatMap { it.isVisible }
- override val icon: Flow<SignalIconModel> = vmProvider.flatMapLatest { it.icon }
+ override val icon: KairosState<SignalIconModel> = vmProvider.flatMap { it.icon }
- override val contentDescription: Flow<MobileContentDescription?> =
- vmProvider.flatMapLatest { it.contentDescription }
+ override val contentDescription: KairosState<MobileContentDescription?> =
+ vmProvider.flatMap { it.contentDescription }
- override val roaming: Flow<Boolean> = vmProvider.flatMapLatest { it.roaming }
+ override val roaming: KairosState<Boolean> = vmProvider.flatMap { it.roaming }
- override val networkTypeIcon: Flow<Icon.Resource?> =
- vmProvider.flatMapLatest { it.networkTypeIcon }
+ override val networkTypeIcon: KairosState<Icon.Resource?> =
+ vmProvider.flatMap { it.networkTypeIcon }
- override val networkTypeBackground: StateFlow<Icon.Resource?> =
- vmProvider
- .flatMapLatest { it.networkTypeBackground }
- .stateIn(scope, SharingStarted.WhileSubscribed(), null)
+ override val networkTypeBackground: KairosState<Icon.Resource?> =
+ vmProvider.flatMap { it.networkTypeBackground }
- override val activityInVisible: Flow<Boolean> =
- vmProvider.flatMapLatest { it.activityInVisible }
+ override val activityInVisible: KairosState<Boolean> =
+ vmProvider.flatMap { it.activityInVisible }
- override val activityOutVisible: Flow<Boolean> =
- vmProvider.flatMapLatest { it.activityOutVisible }
+ override val activityOutVisible: KairosState<Boolean> =
+ vmProvider.flatMap { it.activityOutVisible }
- override val activityContainerVisible: Flow<Boolean> =
- vmProvider.flatMapLatest { it.activityContainerVisible }
+ override val activityContainerVisible: KairosState<Boolean> =
+ vmProvider.flatMap { it.activityContainerVisible }
}
/** Representation of this network when it is non-terrestrial (e.g., satellite) */
+@ExperimentalKairosApi
private class CarrierBasedSatelliteViewModelKairosImpl(
override val subscriptionId: Int,
- airplaneModeInteractor: AirplaneModeInteractor,
- interactor: MobileIconInteractor,
- scope: CoroutineScope,
-) : MobileIconViewModelCommon, MobileIconViewModelCommonKairos {
- override val isVisible: StateFlow<Boolean> =
- airplaneModeInteractor.isAirplaneMode
- .map { !it }
- .stateIn(scope, SharingStarted.WhileSubscribed(), false)
+ override val iconInteractor: MobileIconInteractorKairos,
+ isAirplaneMode: KairosState<Boolean>,
+) : MobileIconViewModelKairosCommon {
+ override val isVisible: KairosState<Boolean> = isAirplaneMode.map { !it }
+ override val icon: KairosState<SignalIconModel>
+ get() = iconInteractor.signalLevelIcon
- override val icon: Flow<SignalIconModel> = interactor.signalLevelIcon
-
- override val contentDescription: Flow<MobileContentDescription?> = MutableStateFlow(null)
+ override val contentDescription: KairosState<MobileContentDescription?> = stateOf(null)
/** These fields are not used for satellite icons currently */
- override val roaming: Flow<Boolean> = flowOf(false)
- override val networkTypeIcon: Flow<Icon.Resource?> = flowOf(null)
- override val networkTypeBackground: StateFlow<Icon.Resource?> = MutableStateFlow(null)
- override val activityInVisible: Flow<Boolean> = flowOf(false)
- override val activityOutVisible: Flow<Boolean> = flowOf(false)
- override val activityContainerVisible: Flow<Boolean> = flowOf(false)
+ override val roaming: KairosState<Boolean> = stateOf(false)
+ override val networkTypeIcon: KairosState<Icon.Resource?> = stateOf(null)
+ override val networkTypeBackground: KairosState<Icon.Resource?> = stateOf(null)
+ override val activityInVisible: KairosState<Boolean> = stateOf(false)
+ override val activityOutVisible: KairosState<Boolean> = stateOf(false)
+ override val activityContainerVisible: KairosState<Boolean> = stateOf(false)
}
/** Terrestrial (cellular) icon. */
-@Suppress("EXPERIMENTAL_IS_NOT_ENABLED")
+@ExperimentalKairosApi
private class CellularIconViewModelKairos(
override val subscriptionId: Int,
- iconInteractor: MobileIconInteractor,
+ override val iconInteractor: MobileIconInteractorKairos,
airplaneModeInteractor: AirplaneModeInteractor,
constants: ConnectivityConstants,
- scope: CoroutineScope,
-) : MobileIconViewModelCommon, MobileIconViewModelCommonKairos {
- override val isVisible: StateFlow<Boolean> =
+ flags: FeatureFlagsClassic,
+) : MobileIconViewModelKairosCommon, KairosBuilder by kairosBuilder() {
+
+ override val isVisible: KairosState<Boolean> =
if (!constants.hasDataCapabilities) {
- flowOf(false)
- } else {
+ stateOf(false)
+ } else {
+ buildState {
combine(
- airplaneModeInteractor.isAirplaneMode,
- iconInteractor.isAllowedDuringAirplaneMode,
- iconInteractor.isForceHidden,
- ) { isAirplaneMode, isAllowedDuringAirplaneMode, isForceHidden ->
- if (isForceHidden) {
- false
- } else if (isAirplaneMode) {
- isAllowedDuringAirplaneMode
- } else {
- true
+ airplaneModeInteractor.isAirplaneMode.toState(),
+ iconInteractor.isAllowedDuringAirplaneMode,
+ iconInteractor.isForceHidden,
+ ) { isAirplaneMode, isAllowedDuringAirplaneMode, isForceHidden ->
+ if (isForceHidden) {
+ false
+ } else if (isAirplaneMode) {
+ isAllowedDuringAirplaneMode
+ } else {
+ true
+ }
+ }
+ .also {
+ logDiffsForTable(it, iconInteractor.tableLogBuffer, columnName = "visible")
}
- }
}
- .distinctUntilChanged()
- .logDiffsForTable(
- iconInteractor.tableLogBuffer,
- columnName = "visible",
- initialValue = false,
- )
- .stateIn(scope, SharingStarted.WhileSubscribed(), false)
+ }
- override val icon: Flow<SignalIconModel> = iconInteractor.signalLevelIcon
+ override val icon: KairosState<SignalIconModel>
+ get() = iconInteractor.signalLevelIcon
- override val contentDescription: Flow<MobileContentDescription?> =
+ override val contentDescription: KairosState<MobileContentDescription?> =
combine(iconInteractor.signalLevelIcon, iconInteractor.networkName) { icon, nameModel ->
- when (icon) {
- is SignalIconModel.Cellular ->
- MobileContentDescription.Cellular(
- nameModel.name,
- icon.levelDescriptionRes(),
- )
- else -> null
- }
+ when (icon) {
+ is SignalIconModel.Cellular ->
+ MobileContentDescription.Cellular(nameModel.name, icon.levelDescriptionRes())
+ else -> null
}
- .stateIn(scope, SharingStarted.WhileSubscribed(), null)
+ }
private fun SignalIconModel.Cellular.levelDescriptionRes() =
when (level) {
@@ -241,7 +222,7 @@ private class CellularIconViewModelKairos(
else -> R.string.accessibility_no_signal
}
- private val showNetworkTypeIcon: Flow<Boolean> =
+ private val showNetworkTypeIcon: KairosState<Boolean> =
combine(
iconInteractor.isDataConnected,
iconInteractor.isDataEnabled,
@@ -252,77 +233,72 @@ private class CellularIconViewModelKairos(
alwaysShow ||
(!carrierNetworkChange && (dataEnabled && dataConnected && mobileIsDefault))
}
- .distinctUntilChanged()
- .logDiffsForTable(
- iconInteractor.tableLogBuffer,
- columnName = "showNetworkTypeIcon",
- initialValue = false,
- )
- .stateIn(scope, SharingStarted.WhileSubscribed(), false)
-
- override val networkTypeIcon: Flow<Icon.Resource?> =
- combine(iconInteractor.networkTypeIconGroup, showNetworkTypeIcon) {
- networkTypeIconGroup,
- shouldShow ->
- val desc =
- if (networkTypeIconGroup.contentDescription != 0)
- ContentDescription.Resource(networkTypeIconGroup.contentDescription)
- else null
- val icon =
- if (networkTypeIconGroup.iconId != 0)
- Icon.Resource(networkTypeIconGroup.iconId, desc)
- else null
- return@combine when {
- !shouldShow -> null
- else -> icon
+ .also {
+ onActivated {
+ logDiffsForTable(
+ it,
+ iconInteractor.tableLogBuffer,
+ columnName = "showNetworkTypeIcon",
+ )
}
}
- .distinctUntilChanged()
- .stateIn(scope, SharingStarted.WhileSubscribed(), null)
-
- override val networkTypeBackground =
- iconInteractor.showSliceAttribution
- .map {
- when {
- it && NewStatusBarIcons.isEnabled ->
- Icon.Resource(R.drawable.mobile_network_type_background_updated, null)
- it -> Icon.Resource(R.drawable.mobile_network_type_background, null)
- else -> null
+
+ override val networkTypeIcon: KairosState<Icon.Resource?> =
+ combine(iconInteractor.networkTypeIconGroup, showNetworkTypeIcon) {
+ networkTypeIconGroup,
+ shouldShow ->
+ val desc =
+ if (networkTypeIconGroup.contentDescription != 0) {
+ ContentDescription.Resource(networkTypeIconGroup.contentDescription)
+ } else {
+ null
}
+ val icon =
+ if (networkTypeIconGroup.iconId != 0) {
+ Icon.Resource(networkTypeIconGroup.iconId, desc)
+ } else {
+ null
+ }
+ when {
+ !shouldShow -> null
+ else -> icon
+ }
+ }
+
+ override val networkTypeBackground: KairosState<Icon.Resource?> =
+ iconInteractor.showSliceAttribution.map {
+ when {
+ it && NewStatusBarIcons.isEnabled ->
+ Icon.Resource(R.drawable.mobile_network_type_background_updated, null)
+ it -> Icon.Resource(R.drawable.mobile_network_type_background, null)
+ else -> null
}
- .stateIn(scope, SharingStarted.WhileSubscribed(), null)
-
- override val roaming: StateFlow<Boolean> =
- iconInteractor.isRoaming
- .logDiffsForTable(
- iconInteractor.tableLogBuffer,
- columnName = "roaming",
- initialValue = false,
- )
- .stateIn(scope, SharingStarted.WhileSubscribed(), false)
-
- private val activity: Flow<DataActivityModel?> =
+ }
+
+ override val roaming: KairosState<Boolean> =
+ iconInteractor.isRoaming.also {
+ onActivated {
+ logDiffsForTable(it, iconInteractor.tableLogBuffer, columnName = "roaming")
+ }
+ }
+
+ private val activity: KairosState<DataActivityModel?> =
if (!constants.shouldShowActivityConfig) {
- flowOf(null)
+ stateOf(null)
} else {
iconInteractor.activity
}
- override val activityInVisible: Flow<Boolean> =
- activity
- .map { it?.hasActivityIn ?: false }
- .stateIn(scope, SharingStarted.WhileSubscribed(), false)
+ override val activityInVisible: KairosState<Boolean> =
+ activity.map { it?.hasActivityIn ?: false }
- override val activityOutVisible: Flow<Boolean> =
- activity
- .map { it?.hasActivityOut ?: false }
- .stateIn(scope, SharingStarted.WhileSubscribed(), false)
+ override val activityOutVisible: KairosState<Boolean> =
+ activity.map { it?.hasActivityOut ?: false }
- override val activityContainerVisible: Flow<Boolean> =
+ override val activityContainerVisible: KairosState<Boolean> =
if (statusBarStaticInoutIndicators()) {
- flowOf(constants.shouldShowActivityConfig)
- } else {
- activity.map { it != null && (it.hasActivityIn || it.hasActivityOut) }
- }
- .stateIn(scope, SharingStarted.WhileSubscribed(), false)
+ stateOf(constants.shouldShowActivityConfig)
+ } else {
+ activity.map { it != null && (it.hasActivityIn || it.hasActivityOut) }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModelKairos.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModelKairos.kt
index a65540738828..41c72bee9e52 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModelKairos.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModelKairos.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2022 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,137 +16,145 @@
package com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel
-import androidx.annotation.VisibleForTesting
-import com.android.app.tracing.coroutines.launchTraced as launch
-import com.android.systemui.coroutines.newTracingContext
+import androidx.compose.runtime.State as ComposeState
+import androidx.compose.runtime.getValue
+import com.android.systemui.Flags
+import com.android.systemui.KairosActivatable
+import com.android.systemui.KairosBuilder
+import com.android.systemui.activated
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.flags.FeatureFlagsClassic
+import com.android.systemui.kairos.BuildScope
+import com.android.systemui.kairos.BuildSpec
+import com.android.systemui.kairos.ExperimentalKairosApi
+import com.android.systemui.kairos.Incremental
+import com.android.systemui.kairos.State as KairosState
+import com.android.systemui.kairos.State
+import com.android.systemui.kairos.buildSpec
+import com.android.systemui.kairos.combine
+import com.android.systemui.kairos.flatten
+import com.android.systemui.kairos.map
+import com.android.systemui.kairos.mapValues
+import com.android.systemui.kairos.stateOf
+import com.android.systemui.kairosBuilder
import com.android.systemui.statusbar.phone.StatusBarLocation
import com.android.systemui.statusbar.pipeline.airplane.domain.interactor.AirplaneModeInteractor
-import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconsInteractor
+import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconInteractorKairos
+import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconsInteractorKairos
import com.android.systemui.statusbar.pipeline.mobile.ui.MobileViewLogger
import com.android.systemui.statusbar.pipeline.mobile.ui.VerboseMobileViewLogger
import com.android.systemui.statusbar.pipeline.mobile.ui.view.ModernStatusBarMobileView
import com.android.systemui.statusbar.pipeline.shared.ConnectivityConstants
-import java.util.concurrent.ConcurrentHashMap
+import com.android.systemui.util.composable.kairos.toComposeState
+import dagger.Provides
+import dagger.multibindings.ElementsIntoSet
import javax.inject.Inject
-import kotlin.coroutines.CoroutineContext
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.Job
-import kotlinx.coroutines.cancel
-import kotlinx.coroutines.flow.SharingStarted
-import kotlinx.coroutines.flow.StateFlow
-import kotlinx.coroutines.flow.flatMapLatest
-import kotlinx.coroutines.flow.flowOf
-import kotlinx.coroutines.flow.map
-import kotlinx.coroutines.flow.mapLatest
-import kotlinx.coroutines.flow.stateIn
+import javax.inject.Provider
/**
* View model for describing the system's current mobile cellular connections. The result is a list
- * of [MobileIconViewModel]s which describe the individual icons and can be bound to
+ * of [MobileIconViewModelKairos]s which describe the individual icons and can be bound to
* [ModernStatusBarMobileView].
*/
+@ExperimentalKairosApi
@SysUISingleton
class MobileIconsViewModelKairos
@Inject
constructor(
val logger: MobileViewLogger,
private val verboseLogger: VerboseMobileViewLogger,
- private val interactor: MobileIconsInteractor,
+ private val interactor: MobileIconsInteractorKairos,
private val airplaneModeInteractor: AirplaneModeInteractor,
private val constants: ConnectivityConstants,
- @Background private val scope: CoroutineScope,
-) {
- @VisibleForTesting
- val reuseCache = ConcurrentHashMap<Int, Pair<MobileIconViewModel, CoroutineScope>>()
-
- val activeMobileDataSubscriptionId: StateFlow<Int?> = interactor.activeMobileDataSubscriptionId
+ private val flags: FeatureFlagsClassic,
+) : KairosBuilder by kairosBuilder() {
- val subscriptionIdsFlow: StateFlow<List<Int>> =
- interactor.filteredSubscriptions
- .mapLatest { subscriptions ->
- subscriptions.map { subscriptionModel -> subscriptionModel.subscriptionId }
- }
- .stateIn(scope, SharingStarted.WhileSubscribed(), listOf())
+ val activeSubscriptionId: State<Int?>
+ get() = interactor.activeDataIconInteractor.map { it?.subscriptionId }
- val mobileSubViewModels: StateFlow<List<MobileIconViewModelCommon>> =
- subscriptionIdsFlow
- .map { ids -> ids.map { commonViewModelForSub(it) } }
- .stateIn(scope, SharingStarted.WhileSubscribed(), emptyList())
+ val subscriptionIds: KairosState<List<Int>> =
+ interactor.filteredSubscriptions.map { subscriptions ->
+ subscriptions.map { it.subscriptionId }
+ }
- private val firstMobileSubViewModel: StateFlow<MobileIconViewModelCommon?> =
- mobileSubViewModels
- .map {
- if (it.isEmpty()) {
- null
- } else {
- // Mobile icons get reversed by [StatusBarIconController], so the last element
- // in this list will show up visually first.
- it.last()
- }
- }
- .stateIn(scope, SharingStarted.WhileSubscribed(), null)
+ val icons: Incremental<Int, MobileIconViewModelKairos> = buildIncremental {
+ interactor.icons
+ .mapValues { (subId, icon) -> buildSpec { commonViewModel(subId, icon) } }
+ .applyLatestSpecForKey()
+ }
- /**
- * A flow that emits `true` if the mobile sub that's displayed first visually is showing its
- * network type icon and `false` otherwise.
- */
- val firstMobileSubShowingNetworkTypeIcon: StateFlow<Boolean> =
- firstMobileSubViewModel
- .flatMapLatest { firstMobileSubViewModel ->
- firstMobileSubViewModel?.networkTypeIcon?.map { it != null } ?: flowOf(false)
+ /** Whether the mobile sub that's displayed first visually is showing its network type icon. */
+ val firstMobileSubShowingNetworkTypeIcon: KairosState<Boolean> = buildState {
+ combine(subscriptionIds.map { it.lastOrNull() }, icons) { lastId, icons ->
+ icons[lastId]?.networkTypeIcon?.map { it != null } ?: stateOf(false)
}
- .stateIn(scope, SharingStarted.WhileSubscribed(), false)
-
- val isStackable: StateFlow<Boolean> = interactor.isStackable
-
- init {
- scope.launch { subscriptionIdsFlow.collect { invalidateCaches(it) } }
+ .flatten()
}
- fun viewModelForSub(subId: Int, location: StatusBarLocation): LocationBasedMobileViewModel {
- val common = commonViewModelForSub(subId)
- return LocationBasedMobileViewModel.viewModelForLocation(
- common,
- interactor.getMobileConnectionInteractorForSubId(subId),
+ val isStackable: KairosState<Boolean>
+ get() = interactor.isStackable
+
+ fun viewModelForSub(
+ subId: Int,
+ location: StatusBarLocation,
+ ): BuildSpec<LocationBasedMobileViewModelKairos> = buildSpec {
+ val iconInteractor =
+ interactor.icons.sample().getOrElse(subId) { error("Unknown subscription id: $subId") }
+ val commonViewModel =
+ icons.sample().getOrElse(subId) { error("Unknown subscription id: $subId") }
+ LocationBasedMobileViewModelKairos.viewModelForLocation(
+ commonViewModel,
+ iconInteractor,
verboseLogger,
location,
- scope,
)
}
- private fun commonViewModelForSub(subId: Int): MobileIconViewModelCommon {
- return reuseCache.getOrPut(subId) { createViewModel(subId) }.first
- }
-
- private fun createViewModel(subId: Int): Pair<MobileIconViewModel, CoroutineScope> {
- // Create a child scope so we can cancel it
- val vmScope = scope.createChildScope(newTracingContext("MobileIconViewModel"))
- val vm =
- MobileIconViewModel(
- subId,
- interactor.getMobileConnectionInteractorForSubId(subId),
- airplaneModeInteractor,
- constants,
- vmScope,
+ fun shadeCarrierGroupIcon(subId: Int): BuildSpec<ShadeCarrierGroupMobileIconViewModelKairos> =
+ buildSpec {
+ val iconInteractor =
+ interactor.icons.sample().getOrElse(subId) {
+ error("Unknown subscription id: $subId")
+ }
+ val commonViewModel =
+ icons.sample().getOrElse(subId) { error("Unknown subscription id: $subId") }
+ ShadeCarrierGroupMobileIconViewModelKairos(commonViewModel, iconInteractor)
+ }
+
+ private fun BuildScope.commonViewModel(subId: Int, iconInteractor: MobileIconInteractorKairos) =
+ activated {
+ MobileIconViewModelKairos(
+ subscriptionId = subId,
+ iconInteractor = iconInteractor,
+ airplaneModeInteractor = airplaneModeInteractor,
+ constants = constants,
+ flags = flags,
)
-
- return Pair(vm, vmScope)
+ }
+
+ @dagger.Module
+ object Module {
+ @Provides
+ @ElementsIntoSet
+ fun bindKairosActivatable(
+ impl: Provider<MobileIconsViewModelKairos>
+ ): Set<@JvmSuppressWildcards KairosActivatable> =
+ if (Flags.statusBarMobileIconKairos()) setOf(impl.get()) else emptySet()
}
+}
- private fun CoroutineScope.createChildScope(extraContext: CoroutineContext) =
- CoroutineScope(coroutineContext + Job(coroutineContext[Job]) + extraContext)
+@ExperimentalKairosApi
+class MobileIconsViewModelKairosComposeWrapper(
+ icons: ComposeState<Map<Int, MobileIconViewModelKairos>>,
+ val logger: MobileViewLogger,
+) {
+ val icons: Map<Int, MobileIconViewModelKairos> by icons
+}
- private fun invalidateCaches(subIds: List<Int>) {
- reuseCache.keys
- .filter { !subIds.contains(it) }
- .forEach { id ->
- reuseCache
- .remove(id)
- // Cancel the view model's scope after removing it
- ?.second
- ?.cancel()
- }
- }
+@ExperimentalKairosApi
+fun MobileIconsViewModelKairos.composeWrapper(): BuildSpec<MobileIconsViewModelKairosComposeWrapper> = buildSpec {
+ MobileIconsViewModelKairosComposeWrapper(
+ icons = toComposeState(icons),
+ logger = logger,
+ )
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/StackedMobileIconViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/StackedMobileIconViewModel.kt
index 2c85a5150575..060454c2b1c5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/StackedMobileIconViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/StackedMobileIconViewModel.kt
@@ -22,6 +22,7 @@ import com.android.systemui.common.shared.model.Icon
import com.android.systemui.lifecycle.ExclusiveActivatable
import com.android.systemui.lifecycle.Hydrator
import com.android.systemui.statusbar.pipeline.mobile.domain.model.SignalIconModel
+import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.StackedMobileIconViewModel.DualSim
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -30,10 +31,22 @@ import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOf
+interface StackedMobileIconViewModel {
+ val dualSim: DualSim?
+ val networkTypeIcon: Icon.Resource?
+ val isIconVisible: Boolean
+
+ data class DualSim(
+ val primary: SignalIconModel.Cellular,
+ val secondary: SignalIconModel.Cellular,
+ )
+}
+
@OptIn(ExperimentalCoroutinesApi::class)
-class StackedMobileIconViewModel
+class StackedMobileIconViewModelImpl
@AssistedInject
-constructor(mobileIconsViewModel: MobileIconsViewModel) : ExclusiveActivatable() {
+constructor(mobileIconsViewModel: MobileIconsViewModel) :
+ ExclusiveActivatable(), StackedMobileIconViewModel {
private val hydrator = Hydrator("StackedMobileIconViewModel")
private val isStackable: Boolean by
@@ -52,7 +65,7 @@ constructor(mobileIconsViewModel: MobileIconsViewModel) : ExclusiveActivatable()
viewModels.sortedByDescending { it.subscriptionId == activeSubId }
}
- val dualSim: DualSim? by
+ override val dualSim: DualSim? by
hydrator.hydratedStateOf(
traceName = "dualSim",
source =
@@ -68,7 +81,7 @@ constructor(mobileIconsViewModel: MobileIconsViewModel) : ExclusiveActivatable()
initialValue = null,
)
- val networkTypeIcon: Icon.Resource? by
+ override val networkTypeIcon: Icon.Resource? by
hydrator.hydratedStateOf(
traceName = "networkTypeIcon",
source =
@@ -78,7 +91,7 @@ constructor(mobileIconsViewModel: MobileIconsViewModel) : ExclusiveActivatable()
initialValue = null,
)
- val isIconVisible: Boolean by derivedStateOf { isStackable && dualSim != null }
+ override val isIconVisible: Boolean by derivedStateOf { isStackable && dualSim != null }
override suspend fun onActivated(): Nothing {
hydrator.activate()
@@ -86,11 +99,6 @@ constructor(mobileIconsViewModel: MobileIconsViewModel) : ExclusiveActivatable()
@AssistedFactory
interface Factory {
- fun create(): StackedMobileIconViewModel
+ fun create(): StackedMobileIconViewModelImpl
}
-
- data class DualSim(
- val primary: SignalIconModel.Cellular,
- val secondary: SignalIconModel.Cellular,
- )
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/StackedMobileIconViewModelKairos.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/StackedMobileIconViewModelKairos.kt
index 2dbb02c8f095..402fdf03941d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/StackedMobileIconViewModelKairos.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/StackedMobileIconViewModelKairos.kt
@@ -16,81 +16,71 @@
package com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel
-import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
+import com.android.systemui.KairosBuilder
import com.android.systemui.common.shared.model.Icon
-import com.android.systemui.lifecycle.ExclusiveActivatable
-import com.android.systemui.lifecycle.Hydrator
+import com.android.systemui.kairos.ExperimentalKairosApi
+import com.android.systemui.kairos.State as KairosState
+import com.android.systemui.kairos.combine
+import com.android.systemui.kairos.flatMap
+import com.android.systemui.kairos.stateOf
+import com.android.systemui.kairosBuilder
import com.android.systemui.statusbar.pipeline.mobile.domain.model.SignalIconModel
+import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.StackedMobileIconViewModel.DualSim
+import com.android.systemui.util.composable.kairos.hydratedComposeStateOf
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.combine
-import kotlinx.coroutines.flow.flatMapLatest
-import kotlinx.coroutines.flow.flowOf
-@OptIn(ExperimentalCoroutinesApi::class)
+@OptIn(ExperimentalKairosApi::class)
class StackedMobileIconViewModelKairos
@AssistedInject
-constructor(mobileIconsViewModel: MobileIconsViewModel) : ExclusiveActivatable() {
- private val hydrator = Hydrator("StackedMobileIconViewModel")
+constructor(mobileIcons: MobileIconsViewModelKairos) :
+ KairosBuilder by kairosBuilder(), StackedMobileIconViewModel {
private val isStackable: Boolean by
- hydrator.hydratedStateOf(
- traceName = "isStackable",
- source = mobileIconsViewModel.isStackable,
- initialValue = false,
- )
+ hydratedComposeStateOf(mobileIcons.isStackable, initialValue = false)
- private val iconViewModelFlow: Flow<List<MobileIconViewModelCommon>> =
- combine(
- mobileIconsViewModel.mobileSubViewModels,
- mobileIconsViewModel.activeMobileDataSubscriptionId,
- ) { viewModels, activeSubId ->
- // Sort to get the active subscription first, if it's set
- viewModels.sortedByDescending { it.subscriptionId == activeSubId }
+ private val iconList: KairosState<List<MobileIconViewModelKairos>> =
+ combine(mobileIcons.icons, mobileIcons.activeSubscriptionId) { iconsBySubId, activeSubId ->
+ buildList {
+ activeSubId?.let { iconsBySubId[activeSubId]?.let { add(it) } }
+ addAll(iconsBySubId.values.asSequence().filter { it.subscriptionId != activeSubId })
+ }
}
- val dualSim: DualSim? by
- hydrator.hydratedStateOf(
- traceName = "dualSim",
- source =
- iconViewModelFlow.flatMapLatest { viewModels ->
- combine(viewModels.map { it.icon }) { icons ->
- icons
- .toList()
- .filterIsInstance<SignalIconModel.Cellular>()
- .takeIf { it.size == 2 }
- ?.let { DualSim(it[0], it[1]) }
- }
- },
+ override val dualSim: DualSim? by
+ hydratedComposeStateOf(
+ iconList.flatMap { icons ->
+ icons.map { it.icon }.combine { signalIcons -> tryParseDualSim(signalIcons) }
+ },
initialValue = null,
)
- val networkTypeIcon: Icon.Resource? by
- hydrator.hydratedStateOf(
- traceName = "networkTypeIcon",
- source =
- iconViewModelFlow.flatMapLatest { viewModels ->
- viewModels.firstOrNull()?.networkTypeIcon ?: flowOf(null)
- },
+ override val networkTypeIcon: Icon.Resource? by
+ hydratedComposeStateOf(
+ iconList.flatMap { icons -> icons.firstOrNull()?.networkTypeIcon ?: stateOf(null) },
initialValue = null,
)
- val isIconVisible: Boolean by derivedStateOf { isStackable && dualSim != null }
+ override val isIconVisible: Boolean
+ get() = isStackable && dualSim != null
- override suspend fun onActivated(): Nothing {
- hydrator.activate()
+ private fun tryParseDualSim(icons: List<SignalIconModel>): DualSim? {
+ var first: SignalIconModel.Cellular? = null
+ var second: SignalIconModel.Cellular? = null
+ for (icon in icons) {
+ when {
+ icon !is SignalIconModel.Cellular -> continue
+ first == null -> first = icon
+ second == null -> second = icon
+ else -> return null
+ }
+ }
+ return first?.let { second?.let { DualSim(first, second) } }
}
@AssistedFactory
interface Factory {
fun create(): StackedMobileIconViewModelKairos
}
-
- data class DualSim(
- val primary: SignalIconModel.Cellular,
- val secondary: SignalIconModel.Cellular,
- )
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/util/FakeMobileMappingsProxy.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/util/FakeMobileMappingsProxy.kt
index a052008d4832..cba06e3caa58 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/util/FakeMobileMappingsProxy.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/util/FakeMobileMappingsProxy.kt
@@ -33,17 +33,20 @@ class FakeMobileMappingsProxy : MobileMappingsProxy {
fun setIconMap(map: Map<String, MobileIconGroup>) {
iconMap = map
}
+
override fun mapIconSets(config: Config): Map<String, MobileIconGroup> {
if (useRealImpl) {
return realImpl.mapIconSets(config)
}
return iconMap
}
+
fun getIconMap() = iconMap
fun setDefaultIcons(group: MobileIconGroup) {
defaultIcons = group
}
+
override fun getDefaultIcons(config: Config): MobileIconGroup {
if (useRealImpl) {
return realImpl.getDefaultIcons(config)
@@ -72,3 +75,6 @@ class FakeMobileMappingsProxy : MobileMappingsProxy {
return toIconKey(networkType) + "_override"
}
}
+
+val MobileMappingsProxy.fake
+ get() = this as FakeMobileMappingsProxy
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModel.kt
index 540babad5dd1..2b1357144a7f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModel.kt
@@ -54,6 +54,7 @@ import com.android.systemui.statusbar.chips.ui.model.MultipleOngoingActivityChip
import com.android.systemui.statusbar.chips.ui.model.MultipleOngoingActivityChipsModelLegacy
import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel
import com.android.systemui.statusbar.chips.ui.viewmodel.OngoingActivityChipsViewModel
+import com.android.systemui.statusbar.chips.uievents.StatusBarChipsUiEventLogger
import com.android.systemui.statusbar.events.domain.interactor.SystemStatusEventAnimationInteractor
import com.android.systemui.statusbar.events.shared.model.SystemEventAnimationState.Idle
import com.android.systemui.statusbar.featurepods.popups.StatusBarPopupChips
@@ -228,6 +229,7 @@ constructor(
@Background bgScope: CoroutineScope,
@Background bgDispatcher: CoroutineDispatcher,
shadeDisplaysInteractor: Provider<ShadeDisplaysInteractor>,
+ private val uiEventLogger: StatusBarChipsUiEventLogger,
) : HomeStatusBarViewModel, ExclusiveActivatable() {
private val hydrator = Hydrator(traceName = "HomeStatusBarViewModel.hydrator")
@@ -591,6 +593,7 @@ constructor(
if (StatusBarPopupChips.isEnabled) {
launch { statusBarPopupChips.activate() }
}
+ launch { uiEventLogger.hydrateUiEventLogging(chipsFlow = chipsVisibilityModel) }
awaitCancellation()
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BaseUserSwitcherAdapter.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BaseUserSwitcherAdapter.kt
index 4c6374b75f82..efab21fd9364 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BaseUserSwitcherAdapter.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BaseUserSwitcherAdapter.kt
@@ -37,7 +37,7 @@ protected constructor(
protected open val users: List<UserRecord>
get() = controller.users.filter {
(!controller.isKeyguardShowing || !it.isRestricted) &&
- (controller.isUserSwitcherEnabled || it.isCurrent)
+ (controller.isUserSwitcherEnabled || it.isCurrent || it.isSignOut)
}
init {
@@ -109,6 +109,7 @@ protected constructor(
item.isAddUser,
item.isGuest,
item.isAddSupervisedUser,
+ item.isSignOut,
isTablet,
item.isManageUsers,
)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java
index 9a81992a6add..7f778bb029f1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java
@@ -256,6 +256,9 @@ public class Clock extends TextView implements
if (mClockFormat != null) {
mClockFormat.setTimeZone(mCalendar.getTimeZone());
}
+ if (mContentDescriptionFormat != null) {
+ mContentDescriptionFormat.setTimeZone(mCalendar.getTimeZone());
+ }
});
} else if (action.equals(Intent.ACTION_CONFIGURATION_CHANGED)) {
final Locale newLocale = getResources().getConfiguration().locale;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingController.java
index fa022b4768fc..0f8d534df659 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingController.java
@@ -28,7 +28,6 @@ import android.util.IndentingPrintWriter;
import androidx.annotation.NonNull;
import com.android.settingslib.devicestate.DeviceStateAutoRotateSettingManager;
-import com.android.settingslib.devicestate.DeviceStateRotationLockSettingsManager;
import com.android.systemui.Dumpable;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dump.DumpManager;
@@ -49,7 +48,7 @@ public final class DeviceStateRotationLockSettingController
private final RotationPolicyWrapper mRotationPolicyWrapper;
private final DeviceStateManager mDeviceStateManager;
private final Executor mMainExecutor;
- private final DeviceStateRotationLockSettingsManager mDeviceStateRotationLockSettingsManager;
+ private final DeviceStateAutoRotateSettingManager mDeviceStateAutoRotateSettingManager;
private final DeviceStateRotationLockSettingControllerLogger mLogger;
// On registration for DeviceStateCallback, we will receive a callback with the current state
@@ -65,13 +64,13 @@ public final class DeviceStateRotationLockSettingController
RotationPolicyWrapper rotationPolicyWrapper,
DeviceStateManager deviceStateManager,
@Main Executor executor,
- DeviceStateRotationLockSettingsManager deviceStateRotationLockSettingsManager,
+ DeviceStateAutoRotateSettingManager deviceStateAutoRotateSettingManager,
DeviceStateRotationLockSettingControllerLogger logger,
DumpManager dumpManager) {
mRotationPolicyWrapper = rotationPolicyWrapper;
mDeviceStateManager = deviceStateManager;
mMainExecutor = executor;
- mDeviceStateRotationLockSettingsManager = deviceStateRotationLockSettingsManager;
+ mDeviceStateAutoRotateSettingManager = deviceStateAutoRotateSettingManager;
mLogger = logger;
dumpManager.registerDumpable(this);
}
@@ -86,14 +85,14 @@ public final class DeviceStateRotationLockSettingController
mDeviceStateManager.registerCallback(mMainExecutor, mDeviceStateCallback);
mDeviceStateAutoRotateSettingListener = () ->
readPersistedSetting("deviceStateRotationLockChange", mDeviceState);
- mDeviceStateRotationLockSettingsManager.registerListener(
+ mDeviceStateAutoRotateSettingManager.registerListener(
mDeviceStateAutoRotateSettingListener);
} else {
if (mDeviceStateCallback != null) {
mDeviceStateManager.unregisterCallback(mDeviceStateCallback);
}
if (mDeviceStateAutoRotateSettingListener != null) {
- mDeviceStateRotationLockSettingsManager.unregisterListener(
+ mDeviceStateAutoRotateSettingManager.unregisterListener(
mDeviceStateAutoRotateSettingListener);
}
}
@@ -102,7 +101,7 @@ public final class DeviceStateRotationLockSettingController
@Override
public void onRotationLockStateChanged(boolean newRotationLocked, boolean affordanceVisible) {
int deviceState = mDeviceState;
- boolean currentRotationLocked = mDeviceStateRotationLockSettingsManager
+ boolean currentRotationLocked = mDeviceStateAutoRotateSettingManager
.isRotationLocked(deviceState);
mLogger.logRotationLockStateChanged(deviceState, newRotationLocked, currentRotationLocked);
if (deviceState == -1) {
@@ -117,7 +116,7 @@ public final class DeviceStateRotationLockSettingController
private void saveNewRotationLockSetting(boolean isRotationLocked) {
int deviceState = mDeviceState;
mLogger.logSaveNewRotationLockSetting(isRotationLocked, deviceState);
- mDeviceStateRotationLockSettingsManager.updateSetting(deviceState, isRotationLocked);
+ mDeviceStateAutoRotateSettingManager.updateSetting(deviceState, isRotationLocked);
}
private void updateDeviceState(@NonNull DeviceState state) {
@@ -139,7 +138,7 @@ public final class DeviceStateRotationLockSettingController
private void readPersistedSetting(String caller, int state) {
int rotationLockSetting =
- mDeviceStateRotationLockSettingsManager.getRotationLockSetting(state);
+ mDeviceStateAutoRotateSettingManager.getRotationLockSetting(state);
boolean shouldBeLocked = rotationLockSetting == DEVICE_STATE_ROTATION_LOCK_LOCKED;
boolean isLocked = mRotationPolicyWrapper.isRotationLocked();
@@ -167,7 +166,7 @@ public final class DeviceStateRotationLockSettingController
@Override
public void dump(@NonNull PrintWriter printWriter, @NonNull String[] args) {
IndentingPrintWriter pw = new IndentingPrintWriter(printWriter);
- mDeviceStateRotationLockSettingsManager.dump(pw);
+ mDeviceStateAutoRotateSettingManager.dump(printWriter, null);
pw.println("DeviceStateRotationLockSettingController");
pw.increaseIndent();
pw.println("mDeviceState: " + mDeviceState);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/PolicyModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/PolicyModule.kt
index a352982f58f2..f6e0123be446 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/PolicyModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/PolicyModule.kt
@@ -36,20 +36,25 @@ import com.android.systemui.qs.tiles.ModesDndTile
import com.android.systemui.qs.tiles.ModesTile
import com.android.systemui.qs.tiles.UiModeNightTile
import com.android.systemui.qs.tiles.WorkModeTile
-import com.android.systemui.qs.tiles.base.interactor.QSTileAvailabilityInteractor
-import com.android.systemui.qs.tiles.base.viewmodel.QSTileViewModelFactory
-import com.android.systemui.qs.tiles.impl.alarm.domain.AlarmTileMapper
+import com.android.systemui.qs.tiles.base.domain.interactor.QSTileAvailabilityInteractor
+import com.android.systemui.qs.tiles.base.shared.model.QSTileConfig
+import com.android.systemui.qs.tiles.base.shared.model.QSTilePolicy
+import com.android.systemui.qs.tiles.base.shared.model.QSTileUIConfig
+import com.android.systemui.qs.tiles.base.ui.viewmodel.QSTileViewModel
+import com.android.systemui.qs.tiles.base.ui.viewmodel.QSTileViewModelFactory
+import com.android.systemui.qs.tiles.base.ui.viewmodel.StubQSTileViewModel
import com.android.systemui.qs.tiles.impl.alarm.domain.interactor.AlarmTileDataInteractor
import com.android.systemui.qs.tiles.impl.alarm.domain.interactor.AlarmTileUserActionInteractor
import com.android.systemui.qs.tiles.impl.alarm.domain.model.AlarmTileModel
-import com.android.systemui.qs.tiles.impl.flashlight.domain.FlashlightMapper
+import com.android.systemui.qs.tiles.impl.alarm.ui.mapper.AlarmTileMapper
import com.android.systemui.qs.tiles.impl.flashlight.domain.interactor.FlashlightTileDataInteractor
import com.android.systemui.qs.tiles.impl.flashlight.domain.interactor.FlashlightTileUserActionInteractor
import com.android.systemui.qs.tiles.impl.flashlight.domain.model.FlashlightTileModel
-import com.android.systemui.qs.tiles.impl.location.domain.LocationTileMapper
+import com.android.systemui.qs.tiles.impl.flashlight.ui.mapper.FlashlightMapper
import com.android.systemui.qs.tiles.impl.location.domain.interactor.LocationTileDataInteractor
import com.android.systemui.qs.tiles.impl.location.domain.interactor.LocationTileUserActionInteractor
import com.android.systemui.qs.tiles.impl.location.domain.model.LocationTileModel
+import com.android.systemui.qs.tiles.impl.location.ui.mapper.LocationTileMapper
import com.android.systemui.qs.tiles.impl.modes.domain.interactor.ModesDndTileDataInteractor
import com.android.systemui.qs.tiles.impl.modes.domain.interactor.ModesDndTileUserActionInteractor
import com.android.systemui.qs.tiles.impl.modes.domain.interactor.ModesTileDataInteractor
@@ -57,25 +62,20 @@ import com.android.systemui.qs.tiles.impl.modes.domain.interactor.ModesTileUserA
import com.android.systemui.qs.tiles.impl.modes.domain.model.ModesDndTileModel
import com.android.systemui.qs.tiles.impl.modes.domain.model.ModesTileModel
import com.android.systemui.qs.tiles.impl.modes.ui.ModesDndTileMapper
-import com.android.systemui.qs.tiles.impl.modes.ui.ModesTileMapper
-import com.android.systemui.qs.tiles.impl.sensorprivacy.SensorPrivacyToggleTileDataInteractor
-import com.android.systemui.qs.tiles.impl.sensorprivacy.domain.SensorPrivacyToggleTileUserActionInteractor
+import com.android.systemui.qs.tiles.impl.modes.ui.mapper.ModesTileMapper
+import com.android.systemui.qs.tiles.impl.sensorprivacy.domain.interactor.SensorPrivacyToggleTileDataInteractor
+import com.android.systemui.qs.tiles.impl.sensorprivacy.domain.interactor.SensorPrivacyToggleTileUserActionInteractor
import com.android.systemui.qs.tiles.impl.sensorprivacy.domain.model.SensorPrivacyToggleTileModel
-import com.android.systemui.qs.tiles.impl.sensorprivacy.ui.SensorPrivacyTileResources
-import com.android.systemui.qs.tiles.impl.sensorprivacy.ui.SensorPrivacyToggleTileMapper
-import com.android.systemui.qs.tiles.impl.uimodenight.domain.UiModeNightTileMapper
+import com.android.systemui.qs.tiles.impl.sensorprivacy.ui.mapper.SensorPrivacyToggleTileMapper
+import com.android.systemui.qs.tiles.impl.sensorprivacy.ui.model.SensorPrivacyTileResources
import com.android.systemui.qs.tiles.impl.uimodenight.domain.interactor.UiModeNightTileDataInteractor
import com.android.systemui.qs.tiles.impl.uimodenight.domain.interactor.UiModeNightTileUserActionInteractor
-import com.android.systemui.qs.tiles.impl.uimodenight.domain.model.UiModeNightTileModel
+import com.android.systemui.qs.tiles.impl.uimodenight.domain.interactor.model.UiModeNightTileModel
+import com.android.systemui.qs.tiles.impl.uimodenight.ui.mapper.UiModeNightTileMapper
import com.android.systemui.qs.tiles.impl.work.domain.interactor.WorkModeTileDataInteractor
import com.android.systemui.qs.tiles.impl.work.domain.interactor.WorkModeTileUserActionInteractor
import com.android.systemui.qs.tiles.impl.work.domain.model.WorkModeTileModel
-import com.android.systemui.qs.tiles.impl.work.ui.WorkModeTileMapper
-import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
-import com.android.systemui.qs.tiles.viewmodel.QSTilePolicy
-import com.android.systemui.qs.tiles.viewmodel.QSTileUIConfig
-import com.android.systemui.qs.tiles.viewmodel.QSTileViewModel
-import com.android.systemui.qs.tiles.viewmodel.StubQSTileViewModel
+import com.android.systemui.qs.tiles.impl.work.ui.mapper.WorkModeTileMapper
import com.android.systemui.res.R
import dagger.Binds
import dagger.Module
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 d1e807f18196..e2a6c195af38 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
@@ -18,13 +18,20 @@ package com.android.systemui.statusbar.policy.dagger;
import android.content.Context;
import android.content.res.Resources;
+import android.hardware.devicestate.DeviceStateManager;
+import android.os.Handler;
import android.os.UserManager;
import com.android.internal.R;
-import com.android.settingslib.devicestate.DeviceStateRotationLockSettingsManager;
+import com.android.settingslib.devicestate.AndroidSecureSettings;
+import com.android.settingslib.devicestate.DeviceStateAutoRotateSettingManager;
+import com.android.settingslib.devicestate.DeviceStateAutoRotateSettingManagerProvider;
+import com.android.settingslib.devicestate.PosturesHelper;
+import com.android.settingslib.devicestate.SecureSettings;
import com.android.settingslib.notification.modes.ZenIconLoader;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Application;
+import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dagger.qualifiers.UiBackground;
import com.android.systemui.log.LogBuffer;
@@ -222,12 +229,34 @@ public interface StatusBarPolicyModule {
return controller;
}
- /** Returns a singleton instance of DeviceStateRotationLockSettingsManager */
+ /** */
+ @SysUISingleton
+ @Provides
+ static SecureSettings provideAndroidSecureSettings(Context context) {
+ return new AndroidSecureSettings(context.getContentResolver());
+ }
+
+ /** */
@SysUISingleton
@Provides
- static DeviceStateRotationLockSettingsManager provideAutoRotateSettingsManager(
- Context context) {
- return DeviceStateRotationLockSettingsManager.getInstance(context);
+ static PosturesHelper providePosturesHelper(Context context,
+ DeviceStateManager deviceStateManager) {
+ return new PosturesHelper(context, deviceStateManager);
+ }
+
+ /** Returns a singleton instance of DeviceStateAutoRotateSettingManager based on auto-rotate
+ * refactor flag. */
+ @SysUISingleton
+ @Provides
+ static DeviceStateAutoRotateSettingManager provideAutoRotateSettingsManager(
+ Context context,
+ @Background Executor bgExecutor,
+ SecureSettings secureSettings,
+ @Main Handler mainHandler,
+ PosturesHelper posturesHelper
+ ) {
+ return DeviceStateAutoRotateSettingManagerProvider.createInstance(context, bgExecutor,
+ secureSettings, mainHandler, posturesHelper);
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowController.kt
index ecfcb29a9944..eae310ed169b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowController.kt
@@ -19,7 +19,7 @@ package com.android.systemui.statusbar.window
import android.content.Context
import android.view.View
import android.view.ViewGroup
-import com.android.app.viewcapture.ViewCaptureAwareWindowManager
+import android.view.WindowManager
import com.android.systemui.animation.ActivityTransitionAnimator
import com.android.systemui.fragments.FragmentHostManager
import com.android.systemui.statusbar.data.repository.StatusBarConfigurationController
@@ -84,7 +84,7 @@ interface StatusBarWindowController {
fun interface Factory {
fun create(
context: Context,
- viewCaptureAwareWindowManager: ViewCaptureAwareWindowManager,
+ windowManager: WindowManager,
statusBarConfigurationController: StatusBarConfigurationController,
contentInsetsProvider: StatusBarContentInsetsProvider,
): StatusBarWindowController
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowControllerImpl.java
index 25972ac2bedf..77cd58111b94 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowControllerImpl.java
@@ -47,7 +47,6 @@ import android.view.WindowManager;
import androidx.annotation.NonNull;
-import com.android.app.viewcapture.ViewCaptureAwareWindowManager;
import com.android.internal.policy.SystemBarUtils;
import com.android.systemui.animation.ActivityTransitionAnimator;
import com.android.systemui.animation.DelegateTransitionAnimatorController;
@@ -78,7 +77,7 @@ public class StatusBarWindowControllerImpl implements StatusBarWindowController
private static final boolean DEBUG = false;
private final Context mContext;
- private final ViewCaptureAwareWindowManager mWindowManager;
+ private final WindowManager mWindowManager;
private final StatusBarConfigurationController mStatusBarConfigurationController;
private final IWindowManager mIWindowManager;
private final StatusBarContentInsetsProvider mContentInsetsProvider;
@@ -100,7 +99,7 @@ public class StatusBarWindowControllerImpl implements StatusBarWindowController
public StatusBarWindowControllerImpl(
@Assisted Context context,
@InternalWindowViewInflater StatusBarWindowViewInflater statusBarWindowViewInflater,
- @Assisted ViewCaptureAwareWindowManager viewCaptureAwareWindowManager,
+ @Assisted WindowManager windowManager,
@Assisted StatusBarConfigurationController statusBarConfigurationController,
IWindowManager iWindowManager,
@Assisted StatusBarContentInsetsProvider contentInsetsProvider,
@@ -108,7 +107,7 @@ public class StatusBarWindowControllerImpl implements StatusBarWindowController
Optional<UnfoldTransitionProgressProvider> unfoldTransitionProgressProvider,
@Main Executor mainExecutor) {
mContext = context;
- mWindowManager = viewCaptureAwareWindowManager;
+ mWindowManager = windowManager;
mStatusBarConfigurationController = statusBarConfigurationController;
mIWindowManager = iWindowManager;
mContentInsetsProvider = contentInsetsProvider;
@@ -406,7 +405,7 @@ public class StatusBarWindowControllerImpl implements StatusBarWindowController
@Override
StatusBarWindowControllerImpl create(
@NonNull Context context,
- @NonNull ViewCaptureAwareWindowManager viewCaptureAwareWindowManager,
+ @NonNull WindowManager windowManager,
@NonNull StatusBarConfigurationController statusBarConfigurationController,
@NonNull StatusBarContentInsetsProvider contentInsetsProvider);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowControllerStore.kt b/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowControllerStore.kt
index 39afc38dad11..74c028a8c7ae 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowControllerStore.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowControllerStore.kt
@@ -18,7 +18,6 @@ package com.android.systemui.statusbar.window
import android.content.Context
import android.view.WindowManager
-import com.android.app.viewcapture.ViewCaptureAwareWindowManager
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.display.data.repository.DisplayRepository
@@ -42,7 +41,6 @@ constructor(
@Background backgroundApplicationScope: CoroutineScope,
private val controllerFactory: StatusBarWindowController.Factory,
private val displayWindowPropertiesRepository: DisplayWindowPropertiesRepository,
- private val viewCaptureAwareWindowManagerFactory: ViewCaptureAwareWindowManager.Factory,
private val statusBarConfigurationControllerStore: StatusBarConfigurationControllerStore,
private val statusBarContentInsetsProviderStore: StatusBarContentInsetsProviderStore,
displayRepository: DisplayRepository,
@@ -67,11 +65,9 @@ constructor(
statusBarConfigurationControllerStore.forDisplay(displayId) ?: return null
val contentInsetsProvider =
statusBarContentInsetsProviderStore.forDisplay(displayId) ?: return null
- val viewCaptureAwareWindowManager =
- viewCaptureAwareWindowManagerFactory.create(statusBarDisplayContext.windowManager)
return controllerFactory.create(
statusBarDisplayContext.context,
- viewCaptureAwareWindowManager,
+ statusBarDisplayContext.windowManager,
statusBarConfigurationController,
contentInsetsProvider,
)
@@ -89,7 +85,7 @@ class SingleDisplayStatusBarWindowControllerStore
@Inject
constructor(
context: Context,
- viewCaptureAwareWindowManager: ViewCaptureAwareWindowManager,
+ windowManager: WindowManager,
factory: StatusBarWindowControllerImpl.Factory,
statusBarConfigurationControllerStore: StatusBarConfigurationControllerStore,
statusBarContentInsetsProviderStore: StatusBarContentInsetsProviderStore,
@@ -98,7 +94,7 @@ constructor(
PerDisplayStore<StatusBarWindowController> by SingleDisplayStore(
factory.create(
context,
- viewCaptureAwareWindowManager,
+ windowManager,
statusBarConfigurationControllerStore.defaultDisplay,
statusBarContentInsetsProviderStore.defaultDisplay,
)
diff --git a/packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayController.kt b/packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayController.kt
index 635576743462..ea5f422f977d 100644
--- a/packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayController.kt
+++ b/packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayController.kt
@@ -32,7 +32,6 @@ import android.view.accessibility.AccessibilityManager.FLAG_CONTENT_ICONS
import android.view.accessibility.AccessibilityManager.FLAG_CONTENT_TEXT
import androidx.annotation.CallSuper
import androidx.annotation.VisibleForTesting
-import com.android.app.viewcapture.ViewCaptureAwareWindowManager
import com.android.systemui.CoreStartable
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.dump.DumpManager
@@ -70,7 +69,7 @@ import java.io.PrintWriter
abstract class TemporaryViewDisplayController<T : TemporaryViewInfo, U : TemporaryViewLogger<T>>(
internal val context: Context,
internal val logger: U,
- internal val windowManager: ViewCaptureAwareWindowManager,
+ internal val windowManager: WindowManager,
@Main private val mainExecutor: DelayableExecutor,
private val accessibilityManager: AccessibilityManager,
private val configurationController: ConfigurationController,
diff --git a/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinator.kt b/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinator.kt
index 9b9cba9186f4..b6f54331b29f 100644
--- a/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinator.kt
@@ -29,6 +29,7 @@ import android.view.View
import android.view.View.ACCESSIBILITY_LIVE_REGION_ASSERTIVE
import android.view.View.ACCESSIBILITY_LIVE_REGION_NONE
import android.view.ViewGroup
+import android.view.WindowManager
import android.view.accessibility.AccessibilityManager
import android.view.accessibility.AccessibilityNodeInfo
import android.widget.ImageView
@@ -37,7 +38,6 @@ import androidx.annotation.DimenRes
import androidx.annotation.IdRes
import androidx.annotation.VisibleForTesting
import com.android.app.animation.Interpolators
-import com.android.app.viewcapture.ViewCaptureAwareWindowManager
import com.android.internal.widget.CachingIconView
import com.android.systemui.Gefingerpoken
import com.android.systemui.classifier.FalsingCollector
@@ -81,7 +81,7 @@ open class ChipbarCoordinator
constructor(
context: Context,
logger: ChipbarLogger,
- viewCaptureAwareWindowManager: ViewCaptureAwareWindowManager,
+ windowManager: WindowManager,
@Main mainExecutor: DelayableExecutor,
accessibilityManager: AccessibilityManager,
configurationController: ConfigurationController,
@@ -100,7 +100,7 @@ constructor(
TemporaryViewDisplayController<ChipbarInfo, ChipbarLogger>(
context,
logger,
- viewCaptureAwareWindowManager,
+ windowManager,
mainExecutor,
accessibilityManager,
configurationController,
diff --git a/packages/SystemUI/src/com/android/systemui/topwindoweffects/TopLevelWindowEffects.kt b/packages/SystemUI/src/com/android/systemui/topwindoweffects/TopLevelWindowEffects.kt
index c11e4c507914..00c6ffd861da 100644
--- a/packages/SystemUI/src/com/android/systemui/topwindoweffects/TopLevelWindowEffects.kt
+++ b/packages/SystemUI/src/com/android/systemui/topwindoweffects/TopLevelWindowEffects.kt
@@ -21,7 +21,6 @@ import android.graphics.PixelFormat
import android.view.Gravity
import android.view.WindowInsets
import android.view.WindowManager
-import com.android.app.viewcapture.ViewCaptureAwareWindowManager
import com.android.systemui.CoreStartable
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
@@ -38,7 +37,7 @@ import javax.inject.Inject
class TopLevelWindowEffects @Inject constructor(
@Application private val context: Context,
@Application private val applicationScope: CoroutineScope,
- private val windowManager: ViewCaptureAwareWindowManager,
+ private val windowManager: WindowManager,
private val squeezeEffectInteractor: SqueezeEffectInteractor,
private val keyEventInteractor: KeyEventInteractor,
private val viewModelFactory: SqueezeEffectViewModel.Factory
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/DisplaySwitchLatencyLogger.kt b/packages/SystemUI/src/com/android/systemui/unfold/DisplaySwitchLatencyLogger.kt
index 1cc7a3185a5d..5541c50fb650 100644
--- a/packages/SystemUI/src/com/android/systemui/unfold/DisplaySwitchLatencyLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/unfold/DisplaySwitchLatencyLogger.kt
@@ -50,7 +50,7 @@ class DisplaySwitchLatencyLogger {
onScreenTurningOnToOnDrawnMs,
onDrawnToOnScreenTurnedOnMs,
trackingResult,
- screenWakelockstatus
+ screenWakelockStatus,
)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/DisplaySwitchLatencyTracker.kt b/packages/SystemUI/src/com/android/systemui/unfold/DisplaySwitchLatencyTracker.kt
index 5800d5ed41c6..336e8d172ad4 100644
--- a/packages/SystemUI/src/com/android/systemui/unfold/DisplaySwitchLatencyTracker.kt
+++ b/packages/SystemUI/src/com/android/systemui/unfold/DisplaySwitchLatencyTracker.kt
@@ -36,11 +36,11 @@ import com.android.systemui.power.shared.model.WakeSleepReason
import com.android.systemui.power.shared.model.WakefulnessModel
import com.android.systemui.power.shared.model.WakefulnessState
import com.android.systemui.shared.system.SysUiStatsLog
-import com.android.systemui.unfold.DisplaySwitchLatencyTracker.DisplaySwitchLatencyEvent
import com.android.systemui.unfold.DisplaySwitchLatencyTracker.TrackingResult.CORRUPTED
import com.android.systemui.unfold.DisplaySwitchLatencyTracker.TrackingResult.SUCCESS
import com.android.systemui.unfold.DisplaySwitchLatencyTracker.TrackingResult.TIMED_OUT
import com.android.systemui.unfold.dagger.UnfoldSingleThreadBg
+import com.android.systemui.unfold.data.repository.ScreenTimeoutPolicyRepository
import com.android.systemui.unfold.data.repository.UnfoldTransitionStatus.TransitionStarted
import com.android.systemui.unfold.domain.interactor.UnfoldTransitionInteractor
import com.android.systemui.util.Compile
@@ -80,6 +80,7 @@ constructor(
private val context: Context,
private val deviceStateRepository: DeviceStateRepository,
private val powerInteractor: PowerInteractor,
+ private val screenTimeoutPolicyRepository: ScreenTimeoutPolicyRepository,
private val unfoldTransitionInteractor: UnfoldTransitionInteractor,
private val animationStatusRepository: AnimationStatusRepository,
private val keyguardInteractor: KeyguardInteractor,
@@ -287,7 +288,18 @@ constructor(
log { "fromFoldableDeviceState=$fromFoldableDeviceState" }
instantForTrack(TAG) { "fromFoldableDeviceState=$fromFoldableDeviceState" }
- return copy(fromFoldableDeviceState = fromFoldableDeviceState)
+ val screenTimeoutActive = screenTimeoutPolicyRepository.screenTimeoutActive.value
+ val screenWakelockStatus =
+ if (screenTimeoutActive) {
+ NO_SCREEN_WAKELOCKS
+ } else {
+ HAS_SCREEN_WAKELOCKS
+ }
+
+ return copy(
+ fromFoldableDeviceState = fromFoldableDeviceState,
+ screenWakelockStatus = screenWakelockStatus
+ )
}
private fun DisplaySwitchLatencyEvent.withAfterFields(
@@ -344,7 +356,7 @@ constructor(
val onDrawnToOnScreenTurnedOnMs: Int = VALUE_UNKNOWN,
val trackingResult: Int =
SysUiStatsLog.DISPLAY_SWITCH_LATENCY_TRACKED__TRACKING_RESULT__UNKNOWN_RESULT,
- val screenWakelockstatus: Int =
+ val screenWakelockStatus: Int =
SysUiStatsLog.DISPLAY_SWITCH_LATENCY_TRACKED__SCREEN_WAKELOCK_STATUS__SCREEN_WAKELOCK_STATUS_UNKNOWN,
)
@@ -372,5 +384,10 @@ constructor(
SysUiStatsLog.DISPLAY_SWITCH_LATENCY_TRACKED__FROM_FOLDABLE_DEVICE_STATE__STATE_OPENED
private const val FOLDABLE_DEVICE_STATE_FLIPPED =
SysUiStatsLog.DISPLAY_SWITCH_LATENCY_TRACKED__FROM_FOLDABLE_DEVICE_STATE__STATE_FLIPPED
+
+ private const val HAS_SCREEN_WAKELOCKS =
+ SysUiStatsLog.DISPLAY_SWITCH_LATENCY_TRACKED__SCREEN_WAKELOCK_STATUS__SCREEN_WAKELOCK_STATUS_HAS_SCREEN_WAKELOCKS
+ private const val NO_SCREEN_WAKELOCKS =
+ SysUiStatsLog.DISPLAY_SWITCH_LATENCY_TRACKED__SCREEN_WAKELOCK_STATUS__SCREEN_WAKELOCK_STATUS_NO_WAKELOCKS
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/data/repository/ScreenTimeoutPolicyRepository.kt b/packages/SystemUI/src/com/android/systemui/unfold/data/repository/ScreenTimeoutPolicyRepository.kt
new file mode 100644
index 000000000000..797939447464
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/unfold/data/repository/ScreenTimeoutPolicyRepository.kt
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2025 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.unfold.data.repository
+
+import android.os.PowerManager
+import android.view.Display
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
+import java.util.concurrent.Executor
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.stateIn
+
+/** Repository to get screen timeout updates */
+@SysUISingleton
+class ScreenTimeoutPolicyRepository
+@Inject
+constructor(
+ private val powerManager: PowerManager,
+ @Background private val executor: Executor,
+ @Background private val scope: CoroutineScope,
+) {
+
+ /** Stores true if there is an active screen timeout */
+ val screenTimeoutActive: StateFlow<Boolean> =
+ conflatedCallbackFlow {
+ val listener =
+ PowerManager.ScreenTimeoutPolicyListener { screenTimeoutPolicy ->
+ trySend(screenTimeoutPolicy == PowerManager.SCREEN_TIMEOUT_ACTIVE)
+ }
+ powerManager.addScreenTimeoutPolicyListener(
+ Display.DEFAULT_DISPLAY,
+ executor,
+ listener,
+ )
+ awaitClose {
+ powerManager.removeScreenTimeoutPolicyListener(
+ Display.DEFAULT_DISPLAY,
+ listener,
+ )
+ }
+ }
+ .stateIn(scope, started = SharingStarted.Eagerly, initialValue = true)
+}
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 05b2e0d1423e..b33eafcdfa84 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
@@ -414,16 +414,15 @@ constructor(
}
}
+ private suspend fun SelectedUserModel.isEligibleForLogout(): Boolean {
+ return withContext(backgroundDispatcher) {
+ selectionStatus == SelectionStatus.SELECTION_COMPLETE &&
+ devicePolicyManager.logoutUser != null
+ }
+ }
+
companion object {
private const val TAG = "UserRepository"
@VisibleForTesting const val SETTING_SIMPLE_USER_SWITCHER = "lockscreenSimpleUserSwitcher"
}
}
-
-fun SelectedUserModel.isEligibleForLogout(): Boolean {
- // TODO(b/206032495): should call mDevicePolicyManager.getLogoutUserId() instead of
- // hardcode it to USER_SYSTEM so it properly supports headless system user mode
- // (and then call mDevicePolicyManager.clearLogoutUser() after switched)
- return selectionStatus == SelectionStatus.SELECTION_COMPLETE &&
- userInfo.id != android.os.UserHandle.USER_SYSTEM
-}
diff --git a/packages/SystemUI/src/com/android/systemui/user/data/source/UserRecord.kt b/packages/SystemUI/src/com/android/systemui/user/data/source/UserRecord.kt
index d4fb5634bd1d..e16a51a6f6fa 100644
--- a/packages/SystemUI/src/com/android/systemui/user/data/source/UserRecord.kt
+++ b/packages/SystemUI/src/com/android/systemui/user/data/source/UserRecord.kt
@@ -42,6 +42,8 @@ data class UserRecord(
@JvmField val isSwitchToEnabled: Boolean = false,
/** Whether this record represents an option to add another supervised user to the device. */
@JvmField val isAddSupervisedUser: Boolean = false,
+ /** Whether this record represents an option to sign out of the current user. */
+ @JvmField val isSignOut: Boolean = false,
/**
* An enforcing admin, if the user action represented by this record is disabled by the admin.
* If not disabled, this is `null`.
@@ -49,7 +51,7 @@ data class UserRecord(
@JvmField val enforcedAdmin: RestrictedLockUtils.EnforcedAdmin? = null,
/** Whether this record is to go to the Settings page to manage users. */
- @JvmField val isManageUsers: Boolean = false
+ @JvmField val isManageUsers: Boolean = false,
) {
/** Returns a new instance of [UserRecord] with its [isCurrent] set to the given value. */
fun copyWithIsCurrent(isCurrent: Boolean): UserRecord {
diff --git a/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserSwitcherInteractor.kt b/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserSwitcherInteractor.kt
index 163288b25b28..b82aefc1ac1c 100644
--- a/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserSwitcherInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserSwitcherInteractor.kt
@@ -38,6 +38,7 @@ import com.android.internal.util.UserIcons
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.keyguard.KeyguardUpdateMonitorCallback
import com.android.systemui.Flags.switchUserOnBg
+import com.android.systemui.Flags.userSwitcherAddSignOutOption
import com.android.systemui.SystemUISecondaryUserService
import com.android.systemui.animation.Expandable
import com.android.systemui.broadcast.BroadcastDispatcher
@@ -110,6 +111,7 @@ constructor(
private val uiEventLogger: UiEventLogger,
private val userRestrictionChecker: UserRestrictionChecker,
private val processWrapper: ProcessWrapper,
+ private val userLogoutInteractor: UserLogoutInteractor,
) {
/**
* Defines interface for classes that can be notified when the state of users on the device is
@@ -242,6 +244,12 @@ constructor(
) {
add(UserActionModel.NAVIGATE_TO_USER_MANAGEMENT)
}
+ if (
+ userSwitcherAddSignOutOption() &&
+ userLogoutInteractor.isLogoutEnabled.value
+ ) {
+ add(UserActionModel.SIGN_OUT)
+ }
}
}
.flowOn(backgroundDispatcher)
@@ -261,7 +269,8 @@ constructor(
action = it,
selectedUserId = selectedUserInfo.id,
isRestricted =
- it != UserActionModel.ENTER_GUEST_MODE &&
+ it != UserActionModel.SIGN_OUT &&
+ it != UserActionModel.ENTER_GUEST_MODE &&
it != UserActionModel.NAVIGATE_TO_USER_MANAGEMENT &&
!settings.isAddUsersFromLockscreen,
)
@@ -499,6 +508,10 @@ constructor(
Intent(Settings.ACTION_USER_SETTINGS),
/* dismissShade= */ true,
)
+ UserActionModel.SIGN_OUT -> {
+ dismissDialog()
+ applicationScope.launch { userLogoutInteractor.logOut() }
+ }
}
}
@@ -583,9 +596,10 @@ constructor(
actionType = action,
isRestricted = isRestricted,
isSwitchToEnabled =
- canSwitchUsers(selectedUserId = selectedUserId, isAction = true) &&
- // If the user is auto-created is must not be currently resetting.
- !(isGuestUserAutoCreated && isGuestUserResetting),
+ action == UserActionModel.SIGN_OUT ||
+ (canSwitchUsers(selectedUserId = selectedUserId, isAction = true) &&
+ // If the user is auto-created is must not be currently resetting.
+ !(isGuestUserAutoCreated && isGuestUserResetting)),
userRestrictionChecker = userRestrictionChecker,
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/user/legacyhelper/data/LegacyUserDataHelper.kt b/packages/SystemUI/src/com/android/systemui/user/legacyhelper/data/LegacyUserDataHelper.kt
index 80139bd6ac0c..23ca4ceda2de 100644
--- a/packages/SystemUI/src/com/android/systemui/user/legacyhelper/data/LegacyUserDataHelper.kt
+++ b/packages/SystemUI/src/com/android/systemui/user/legacyhelper/data/LegacyUserDataHelper.kt
@@ -74,6 +74,7 @@ object LegacyUserDataHelper {
isGuest = actionType == UserActionModel.ENTER_GUEST_MODE,
isAddUser = actionType == UserActionModel.ADD_USER,
isAddSupervisedUser = actionType == UserActionModel.ADD_SUPERVISED_USER,
+ isSignOut = actionType == UserActionModel.SIGN_OUT,
isRestricted = isRestricted,
isSwitchToEnabled = isSwitchToEnabled,
enforcedAdmin =
@@ -94,6 +95,7 @@ object LegacyUserDataHelper {
record.isAddSupervisedUser -> UserActionModel.ADD_SUPERVISED_USER
record.isGuest -> UserActionModel.ENTER_GUEST_MODE
record.isManageUsers -> UserActionModel.NAVIGATE_TO_USER_MANAGEMENT
+ record.isSignOut -> UserActionModel.SIGN_OUT
else -> error("Not a known action: $record")
}
}
@@ -105,15 +107,14 @@ object LegacyUserDataHelper {
private fun getEnforcedAdmin(
context: Context,
selectedUserId: Int,
- userRestrictionChecker: UserRestrictionChecker
+ userRestrictionChecker: UserRestrictionChecker,
): EnforcedAdmin? {
val admin =
userRestrictionChecker.checkIfRestrictionEnforced(
context,
UserManager.DISALLOW_ADD_USER,
selectedUserId,
- )
- ?: return null
+ ) ?: return null
return if (
!userRestrictionChecker.hasBaseUserRestriction(
@@ -145,11 +146,6 @@ object LegacyUserDataHelper {
val unscaledOrNull = manager.getUserIcon(userInfo.id) ?: return null
val avatarSize = context.resources.getDimensionPixelSize(R.dimen.max_avatar_size)
- return Bitmap.createScaledBitmap(
- unscaledOrNull,
- avatarSize,
- avatarSize,
- /* filter= */ true,
- )
+ return Bitmap.createScaledBitmap(unscaledOrNull, avatarSize, avatarSize, /* filter= */ true)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/user/legacyhelper/ui/LegacyUserUiHelper.kt b/packages/SystemUI/src/com/android/systemui/user/legacyhelper/ui/LegacyUserUiHelper.kt
index 09cef1ed64fc..e7a3c23e9119 100644
--- a/packages/SystemUI/src/com/android/systemui/user/legacyhelper/ui/LegacyUserUiHelper.kt
+++ b/packages/SystemUI/src/com/android/systemui/user/legacyhelper/ui/LegacyUserUiHelper.kt
@@ -41,6 +41,7 @@ object LegacyUserUiHelper {
isAddUser: Boolean,
isGuest: Boolean,
isAddSupervisedUser: Boolean,
+ isSignOut: Boolean,
isTablet: Boolean = false,
isManageUsers: Boolean,
): Int {
@@ -52,6 +53,8 @@ object LegacyUserUiHelper {
com.android.settingslib.R.drawable.ic_account_circle
} else if (isAddSupervisedUser) {
com.android.settingslib.R.drawable.ic_add_supervised_user
+ } else if (isSignOut) {
+ com.android.internal.R.drawable.ic_logout
} else if (isManageUsers) {
R.drawable.ic_manage_users
} else {
@@ -81,6 +84,7 @@ object LegacyUserUiHelper {
isGuestUserResetting = isGuestUserResetting,
isAddUser = record.isAddUser,
isAddSupervisedUser = record.isAddSupervisedUser,
+ isSignOut = record.isSignOut,
isTablet = isTablet,
isManageUsers = record.isManageUsers,
)
@@ -111,10 +115,11 @@ object LegacyUserUiHelper {
isGuestUserResetting: Boolean,
isAddUser: Boolean,
isAddSupervisedUser: Boolean,
+ isSignOut: Boolean,
isTablet: Boolean = false,
isManageUsers: Boolean,
): Int {
- check(isGuest || isAddUser || isAddSupervisedUser || isManageUsers)
+ check(isGuest || isAddUser || isAddSupervisedUser || isManageUsers || isSignOut)
return when {
isGuest && isGuestUserAutoCreated && isGuestUserResetting ->
@@ -124,6 +129,7 @@ object LegacyUserUiHelper {
isGuest -> com.android.internal.R.string.guest_name
isAddUser -> com.android.settingslib.R.string.user_add_user
isAddSupervisedUser -> R.string.add_user_supervised
+ isSignOut -> com.android.internal.R.string.global_action_logout
isManageUsers -> R.string.manage_users
else -> error("This should never happen!")
}
diff --git a/packages/SystemUI/src/com/android/systemui/user/shared/model/UserActionModel.kt b/packages/SystemUI/src/com/android/systemui/user/shared/model/UserActionModel.kt
index 823bf74dc0f0..7f67d7691bf5 100644
--- a/packages/SystemUI/src/com/android/systemui/user/shared/model/UserActionModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/user/shared/model/UserActionModel.kt
@@ -22,4 +22,5 @@ enum class UserActionModel {
ADD_USER,
ADD_SUPERVISED_USER,
NAVIGATE_TO_USER_MANAGEMENT,
+ SIGN_OUT,
}
diff --git a/packages/SystemUI/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModel.kt b/packages/SystemUI/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModel.kt
index 4089889f4b1e..2e3af1c7ad00 100644
--- a/packages/SystemUI/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModel.kt
@@ -129,9 +129,7 @@ constructor(
cancelButtonClicked || executedActionFinish || userSwitched
}
- private fun toViewModel(
- model: UserModel,
- ): UserViewModel {
+ private fun toViewModel(model: UserModel): UserViewModel {
return UserViewModel(
viewKey = model.id,
name =
@@ -152,14 +150,13 @@ constructor(
)
}
- private fun toViewModel(
- model: UserActionModel,
- ): UserActionViewModel {
+ private fun toViewModel(model: UserActionModel): UserActionViewModel {
return UserActionViewModel(
viewKey = model.ordinal.toLong(),
iconResourceId =
LegacyUserUiHelper.getUserSwitcherActionIconResourceId(
isAddSupervisedUser = model == UserActionModel.ADD_SUPERVISED_USER,
+ isSignOut = model == UserActionModel.SIGN_OUT,
isAddUser = model == UserActionModel.ADD_USER,
isGuest = model == UserActionModel.ENTER_GUEST_MODE,
isManageUsers = model == UserActionModel.NAVIGATE_TO_USER_MANAGEMENT,
@@ -171,6 +168,7 @@ constructor(
isGuestUserAutoCreated = guestUserInteractor.isGuestUserAutoCreated,
isGuestUserResetting = guestUserInteractor.isGuestUserResetting,
isAddSupervisedUser = model == UserActionModel.ADD_SUPERVISED_USER,
+ isSignOut = model == UserActionModel.SIGN_OUT,
isAddUser = model == UserActionModel.ADD_USER,
isManageUsers = model == UserActionModel.NAVIGATE_TO_USER_MANAGEMENT,
isTablet = true,
diff --git a/packages/SystemUI/src/com/android/systemui/util/AlarmTimeout.java b/packages/SystemUI/src/com/android/systemui/util/AlarmTimeout.java
index a791376a48d5..38ac5e6be2e1 100644
--- a/packages/SystemUI/src/com/android/systemui/util/AlarmTimeout.java
+++ b/packages/SystemUI/src/com/android/systemui/util/AlarmTimeout.java
@@ -26,7 +26,6 @@ import android.os.SystemClock;
*/
public class AlarmTimeout implements AlarmManager.OnAlarmListener {
- public static final int MODE_CRASH_IF_SCHEDULED = 0;
public static final int MODE_IGNORE_IF_SCHEDULED = 1;
public static final int MODE_RESCHEDULE_IF_SCHEDULED = 2;
@@ -48,17 +47,11 @@ public class AlarmTimeout implements AlarmManager.OnAlarmListener {
* Schedules an alarm in {@code timeout} milliseconds in the future.
*
* @param timeout How long to wait from now.
- * @param mode {@link #MODE_CRASH_IF_SCHEDULED}, {@link #MODE_IGNORE_IF_SCHEDULED} or
- * {@link #MODE_RESCHEDULE_IF_SCHEDULED}.
+ * @param mode {@link #MODE_IGNORE_IF_SCHEDULED} or {@link #MODE_RESCHEDULE_IF_SCHEDULED}.
* @return {@code true} when scheduled successfully, {@code false} otherwise.
*/
public boolean schedule(long timeout, int mode) {
switch (mode) {
- case MODE_CRASH_IF_SCHEDULED:
- if (mScheduled) {
- throw new IllegalStateException(mTag + " timeout is already scheduled");
- }
- break;
case MODE_IGNORE_IF_SCHEDULED:
if (mScheduled) {
return false;
diff --git a/packages/SystemUI/src/com/android/systemui/util/ConvenienceExtensions.kt b/packages/SystemUI/src/com/android/systemui/util/ConvenienceExtensions.kt
index 70fd5ab767d0..f1a556353273 100644
--- a/packages/SystemUI/src/com/android/systemui/util/ConvenienceExtensions.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/ConvenienceExtensions.kt
@@ -20,7 +20,6 @@ import android.graphics.Rect
import android.util.IndentingPrintWriter
import android.view.View
import android.view.ViewGroup
-import dagger.Lazy
import java.io.PrintWriter
/** [Sequence] that yields all of the direct children of this [ViewGroup] */
@@ -56,11 +55,6 @@ val View.boundsOnScreen: Rect
return bounds
}
-/** Extension method to convert [dagger.Lazy] to [kotlin.Lazy] for object of any class [T]. */
-fun <T> Lazy<T>.toKotlinLazy(): kotlin.Lazy<T> {
- return lazy { this.get() }
-}
-
/**
* Returns whether this [Collection] contains exactly all [elements].
*
diff --git a/packages/SystemUI/src/com/android/systemui/util/composable/kairos/ActivatedKairosSpec.kt b/packages/SystemUI/src/com/android/systemui/util/composable/kairos/ActivatedKairosSpec.kt
new file mode 100644
index 000000000000..a108b1081cd2
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/composable/kairos/ActivatedKairosSpec.kt
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.util.composable.kairos
+
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import com.android.systemui.kairos.BuildSpec
+import com.android.systemui.kairos.ExperimentalKairosApi
+import com.android.systemui.kairos.KairosNetwork
+import com.android.systemui.kairos.awaitClose
+import com.android.systemui.kairos.launchEffect
+
+/**
+ * Activates the Kairos [buildSpec] within [kairosNetwork], bound to the current composition.
+ *
+ * [block] will be invoked with the result of activating the [buildSpec]. [buildSpec] will be
+ * deactivated automatically when [ActivatedKairosSpec] leaves the composition.
+ */
+@ExperimentalKairosApi
+@Composable
+fun <T> ActivatedKairosSpec(
+ buildSpec: BuildSpec<T>,
+ kairosNetwork: KairosNetwork,
+ block: @Composable (T) -> Unit,
+) {
+ val uninit = Any()
+ var state by remember { mutableStateOf<Any?>(uninit) }
+ LaunchedEffect(key1 = Unit) {
+ kairosNetwork.activateSpec {
+ val v = buildSpec.applySpec()
+ launchEffect {
+ state = v
+ awaitClose { state = uninit }
+ }
+ }
+ }
+ state.let {
+ if (it !== uninit) {
+ @Suppress("UNCHECKED_CAST") block(it as T)
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/util/composable/kairos/HydratedComposeStateOf.kt b/packages/SystemUI/src/com/android/systemui/util/composable/kairos/HydratedComposeStateOf.kt
new file mode 100644
index 000000000000..0d53a001f3f4
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/composable/kairos/HydratedComposeStateOf.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2025 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.util.composable.kairos
+
+import androidx.compose.runtime.mutableStateOf
+import com.android.systemui.KairosBuilder
+import com.android.systemui.kairos.ExperimentalKairosApi
+import com.android.systemui.kairos.State
+
+@ExperimentalKairosApi
+fun <T> KairosBuilder.hydratedComposeStateOf(
+ source: State<T>,
+ initialValue: T,
+): androidx.compose.runtime.State<T> =
+ mutableStateOf(initialValue).also { state ->
+ onActivated { source.observe { state.value = it } }
+ }
diff --git a/packages/SystemUI/src/com/android/systemui/util/composable/kairos/RememberKairosActivatable.kt b/packages/SystemUI/src/com/android/systemui/util/composable/kairos/RememberKairosActivatable.kt
new file mode 100644
index 000000000000..03a60bc002e8
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/composable/kairos/RememberKairosActivatable.kt
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2025 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.util.composable.kairos
+
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.remember
+import com.android.systemui.KairosActivatable
+import com.android.systemui.kairos.ExperimentalKairosApi
+import com.android.systemui.kairos.KairosNetwork
+
+@ExperimentalKairosApi
+@Composable
+fun <T : KairosActivatable> rememberKairosActivatable(
+ kairosNetwork: KairosNetwork,
+ key: Any = Unit,
+ factory: () -> T,
+): T {
+ val instance = remember(key, factory) { factory() }
+ LaunchedEffect(instance, kairosNetwork) {
+ kairosNetwork.activateSpec { instance.run { activate() } }
+ }
+ return instance
+}
diff --git a/packages/SystemUI/src/com/android/systemui/util/composable/kairos/ToComposeState.kt b/packages/SystemUI/src/com/android/systemui/util/composable/kairos/ToComposeState.kt
new file mode 100644
index 000000000000..b3accb66c179
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/composable/kairos/ToComposeState.kt
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2025 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.util.composable.kairos
+
+import androidx.compose.runtime.State as ComposeState
+import androidx.compose.runtime.mutableStateOf
+import com.android.systemui.kairos.BuildScope
+import com.android.systemui.kairos.ExperimentalKairosApi
+import com.android.systemui.kairos.State as KairosState
+import com.android.systemui.kairos.changes
+
+/**
+ * Returns a [ComposeState] that is kept hydrated with the current value of [state] within this
+ * [BuildScope].
+ */
+@ExperimentalKairosApi
+fun <T> BuildScope.toComposeState(state: KairosState<T>): ComposeState<T> {
+ val initial = state.sample()
+ val cState = mutableStateOf(initial)
+ state.changes.observe { cState.value = it }
+ return cState
+}
diff --git a/packages/SystemUI/src/com/android/systemui/util/display/DisplayHelper.java b/packages/SystemUI/src/com/android/systemui/util/display/DisplayHelper.java
index 8acd6535e751..757b2d973312 100644
--- a/packages/SystemUI/src/com/android/systemui/util/display/DisplayHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/util/display/DisplayHelper.java
@@ -21,6 +21,8 @@ import android.hardware.display.DisplayManager;
import android.view.Display;
import android.view.WindowManager;
+import com.android.systemui.utils.windowmanager.WindowManagerProvider;
+
import javax.inject.Inject;
/**
@@ -29,14 +31,17 @@ import javax.inject.Inject;
public class DisplayHelper {
private final Context mContext;
private final DisplayManager mDisplayManager;
+ private final WindowManagerProvider mWindowManagerProvider;
/**
* Default constructor.
*/
@Inject
- public DisplayHelper(Context context, DisplayManager displayManager) {
+ public DisplayHelper(Context context, DisplayManager displayManager,
+ WindowManagerProvider windowManagerProvider) {
mContext = context;
mDisplayManager = displayManager;
+ mWindowManagerProvider = windowManagerProvider;
}
@@ -45,9 +50,8 @@ public class DisplayHelper {
*/
public Rect getMaxBounds(int displayId, int windowContextType) {
final Display display = mDisplayManager.getDisplay(displayId);
- WindowManager windowManager = mContext.createDisplayContext(display)
- .createWindowContext(windowContextType, null)
- .getSystemService(WindowManager.class);
+ WindowManager windowManager = mWindowManagerProvider.getWindowManager(mContext
+ .createDisplayContext(display).createWindowContext(windowContextType, null));
return windowManager.getMaximumWindowMetrics().getBounds();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/VolumeDialog.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/VolumeDialog.kt
index 3ac6c7bc0c6b..457b6ac42f8b 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/VolumeDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/VolumeDialog.kt
@@ -18,6 +18,7 @@ package com.android.systemui.volume.dialog
import android.content.Context
import android.os.Bundle
+import android.view.Gravity
import android.view.MotionEvent
import android.view.View
import android.view.ViewGroup
@@ -57,8 +58,10 @@ constructor(
attributes.apply {
title = "VolumeDialog" // Not the same as Window#setTitle
}
- setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)
+ setLayout(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.MATCH_PARENT)
+ setGravity(Gravity.END)
}
+ setCancelable(false)
setCanceledOnTouchOutside(true)
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/VolumeDialogPlugin.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/VolumeDialogPlugin.kt
index 938e313771ad..b06a3b7784f8 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/VolumeDialogPlugin.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/VolumeDialogPlugin.kt
@@ -68,6 +68,7 @@ constructor(
viewModel.isShowingSafetyWarning
.mapLatest { isShowingSafetyWarning ->
if (isShowingSafetyWarning) {
+ viewModel.onSafetyWarningDialogShown()
showSafetyWarningVisibility { viewModel.onSafetyWarningDismissed() }
}
}
@@ -76,6 +77,7 @@ constructor(
viewModel.csdWarning
.mapLatest { csdWarning ->
if (csdWarning != null) {
+ viewModel.onCsdWarningDialogShown()
showCsdWarningDialog(csdWarning, viewModel.csdWarningConfigModel.actions) {
viewModel.onCsdWarningDismissed()
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogVisibilityInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogVisibilityInteractor.kt
index bd23e8cce82d..4ed25859fb86 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogVisibilityInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogVisibilityInteractor.kt
@@ -108,6 +108,7 @@ constructor(
/** Resets current dialog timeout. */
fun resetDismissTimeout() {
+ controller.userActivity()
mutableDismissDialogEvents.tryEmit(Unit)
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/util/RingerDrawerConstraintsUtils.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/util/RingerDrawerConstraintsUtils.kt
index fb9884cf4341..ad24f08a7214 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/util/RingerDrawerConstraintsUtils.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/util/RingerDrawerConstraintsUtils.kt
@@ -111,7 +111,7 @@ private fun ConstraintSet.adjustOpenConstraintsForDrawer(
view.id,
ConstraintSet.START,
motionLayout.context.resources.getDimensionPixelSize(
- R.dimen.volume_dialog_ringer_drawer_margin
+ R.dimen.volume_dialog_background_margin
),
)
}
@@ -121,7 +121,7 @@ private fun ConstraintSet.adjustOpenConstraintsForDrawer(
view.id,
ConstraintSet.END,
motionLayout.context.resources.getDimensionPixelSize(
- R.dimen.volume_dialog_components_spacing
+ R.dimen.volume_dialog_ringer_drawer_buttons_spacing
),
)
} else {
@@ -140,7 +140,7 @@ private fun ConstraintSet.adjustOpenConstraintsForDrawer(
view.id,
ConstraintSet.BOTTOM,
motionLayout.context.resources.getDimensionPixelSize(
- R.dimen.volume_dialog_components_spacing
+ R.dimen.volume_dialog_ringer_drawer_buttons_spacing
),
)
} else {
@@ -158,10 +158,10 @@ private fun ConstraintSet.adjustOpenConstraintsForDrawer(
R.dimen.volume_dialog_ringer_drawer_button_size
) * (motionLayout.childCount - 1)) +
(motionLayout.context.resources.getDimensionPixelSize(
- R.dimen.volume_dialog_ringer_drawer_margin
+ R.dimen.volume_dialog_background_margin
) * 2) +
(motionLayout.context.resources.getDimensionPixelSize(
- R.dimen.volume_dialog_components_spacing
+ R.dimen.volume_dialog_ringer_drawer_buttons_spacing
) * (motionLayout.childCount - 2))
ORIENTATION_PORTRAIT ->
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSliderViewBinder.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSliderViewBinder.kt
index 43d1ef478ae1..dc41e90faeaf 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSliderViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSliderViewBinder.kt
@@ -16,44 +16,38 @@
package com.android.systemui.volume.dialog.sliders.ui
-import android.graphics.drawable.Drawable
import android.view.View
-import androidx.compose.animation.AnimatedVisibility
-import androidx.compose.animation.core.tween
-import androidx.compose.animation.fadeIn
-import androidx.compose.animation.fadeOut
import androidx.compose.foundation.gestures.Orientation
import androidx.compose.foundation.interaction.DragInteraction
import androidx.compose.foundation.interaction.MutableInteractionSource
-import androidx.compose.foundation.layout.BoxScope
-import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.material3.ExperimentalMaterial3Api
-import androidx.compose.material3.Icon
+import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.SliderDefaults
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
-import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.platform.ComposeView
+import androidx.compose.ui.unit.DpSize
import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.android.compose.theme.PlatformTheme
-import com.android.compose.ui.graphics.painter.DrawablePainter
+import com.android.systemui.common.ui.compose.Icon
import com.android.systemui.haptics.slider.SliderHapticFeedbackFilter
import com.android.systemui.haptics.slider.compose.ui.SliderHapticsViewModel
import com.android.systemui.res.R
import com.android.systemui.volume.dialog.sliders.dagger.VolumeDialogSliderScope
-import com.android.systemui.volume.dialog.sliders.ui.compose.VolumeDialogSliderTrack
+import com.android.systemui.volume.dialog.sliders.ui.compose.SliderTrack
import com.android.systemui.volume.dialog.sliders.ui.viewmodel.VolumeDialogOverscrollViewModel
import com.android.systemui.volume.dialog.sliders.ui.viewmodel.VolumeDialogSliderViewModel
-import com.android.systemui.volume.ui.slider.AccessibilityParams
-import com.android.systemui.volume.ui.slider.Haptics
-import com.android.systemui.volume.ui.slider.Slider
+import com.android.systemui.volume.ui.compose.slider.AccessibilityParams
+import com.android.systemui.volume.ui.compose.slider.Haptics
+import com.android.systemui.volume.ui.compose.slider.Slider
+import com.android.systemui.volume.ui.compose.slider.SliderIcon
import javax.inject.Inject
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.currentCoroutineContext
@@ -86,7 +80,7 @@ constructor(
}
}
-@OptIn(ExperimentalMaterial3Api::class)
+@OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterial3ExpressiveApi::class)
@Composable
private fun VolumeDialogSlider(
viewModel: VolumeDialogSliderViewModel,
@@ -96,11 +90,10 @@ private fun VolumeDialogSlider(
) {
val colors =
SliderDefaults.colors(
- thumbColor = MaterialTheme.colorScheme.primary,
activeTickColor = MaterialTheme.colorScheme.surfaceContainerHighest,
- inactiveTickColor = MaterialTheme.colorScheme.primary,
- activeTrackColor = MaterialTheme.colorScheme.primary,
inactiveTrackColor = MaterialTheme.colorScheme.surfaceContainerHighest,
+ disabledActiveTickColor = MaterialTheme.colorScheme.surfaceContainerHighest,
+ disabledInactiveTrackColor = MaterialTheme.colorScheme.surfaceContainerHighest,
)
val collectedSliderStateModel by viewModel.state.collectAsStateWithLifecycle(null)
val sliderStateModel = collectedSliderStateModel ?: return
@@ -143,19 +136,39 @@ private fun VolumeDialogSlider(
} ?: Haptics.Disabled,
stepDistance = 1f,
track = { sliderState ->
- VolumeDialogSliderTrack(
+ SliderTrack(
sliderState,
colors = colors,
isEnabled = !sliderStateModel.isDisabled,
- activeTrackEndIcon = { iconsState ->
- VolumeIcon(sliderStateModel.icon, iconsState.isActiveTrackEndIconVisible)
+ isVertical = true,
+ activeTrackStartIcon = { iconsState ->
+ SliderIcon(
+ icon = {
+ Icon(icon = sliderStateModel.icon, modifier = Modifier.size(20.dp))
+ },
+ isVisible = iconsState.isActiveTrackStartIconVisible,
+ )
},
- inactiveTrackEndIcon = { iconsState ->
- VolumeIcon(sliderStateModel.icon, !iconsState.isActiveTrackEndIconVisible)
+ inactiveTrackStartIcon = { iconsState ->
+ SliderIcon(
+ icon = {
+ Icon(icon = sliderStateModel.icon, modifier = Modifier.size(20.dp))
+ },
+ isVisible = !iconsState.isActiveTrackStartIconVisible,
+ )
},
)
},
- accessibilityParams = AccessibilityParams(label = sliderStateModel.label),
+ thumb = { sliderState, interactions ->
+ SliderDefaults.Thumb(
+ sliderState = sliderState,
+ interactionSource = interactions,
+ enabled = !sliderStateModel.isDisabled,
+ colors = colors,
+ thumbSize = DpSize(52.dp, 4.dp),
+ )
+ },
+ accessibilityParams = AccessibilityParams(contentDescription = sliderStateModel.label),
modifier =
modifier.pointerInput(Unit) {
coroutineScope {
@@ -169,19 +182,3 @@ private fun VolumeDialogSlider(
},
)
}
-
-@Composable
-private fun BoxScope.VolumeIcon(
- drawable: Drawable,
- isVisible: Boolean,
- modifier: Modifier = Modifier,
-) {
- AnimatedVisibility(
- visible = isVisible,
- enter = fadeIn(animationSpec = tween(delayMillis = 33, durationMillis = 100)),
- exit = fadeOut(animationSpec = tween(durationMillis = 50)),
- modifier = modifier.align(Alignment.Center).size(40.dp).padding(10.dp),
- ) {
- Icon(painter = DrawablePainter(drawable), contentDescription = null)
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/compose/VolumeDialogSliderTrack.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/compose/VolumeDialogSliderTrack.kt
index 1dd9ddac79be..fb8de45bfad1 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/compose/VolumeDialogSliderTrack.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/compose/VolumeDialogSliderTrack.kt
@@ -19,6 +19,7 @@ package com.android.systemui.volume.dialog.sliders.ui.compose
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.BoxScope
import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.width
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
@@ -32,38 +33,47 @@ import androidx.compose.runtime.MutableState
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
-import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.Layout
import androidx.compose.ui.layout.Measurable
import androidx.compose.ui.layout.MeasurePolicy
import androidx.compose.ui.layout.MeasureResult
import androidx.compose.ui.layout.MeasureScope
-import androidx.compose.ui.layout.Placeable
import androidx.compose.ui.layout.layoutId
+import androidx.compose.ui.platform.LocalLayoutDirection
import androidx.compose.ui.unit.Constraints
import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.LayoutDirection
import androidx.compose.ui.unit.dp
-import androidx.compose.ui.util.fastFilter
import androidx.compose.ui.util.fastFirst
import kotlin.math.min
@Composable
@OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterial3ExpressiveApi::class)
-fun VolumeDialogSliderTrack(
+fun SliderTrack(
sliderState: SliderState,
- colors: SliderColors,
isEnabled: Boolean,
modifier: Modifier = Modifier,
+ colors: SliderColors = SliderDefaults.colors(),
thumbTrackGapSize: Dp = 6.dp,
trackCornerSize: Dp = 12.dp,
trackInsideCornerSize: Dp = 2.dp,
trackSize: Dp = 40.dp,
+ isVertical: Boolean = false,
activeTrackStartIcon: (@Composable BoxScope.(iconsState: SliderIconsState) -> Unit)? = null,
activeTrackEndIcon: (@Composable BoxScope.(iconsState: SliderIconsState) -> Unit)? = null,
inactiveTrackStartIcon: (@Composable BoxScope.(iconsState: SliderIconsState) -> Unit)? = null,
inactiveTrackEndIcon: (@Composable BoxScope.(iconsState: SliderIconsState) -> Unit)? = null,
) {
- val measurePolicy = remember(sliderState) { TrackMeasurePolicy(sliderState) }
+ val isRtl = LocalLayoutDirection.current == LayoutDirection.Rtl
+ val measurePolicy =
+ remember(sliderState) {
+ TrackMeasurePolicy(
+ sliderState = sliderState,
+ shouldMirrorIcons = !isVertical && isRtl || isVertical,
+ isVertical = isVertical,
+ gapSize = thumbTrackGapSize,
+ )
+ }
Layout(
measurePolicy = measurePolicy,
content = {
@@ -76,33 +86,41 @@ fun VolumeDialogSliderTrack(
drawStopIndicator = null,
thumbTrackGapSize = thumbTrackGapSize,
drawTick = { _, _ -> },
- modifier = Modifier.width(trackSize).layoutId(Contents.Track),
+ modifier =
+ Modifier.then(
+ if (isVertical) {
+ Modifier.width(trackSize)
+ } else {
+ Modifier.height(trackSize)
+ }
+ )
+ .layoutId(Contents.Track),
)
TrackIcon(
icon = activeTrackStartIcon,
- contentsId = Contents.Active.TrackStartIcon,
+ contents = Contents.Active.TrackStartIcon,
isEnabled = isEnabled,
colors = colors,
state = measurePolicy,
)
TrackIcon(
icon = activeTrackEndIcon,
- contentsId = Contents.Active.TrackEndIcon,
+ contents = Contents.Active.TrackEndIcon,
isEnabled = isEnabled,
colors = colors,
state = measurePolicy,
)
TrackIcon(
icon = inactiveTrackStartIcon,
- contentsId = Contents.Inactive.TrackStartIcon,
+ contents = Contents.Inactive.TrackStartIcon,
isEnabled = isEnabled,
colors = colors,
state = measurePolicy,
)
TrackIcon(
icon = inactiveTrackEndIcon,
- contentsId = Contents.Inactive.TrackEndIcon,
+ contents = Contents.Inactive.TrackEndIcon,
isEnabled = isEnabled,
colors = colors,
state = measurePolicy,
@@ -116,24 +134,47 @@ fun VolumeDialogSliderTrack(
private fun TrackIcon(
icon: (@Composable BoxScope.(sliderIconsState: SliderIconsState) -> Unit)?,
isEnabled: Boolean,
- contentsId: Contents,
+ contents: Contents,
state: SliderIconsState,
colors: SliderColors,
modifier: Modifier = Modifier,
) {
icon ?: return
- Box(modifier = modifier.layoutId(contentsId).fillMaxSize()) {
- CompositionLocalProvider(
- LocalContentColor provides contentsId.getColor(colors, isEnabled)
- ) {
- icon(state)
+ /*
+ ignore icons mirroring for the rtl layouts here because icons positioning is handled by the
+ TrackMeasurePolicy. It ensures that active icons are always above the active track and the
+ same for inactive
+ */
+ val iconColor =
+ when (contents) {
+ is Contents.Inactive ->
+ if (isEnabled) {
+ colors.inactiveTickColor
+ } else {
+ colors.disabledInactiveTickColor
+ }
+ is Contents.Active ->
+ if (isEnabled) {
+ colors.activeTickColor
+ } else {
+ colors.disabledActiveTickColor
+ }
+ is Contents.Track -> {
+ error("$contents is unsupported by the TrackIcon")
+ }
}
+ Box(modifier = modifier.layoutId(contents).fillMaxSize()) {
+ CompositionLocalProvider(LocalContentColor provides iconColor) { icon(state) }
}
}
@OptIn(ExperimentalMaterial3Api::class)
-private class TrackMeasurePolicy(private val sliderState: SliderState) :
- MeasurePolicy, SliderIconsState {
+private class TrackMeasurePolicy(
+ private val sliderState: SliderState,
+ private val shouldMirrorIcons: Boolean,
+ private val gapSize: Dp,
+ private val isVertical: Boolean,
+) : MeasurePolicy, SliderIconsState {
private val isVisible: Map<Contents, MutableState<Boolean>> =
mutableMapOf(
@@ -144,16 +185,16 @@ private class TrackMeasurePolicy(private val sliderState: SliderState) :
)
override val isActiveTrackStartIconVisible: Boolean
- get() = isVisible.getValue(Contents.Active.TrackStartIcon).value
+ get() = isVisible.getValue(Contents.Active.TrackStartIcon.resolve()).value
override val isActiveTrackEndIconVisible: Boolean
- get() = isVisible.getValue(Contents.Active.TrackEndIcon).value
+ get() = isVisible.getValue(Contents.Active.TrackEndIcon.resolve()).value
override val isInactiveTrackStartIconVisible: Boolean
- get() = isVisible.getValue(Contents.Inactive.TrackStartIcon).value
+ get() = isVisible.getValue(Contents.Inactive.TrackStartIcon.resolve()).value
override val isInactiveTrackEndIconVisible: Boolean
- get() = isVisible.getValue(Contents.Inactive.TrackEndIcon).value
+ get() = isVisible.getValue(Contents.Inactive.TrackEndIcon.resolve()).value
override fun MeasureScope.measure(
measurables: List<Measurable>,
@@ -164,178 +205,196 @@ private class TrackMeasurePolicy(private val sliderState: SliderState) :
val iconSize = min(track.width, track.height)
val iconConstraints = constraints.copy(maxWidth = iconSize, maxHeight = iconSize)
- val icons =
- measurables
- .fastFilter { it.layoutId != Contents.Track }
- .associateBy(
- keySelector = { it.layoutId as Contents },
- valueTransform = { it.measure(iconConstraints) },
- )
-
- return layout(track.width, track.height) {
- with(Contents.Track) {
- performPlacing(
- placeable = track,
- width = track.width,
- height = track.height,
- sliderState = sliderState,
- )
+ val components = buildMap {
+ put(Contents.Track, track)
+ for (measurable in measurables) {
+ // don't measure track a second time
+ if (measurable.layoutId != Contents.Track) {
+ put(
+ (measurable.layoutId as Contents).resolve(),
+ measurable.measure(iconConstraints),
+ )
+ }
}
+ }
- for (iconLayoutId in icons.keys) {
- with(iconLayoutId) {
- performPlacing(
- placeable = icons.getValue(iconLayoutId),
- width = track.width,
- height = track.height,
- sliderState = sliderState,
+ return layout(track.width, track.height) {
+ val gapSizePx = gapSize.roundToPx()
+ val coercedValueAsFraction =
+ if (shouldMirrorIcons) {
+ 1 - sliderState.coercedValueAsFraction
+ } else {
+ sliderState.coercedValueAsFraction
+ }
+ for (iconLayoutId in components.keys) {
+ val iconPlaceable = components.getValue(iconLayoutId)
+ if (isVertical) {
+ iconPlaceable.place(
+ 0,
+ iconLayoutId.calculatePosition(
+ placeableDimension = iconPlaceable.height,
+ containerDimension = track.height,
+ gapSize = gapSizePx,
+ coercedValueAsFraction = coercedValueAsFraction,
+ ),
+ )
+ } else {
+ iconPlaceable.place(
+ iconLayoutId.calculatePosition(
+ placeableDimension = iconPlaceable.width,
+ containerDimension = track.width,
+ gapSize = gapSizePx,
+ coercedValueAsFraction = coercedValueAsFraction,
+ ),
+ 0,
)
+ }
- isVisible.getValue(iconLayoutId).value =
- isVisible(
- placeable = icons.getValue(iconLayoutId),
- width = track.width,
- height = track.height,
- sliderState = sliderState,
+ // isVisible is only relevant for the icons
+ if (iconLayoutId != Contents.Track) {
+ val isVisibleState = isVisible.getValue(iconLayoutId)
+ val newIsVisible =
+ iconLayoutId.isVisible(
+ placeableDimension =
+ if (isVertical) iconPlaceable.height else iconPlaceable.width,
+ containerDimension = if (isVertical) track.height else track.width,
+ gapSize = gapSizePx,
+ coercedValueAsFraction = coercedValueAsFraction,
)
+ if (isVisibleState.value != newIsVisible) {
+ isVisibleState.value = newIsVisible
+ }
}
}
}
}
+
+ private fun Contents.resolve(): Contents {
+ return if (shouldMirrorIcons) {
+ mirrored
+ } else {
+ this
+ }
+ }
}
-@OptIn(ExperimentalMaterial3Api::class)
private sealed interface Contents {
data object Track : Contents {
- override fun Placeable.PlacementScope.performPlacing(
- placeable: Placeable,
- width: Int,
- height: Int,
- sliderState: SliderState,
- ) = placeable.place(x = 0, y = 0)
+
+ override val mirrored: Contents
+ get() = error("unsupported for Track")
+
+ override fun calculatePosition(
+ placeableDimension: Int,
+ containerDimension: Int,
+ gapSize: Int,
+ coercedValueAsFraction: Float,
+ ): Int = 0
override fun isVisible(
- placeable: Placeable,
- width: Int,
- height: Int,
- sliderState: SliderState,
- ) = true
-
- override fun getColor(sliderColors: SliderColors, isEnabled: Boolean): Color =
- error("Unsupported")
+ placeableDimension: Int,
+ containerDimension: Int,
+ gapSize: Int,
+ coercedValueAsFraction: Float,
+ ): Boolean = true
}
interface Active : Contents {
- override fun getColor(sliderColors: SliderColors, isEnabled: Boolean): Color {
- return if (isEnabled) {
- sliderColors.activeTickColor
- } else {
- sliderColors.disabledActiveTickColor
- }
- }
+
+ override fun isVisible(
+ placeableDimension: Int,
+ containerDimension: Int,
+ gapSize: Int,
+ coercedValueAsFraction: Float,
+ ): Boolean =
+ (containerDimension * coercedValueAsFraction - gapSize).toInt() > placeableDimension
data object TrackStartIcon : Active {
- override fun Placeable.PlacementScope.performPlacing(
- placeable: Placeable,
- width: Int,
- height: Int,
- sliderState: SliderState,
- ) =
- placeable.place(
- x = 0,
- y = (height * (1 - sliderState.coercedValueAsFraction)).toInt(),
- )
-
- override fun isVisible(
- placeable: Placeable,
- width: Int,
- height: Int,
- sliderState: SliderState,
- ): Boolean = (height * (sliderState.coercedValueAsFraction)).toInt() > placeable.height
+
+ override val mirrored: Contents
+ get() = Inactive.TrackEndIcon
+
+ override fun calculatePosition(
+ placeableDimension: Int,
+ containerDimension: Int,
+ gapSize: Int,
+ coercedValueAsFraction: Float,
+ ): Int = 0
}
data object TrackEndIcon : Active {
- override fun Placeable.PlacementScope.performPlacing(
- placeable: Placeable,
- width: Int,
- height: Int,
- sliderState: SliderState,
- ) = placeable.place(x = 0, y = (height - placeable.height))
-
- override fun isVisible(
- placeable: Placeable,
- width: Int,
- height: Int,
- sliderState: SliderState,
- ): Boolean = (height * (sliderState.coercedValueAsFraction)).toInt() > placeable.height
+
+ override val mirrored: Contents
+ get() = Inactive.TrackStartIcon
+
+ override fun calculatePosition(
+ placeableDimension: Int,
+ containerDimension: Int,
+ gapSize: Int,
+ coercedValueAsFraction: Float,
+ ): Int =
+ (containerDimension * coercedValueAsFraction - placeableDimension - gapSize).toInt()
}
}
interface Inactive : Contents {
- override fun getColor(sliderColors: SliderColors, isEnabled: Boolean): Color {
- return if (isEnabled) {
- sliderColors.inactiveTickColor
- } else {
- sliderColors.disabledInactiveTickColor
- }
- }
+ override fun isVisible(
+ placeableDimension: Int,
+ containerDimension: Int,
+ gapSize: Int,
+ coercedValueAsFraction: Float,
+ ): Boolean =
+ containerDimension - (containerDimension * coercedValueAsFraction + gapSize) >
+ placeableDimension
data object TrackStartIcon : Inactive {
- override fun Placeable.PlacementScope.performPlacing(
- placeable: Placeable,
- width: Int,
- height: Int,
- sliderState: SliderState,
- ) {
- placeable.place(x = 0, y = 0)
- }
- override fun isVisible(
- placeable: Placeable,
- width: Int,
- height: Int,
- sliderState: SliderState,
- ): Boolean =
- (height * (1 - sliderState.coercedValueAsFraction)).toInt() > placeable.height
+ override val mirrored: Contents
+ get() = Active.TrackEndIcon
+
+ override fun calculatePosition(
+ placeableDimension: Int,
+ containerDimension: Int,
+ gapSize: Int,
+ coercedValueAsFraction: Float,
+ ): Int = (containerDimension * coercedValueAsFraction + gapSize).toInt()
}
data object TrackEndIcon : Inactive {
- override fun Placeable.PlacementScope.performPlacing(
- placeable: Placeable,
- width: Int,
- height: Int,
- sliderState: SliderState,
- ) {
- placeable.place(
- x = 0,
- y =
- (height * (1 - sliderState.coercedValueAsFraction)).toInt() -
- placeable.height,
- )
- }
- override fun isVisible(
- placeable: Placeable,
- width: Int,
- height: Int,
- sliderState: SliderState,
- ): Boolean =
- (height * (1 - sliderState.coercedValueAsFraction)).toInt() > placeable.height
+ override val mirrored: Contents
+ get() = Active.TrackStartIcon
+
+ override fun calculatePosition(
+ placeableDimension: Int,
+ containerDimension: Int,
+ gapSize: Int,
+ coercedValueAsFraction: Float,
+ ): Int = containerDimension - placeableDimension
}
}
- fun Placeable.PlacementScope.performPlacing(
- placeable: Placeable,
- width: Int,
- height: Int,
- sliderState: SliderState,
- )
-
- fun isVisible(placeable: Placeable, width: Int, height: Int, sliderState: SliderState): Boolean
-
- fun getColor(sliderColors: SliderColors, isEnabled: Boolean): Color
+ fun calculatePosition(
+ placeableDimension: Int,
+ containerDimension: Int,
+ gapSize: Int,
+ coercedValueAsFraction: Float,
+ ): Int
+
+ fun isVisible(
+ placeableDimension: Int,
+ containerDimension: Int,
+ gapSize: Int,
+ coercedValueAsFraction: Float,
+ ): Boolean
+
+ /**
+ * [Contents] that is visually on the opposite side of the current one on the slider. This is
+ * handy when dealing with the rtl layouts
+ */
+ val mirrored: Contents
}
/** Provides visibility state for each of the Slider's icons. */
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderIconProvider.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderIconProvider.kt
index ef147c741bec..3712276488ff 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderIconProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderIconProvider.kt
@@ -18,32 +18,36 @@ package com.android.systemui.volume.dialog.sliders.ui.viewmodel
import android.annotation.SuppressLint
import android.content.Context
-import android.graphics.drawable.Drawable
import android.media.AudioManager
import androidx.annotation.DrawableRes
import com.android.settingslib.R as SettingsR
import com.android.settingslib.volume.domain.interactor.AudioVolumeInteractor
import com.android.settingslib.volume.shared.model.AudioStream
import com.android.settingslib.volume.shared.model.RingerMode
+import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.dagger.qualifiers.UiBackground
import com.android.systemui.res.R
import com.android.systemui.statusbar.policy.domain.interactor.ZenModeInteractor
import com.android.systemui.statusbar.policy.domain.model.ActiveZenModes
import javax.inject.Inject
+import kotlin.coroutines.CoroutineContext
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.withContext
@SuppressLint("UseCompatLoadingForDrawables")
class VolumeDialogSliderIconProvider
@Inject
constructor(
private val context: Context,
+ @UiBackground private val uiBackgroundContext: CoroutineContext,
private val zenModeInteractor: ZenModeInteractor,
private val audioVolumeInteractor: AudioVolumeInteractor,
) {
- fun getAudioSharingIcon(isMuted: Boolean): Flow<Drawable> {
+ fun getAudioSharingIcon(isMuted: Boolean): Flow<Icon.Loaded> {
return flow {
val iconRes =
if (isMuted) {
@@ -51,11 +55,12 @@ constructor(
} else {
R.drawable.ic_volume_media_bt
}
- emit(context.getDrawable(iconRes)!!)
+ val drawable = withContext(uiBackgroundContext) { context.getDrawable(iconRes)!! }
+ emit(Icon.Loaded(drawable = drawable, contentDescription = null, res = iconRes))
}
}
- fun getCastIcon(isMuted: Boolean): Flow<Drawable> {
+ fun getCastIcon(isMuted: Boolean): Flow<Icon.Loaded> {
return flow {
val iconRes =
if (isMuted) {
@@ -63,7 +68,8 @@ constructor(
} else {
SettingsR.drawable.ic_volume_remote
}
- emit(context.getDrawable(iconRes)!!)
+ val drawable = withContext(uiBackgroundContext) { context.getDrawable(iconRes)!! }
+ emit(Icon.Loaded(drawable = drawable, contentDescription = null, res = iconRes))
}
}
@@ -74,15 +80,18 @@ constructor(
levelMax: Int,
isMuted: Boolean,
isRoutedToBluetooth: Boolean,
- ): Flow<Drawable> {
+ ): Flow<Icon.Loaded> {
return combine(
zenModeInteractor.activeModesBlockingStream(stream),
ringerModeForStream(stream),
) { activeModesBlockingStream, ringerMode ->
if (activeModesBlockingStream?.mainMode?.icon != null) {
- return@combine activeModesBlockingStream.mainMode.icon.drawable
+ Icon.Loaded(
+ drawable = activeModesBlockingStream.mainMode.icon.drawable,
+ contentDescription = null,
+ )
} else {
- context.getDrawable(
+ val iconRes =
getIconRes(
stream,
level,
@@ -92,7 +101,8 @@ constructor(
isRoutedToBluetooth,
ringerMode,
)
- )!!
+ val drawable = withContext(uiBackgroundContext) { context.getDrawable(iconRes)!! }
+ Icon.Loaded(drawable = drawable, contentDescription = null, res = iconRes)
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderStateModel.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderStateModel.kt
index 88a061f3813c..ed59598d97d0 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderStateModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderStateModel.kt
@@ -17,7 +17,7 @@
package com.android.systemui.volume.dialog.sliders.ui.viewmodel
import android.content.Context
-import android.graphics.drawable.Drawable
+import com.android.systemui.common.shared.model.Icon
import com.android.systemui.volume.dialog.shared.model.VolumeDialogStreamModel
import com.android.systemui.volume.dialog.shared.model.streamLabel
@@ -25,14 +25,14 @@ data class VolumeDialogSliderStateModel(
val value: Float,
val isDisabled: Boolean,
val valueRange: ClosedFloatingPointRange<Float>,
- val icon: Drawable,
+ val icon: Icon.Loaded,
val label: String,
)
fun VolumeDialogStreamModel.toStateModel(
context: Context,
isDisabled: Boolean,
- icon: Drawable,
+ icon: Icon.Loaded,
): VolumeDialogSliderStateModel {
return VolumeDialogSliderStateModel(
value = level.toFloat(),
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderViewModel.kt
index f6aa189eb571..faf0abd4cabd 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderViewModel.kt
@@ -108,11 +108,9 @@ constructor(
isMuted = isMuted,
isRoutedToBluetooth = routedToBluetooth,
)
-
is VolumeDialogSliderType.RemoteMediaStream -> {
volumeDialogSliderIconProvider.getCastIcon(isMuted)
}
-
is VolumeDialogSliderType.AudioSharingStream -> {
volumeDialogSliderIconProvider.getAudioSharingIcon(isMuted)
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/viewmodel/VolumeDialogPluginViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/viewmodel/VolumeDialogPluginViewModel.kt
index 6d16300bf56e..87cc1830af28 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/viewmodel/VolumeDialogPluginViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/viewmodel/VolumeDialogPluginViewModel.kt
@@ -70,10 +70,18 @@ constructor(
val isShowingSafetyWarning: Flow<Boolean> = dialogSafetyWarningInteractor.isShowingSafetyWarning
val csdWarning: Flow<Int?> = dialogCsdWarningInteractor.csdWarning
+ fun onSafetyWarningDialogShown() {
+ dialogVisibilityInteractor.resetDismissTimeout()
+ }
+
fun onSafetyWarningDismissed() {
dialogSafetyWarningInteractor.onSafetyWarningDismissed()
}
+ fun onCsdWarningDialogShown() {
+ dialogVisibilityInteractor.resetDismissTimeout()
+ }
+
fun onCsdWarningDismissed() {
dialogCsdWarningInteractor.onCsdWarningDismissed()
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioSharingStreamSliderViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioSharingStreamSliderViewModel.kt
index 3d98ebacc7ca..f6452679cbf4 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioSharingStreamSliderViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioSharingStreamSliderViewModel.kt
@@ -16,9 +16,11 @@
package com.android.systemui.volume.panel.component.volume.slider.ui.viewmodel
+import android.content.Context
import com.android.internal.logging.UiEventLogger
import com.android.systemui.Flags
import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.dagger.qualifiers.UiBackground
import com.android.systemui.haptics.slider.SliderHapticFeedbackFilter
import com.android.systemui.haptics.slider.compose.ui.SliderHapticsViewModel
import com.android.systemui.res.R
@@ -28,6 +30,7 @@ import com.android.systemui.volume.panel.ui.VolumePanelUiEvent
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
+import kotlin.coroutines.CoroutineContext
import kotlin.math.roundToInt
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.MutableStateFlow
@@ -39,11 +42,14 @@ import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.stateIn
+import kotlinx.coroutines.withContext
class AudioSharingStreamSliderViewModel
@AssistedInject
constructor(
+ private val context: Context,
@Assisted private val coroutineScope: CoroutineScope,
+ @UiBackground private val uiBackgroundContext: CoroutineContext,
private val audioSharingInteractor: AudioSharingInteractor,
private val uiEventLogger: UiEventLogger,
private val hapticsViewModelFactory: SliderHapticsViewModel.Factory,
@@ -51,6 +57,12 @@ constructor(
) : SliderViewModel {
private val volumeChanges = MutableStateFlow<Int?>(null)
+ private val audioSharingIcon =
+ Icon.Loaded(
+ drawable = context.getDrawable(R.drawable.ic_volume_media_bt)!!,
+ contentDescription = null,
+ res = R.drawable.ic_volume_media_bt,
+ )
override val slider: StateFlow<SliderState> =
combine(
audioSharingInteractor.volume.distinctUntilChanged().onEach {
@@ -62,16 +74,17 @@ constructor(
if (volume == null) {
SliderState.Empty
} else {
-
- State(
- value = volume.toFloat(),
- valueRange =
- audioSharingInteractor.volumeMin.toFloat()..audioSharingInteractor
- .volumeMax
- .toFloat(),
- icon = Icon.Resource(R.drawable.ic_volume_media_bt, null),
- label = deviceName,
- )
+ withContext(uiBackgroundContext) {
+ State(
+ value = volume.toFloat(),
+ valueRange =
+ audioSharingInteractor.volumeMin.toFloat()..audioSharingInteractor
+ .volumeMax
+ .toFloat(),
+ icon = audioSharingIcon,
+ label = deviceName,
+ )
+ }
}
}
.stateIn(coroutineScope, SharingStarted.Eagerly, SliderState.Empty)
@@ -107,7 +120,7 @@ constructor(
private data class State(
override val value: Float,
override val valueRange: ClosedFloatingPointRange<Float>,
- override val icon: Icon,
+ override val icon: Icon.Loaded?,
override val label: String,
) : SliderState {
override val hapticFilter: SliderHapticFeedbackFilter
@@ -116,7 +129,7 @@ constructor(
override val isEnabled: Boolean
get() = true
- override val a11yStep: Float
+ override val step: Float
get() = 1f
override val disabledMessage: String?
@@ -125,6 +138,9 @@ constructor(
override val isMutable: Boolean
get() = false
+ override val a11yContentDescription: String
+ get() = label
+
override val a11yClickDescription: String?
get() = null
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioStreamSliderViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioStreamSliderViewModel.kt
index 9d32285fecb3..9fe0ad42cdba 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioStreamSliderViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioStreamSliderViewModel.kt
@@ -19,6 +19,7 @@ package com.android.systemui.volume.panel.component.volume.slider.ui.viewmodel
import android.content.Context
import android.media.AudioManager
import android.util.Log
+import androidx.annotation.DrawableRes
import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.internal.logging.UiEventLogger
import com.android.settingslib.bluetooth.CachedBluetoothDevice
@@ -28,6 +29,7 @@ import com.android.settingslib.volume.shared.model.AudioStreamModel
import com.android.settingslib.volume.shared.model.RingerMode
import com.android.systemui.Flags
import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.dagger.qualifiers.UiBackground
import com.android.systemui.haptics.slider.SliderHapticFeedbackFilter
import com.android.systemui.haptics.slider.compose.ui.SliderHapticsViewModel
import com.android.systemui.modes.shared.ModesUiIcons
@@ -40,18 +42,21 @@ import com.android.systemui.volume.panel.ui.VolumePanelUiEvent
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
+import kotlin.coroutines.CoroutineContext
import kotlin.math.roundToInt
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.stateIn
+import kotlinx.coroutines.withContext
/** Models a particular slider state. */
class AudioStreamSliderViewModel
@@ -59,10 +64,11 @@ class AudioStreamSliderViewModel
constructor(
@Assisted private val audioStreamWrapper: FactoryAudioStreamWrapper,
@Assisted private val coroutineScope: CoroutineScope,
+ @UiBackground private val uiBackgroundContext: CoroutineContext,
private val context: Context,
private val audioVolumeInteractor: AudioVolumeInteractor,
private val zenModeInteractor: ZenModeInteractor,
- private val audioSharingInteractor: AudioSharingInteractor,
+ audioSharingInteractor: AudioSharingInteractor,
private val uiEventLogger: UiEventLogger,
private val volumePanelLogger: VolumePanelLogger,
private val hapticsViewModelFactory: SliderHapticsViewModel.Factory,
@@ -148,57 +154,69 @@ constructor(
null
}
- private fun AudioStreamModel.toState(
+ private suspend fun AudioStreamModel.toState(
isEnabled: Boolean,
ringerMode: RingerMode,
disabledMessage: String?,
inAudioSharing: Boolean,
primaryDevice: CachedBluetoothDevice?,
- ): State {
- val label = getLabel(inAudioSharing, primaryDevice)
- val icon = getIcon(ringerMode, inAudioSharing)
- return State(
- value = volume.toFloat(),
- valueRange = volumeRange.first.toFloat()..volumeRange.last.toFloat(),
- hapticFilter = createHapticFilter(ringerMode),
- icon = icon,
- label = label,
- disabledMessage = disabledMessage,
- isEnabled = isEnabled,
- a11yStep = volumeRange.step.toFloat(),
- a11yClickDescription =
- if (isAffectedByMute) {
- context.getString(
- if (isMuted) {
- R.string.volume_panel_hint_unmute
- } else {
- R.string.volume_panel_hint_mute
- },
- label,
- )
- } else {
- null
- },
- a11yStateDescription =
- if (isMuted) {
- context.getString(
- if (isAffectedByRingerMode) {
- if (ringerMode.value == AudioManager.RINGER_MODE_VIBRATE) {
- R.string.volume_panel_hint_vibrate
+ ): State =
+ withContext(uiBackgroundContext) {
+ val label = getLabel(inAudioSharing, primaryDevice)
+ State(
+ value = volume.toFloat(),
+ valueRange = volumeRange.first.toFloat()..volumeRange.last.toFloat(),
+ hapticFilter = createHapticFilter(ringerMode),
+ icon = getIcon(ringerMode, inAudioSharing),
+ label = label,
+ disabledMessage = disabledMessage,
+ isEnabled = isEnabled,
+ step = volumeRange.step.toFloat(),
+ a11yContentDescription =
+ if (isEnabled) {
+ label
+ } else {
+ disabledMessage?.let {
+ context.getString(
+ R.string.volume_slider_disabled_message_template,
+ label,
+ disabledMessage,
+ )
+ } ?: label
+ },
+ a11yClickDescription =
+ if (isAffectedByMute) {
+ context.getString(
+ if (isMuted) {
+ R.string.volume_panel_hint_unmute
+ } else {
+ R.string.volume_panel_hint_mute
+ },
+ label,
+ )
+ } else {
+ null
+ },
+ a11yStateDescription =
+ if (isMuted) {
+ context.getString(
+ if (isAffectedByRingerMode) {
+ if (ringerMode.value == AudioManager.RINGER_MODE_VIBRATE) {
+ R.string.volume_panel_hint_vibrate
+ } else {
+ R.string.volume_panel_hint_muted
+ }
} else {
R.string.volume_panel_hint_muted
}
- } else {
- R.string.volume_panel_hint_muted
- }
- )
- } else {
- null
- },
- audioStreamModel = this,
- isMutable = isAffectedByMute,
- )
- }
+ )
+ } else {
+ null
+ },
+ audioStreamModel = this@toState,
+ isMutable = isAffectedByMute,
+ )
+ }
private fun AudioStreamModel.createHapticFilter(
ringerMode: RingerMode
@@ -220,12 +238,14 @@ constructor(
flowOf(context.getString(R.string.stream_notification_unavailable))
} else {
if (zenModeInteractor.canBeBlockedByZenMode(audioStream)) {
- zenModeInteractor.activeModesBlockingStream(audioStream).map { blockingZenModes
- ->
- blockingZenModes.mainMode?.name?.let {
- context.getString(R.string.stream_unavailable_by_modes, it)
- } ?: context.getString(R.string.stream_unavailable_by_unknown)
- }
+ zenModeInteractor
+ .activeModesBlockingStream(audioStream)
+ .map { blockingZenModes ->
+ blockingZenModes.mainMode?.name?.let {
+ context.getString(R.string.stream_unavailable_by_modes, it)
+ } ?: context.getString(R.string.stream_unavailable_by_unknown)
+ }
+ .distinctUntilChanged()
} else {
flowOf(context.getString(R.string.stream_unavailable_by_unknown))
}
@@ -256,8 +276,11 @@ constructor(
?: error("No label for the stream: $audioStream")
}
- private fun AudioStreamModel.getIcon(ringerMode: RingerMode, inAudioSharing: Boolean): Icon {
- val iconRes =
+ private fun AudioStreamModel.getIcon(
+ ringerMode: RingerMode,
+ inAudioSharing: Boolean,
+ ): Icon.Loaded {
+ val iconResource: Int =
if (isMuted) {
if (isAffectedByRingerMode) {
if (ringerMode.value == AudioManager.RINGER_MODE_VIBRATE) {
@@ -272,14 +295,21 @@ constructor(
inAudioSharing
) {
R.drawable.ic_volume_media_bt_mute
- } else R.drawable.ic_volume_off
+ } else {
+ R.drawable.ic_volume_off
+ }
}
} else {
getIconByStream(audioStream, inAudioSharing)
}
- return Icon.Resource(iconRes, null)
+ return Icon.Loaded(
+ drawable = context.getDrawable(iconResource)!!,
+ contentDescription = null,
+ res = iconResource,
+ )
}
+ @DrawableRes
private fun getIconByStream(audioStream: AudioStream, inAudioSharing: Boolean): Int =
when (audioStream.value) {
AudioManager.STREAM_MUSIC ->
@@ -302,14 +332,15 @@ constructor(
private data class State(
override val value: Float,
override val valueRange: ClosedFloatingPointRange<Float>,
+ override val step: Float,
override val hapticFilter: SliderHapticFeedbackFilter,
- override val icon: Icon,
+ override val icon: Icon.Loaded?,
override val label: String,
override val disabledMessage: String?,
override val isEnabled: Boolean,
- override val a11yStep: Float,
override val a11yClickDescription: String?,
override val a11yStateDescription: String?,
+ override val a11yContentDescription: String,
override val isMutable: Boolean,
val audioStreamModel: AudioStreamModel,
) : SliderState
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/CastVolumeSliderViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/CastVolumeSliderViewModel.kt
index a6c809186ca5..01810f9aafc3 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/CastVolumeSliderViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/CastVolumeSliderViewModel.kt
@@ -21,6 +21,7 @@ import android.media.session.MediaController.PlaybackInfo
import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.systemui.Flags
import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.dagger.qualifiers.UiBackground
import com.android.systemui.haptics.slider.SliderHapticFeedbackFilter
import com.android.systemui.haptics.slider.compose.ui.SliderHapticsViewModel
import com.android.systemui.res.R
@@ -30,30 +31,40 @@ import com.android.systemui.volume.panel.shared.VolumePanelLogger
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
+import kotlin.coroutines.CoroutineContext
import kotlin.math.roundToInt
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.mapNotNull
import kotlinx.coroutines.flow.stateIn
+import kotlinx.coroutines.withContext
class CastVolumeSliderViewModel
@AssistedInject
constructor(
@Assisted private val session: MediaDeviceSession,
@Assisted private val coroutineScope: CoroutineScope,
+ @UiBackground private val uiBackgroundContext: CoroutineContext,
private val context: Context,
private val mediaDeviceSessionInteractor: MediaDeviceSessionInteractor,
private val hapticsViewModelFactory: SliderHapticsViewModel.Factory,
private val volumePanelLogger: VolumePanelLogger,
) : SliderViewModel {
+ private val castLabel = context.getString(R.string.media_device_cast)
+ private val castIcon =
+ Icon.Loaded(
+ drawable = context.getDrawable(R.drawable.ic_cast)!!,
+ contentDescription = null,
+ res = R.drawable.ic_cast,
+ )
override val slider: StateFlow<SliderState> =
mediaDeviceSessionInteractor
.playbackInfo(session)
.mapNotNull {
volumePanelLogger.onVolumeUpdateReceived(session.sessionToken, it.currentVolume)
- it.getCurrentState()
+ withContext(uiBackgroundContext) { it.getCurrentState() }
}
.stateIn(coroutineScope, SharingStarted.Eagerly, SliderState.Empty)
@@ -83,20 +94,20 @@ constructor(
return State(
value = currentVolume.toFloat(),
valueRange = volumeRange.first.toFloat()..volumeRange.last.toFloat(),
- icon = Icon.Resource(R.drawable.ic_cast, null),
- label = context.getString(R.string.media_device_cast),
+ icon = castIcon,
+ label = castLabel,
isEnabled = true,
- a11yStep = 1f,
+ step = 1f,
)
}
private data class State(
override val value: Float,
override val valueRange: ClosedFloatingPointRange<Float>,
- override val icon: Icon,
+ override val icon: Icon.Loaded?,
override val label: String,
override val isEnabled: Boolean,
- override val a11yStep: Float,
+ override val step: Float,
) : SliderState {
override val hapticFilter: SliderHapticFeedbackFilter
get() = SliderHapticFeedbackFilter()
@@ -107,6 +118,9 @@ constructor(
override val isMutable: Boolean
get() = false
+ override val a11yContentDescription: String
+ get() = label
+
override val a11yClickDescription: String?
get() = null
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/SliderState.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/SliderState.kt
index 4bc237bd36f5..b1d183404a9f 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/SliderState.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/SliderState.kt
@@ -27,18 +27,17 @@ import com.android.systemui.haptics.slider.SliderHapticFeedbackFilter
sealed interface SliderState {
val value: Float
val valueRange: ClosedFloatingPointRange<Float>
+ val step: Float
val hapticFilter: SliderHapticFeedbackFilter
- val icon: Icon?
+ // Force preloaded icon
+ val icon: Icon.Loaded?
val isEnabled: Boolean
val label: String
- /**
- * A11y slider controls works by adjusting one step up or down. The default slider step isn't
- * enough to trigger rounding to the correct value.
- */
- val a11yStep: Float
+
val a11yClickDescription: String?
val a11yStateDescription: String?
+ val a11yContentDescription: String
val disabledMessage: String?
val isMutable: Boolean
@@ -46,12 +45,13 @@ sealed interface SliderState {
override val value: Float = 0f
override val valueRange: ClosedFloatingPointRange<Float> = 0f..1f
override val hapticFilter = SliderHapticFeedbackFilter()
- override val icon: Icon? = null
+ override val icon: Icon.Loaded? = null
override val label: String = ""
override val disabledMessage: String? = null
- override val a11yStep: Float = 0f
+ override val step: Float = 0f
override val a11yClickDescription: String? = null
override val a11yStateDescription: String? = null
+ override val a11yContentDescription: String = label
override val isEnabled: Boolean = true
override val isMutable: Boolean = false
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/ui/slider/Slider.kt b/packages/SystemUI/src/com/android/systemui/volume/ui/compose/slider/Slider.kt
index f6582a005035..54d2f79509c3 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/ui/slider/Slider.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/ui/compose/slider/Slider.kt
@@ -16,7 +16,7 @@
@file:OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterial3ExpressiveApi::class)
-package com.android.systemui.volume.ui.slider
+package com.android.systemui.volume.ui.compose.slider
import androidx.compose.animation.core.Animatable
import androidx.compose.animation.core.Spring
@@ -40,7 +40,6 @@ import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.runtime.snapshotFlow
import androidx.compose.ui.Modifier
-import androidx.compose.ui.res.stringResource
import androidx.compose.ui.semantics.ProgressBarRangeInfo
import androidx.compose.ui.semantics.SemanticsPropertyReceiver
import androidx.compose.ui.semantics.clearAndSetSemantics
@@ -52,7 +51,6 @@ import androidx.compose.ui.semantics.stateDescription
import com.android.systemui.haptics.slider.SliderHapticFeedbackFilter
import com.android.systemui.haptics.slider.compose.ui.SliderHapticsViewModel
import com.android.systemui.lifecycle.rememberViewModel
-import com.android.systemui.res.R
import com.android.systemui.volume.haptics.ui.VolumeHapticsConfigsProvider
import kotlin.math.round
import kotlinx.coroutines.Job
@@ -63,8 +61,6 @@ import kotlinx.coroutines.launch
private val defaultSpring =
SpringSpec<Float>(dampingRatio = Spring.DampingRatioNoBouncy, stiffness = Spring.StiffnessHigh)
-private val defaultTrack: @Composable (SliderState) -> Unit =
- @Composable { SliderDefaults.Track(it) }
@Composable
fun Slider(
@@ -81,7 +77,14 @@ fun Slider(
haptics: Haptics = Haptics.Disabled,
isVertical: Boolean = false,
isReverseDirection: Boolean = false,
- track: (@Composable (SliderState) -> Unit)? = null,
+ track: (@Composable (SliderState) -> Unit) = { SliderDefaults.Track(it) },
+ thumb: (@Composable (SliderState, MutableInteractionSource) -> Unit) = { _, _ ->
+ SliderDefaults.Thumb(
+ interactionSource = interactionSource,
+ colors = colors,
+ enabled = isEnabled,
+ )
+ },
) {
require(stepDistance >= 0) { "stepDistance must not be negative" }
val coroutineScope = rememberCoroutineScope()
@@ -108,7 +111,8 @@ fun Slider(
}
}
val semantics =
- accessibilityParams.createSemantics(
+ createSemantics(
+ accessibilityParams,
animatable.targetValue,
valueRange,
valueChange,
@@ -140,7 +144,8 @@ fun Slider(
reverseDirection = isReverseDirection,
interactionSource = interactionSource,
colors = colors,
- track = track ?: defaultTrack,
+ track = track,
+ thumb = { thumb(it, interactionSource) },
modifier = modifier.clearAndSetSemantics(semantics),
)
} else {
@@ -149,7 +154,8 @@ fun Slider(
enabled = isEnabled,
interactionSource = interactionSource,
colors = colors,
- track = track ?: defaultTrack,
+ track = track,
+ thumb = { thumb(it, interactionSource) },
modifier = modifier.clearAndSetSemantics(semantics),
)
}
@@ -167,24 +173,18 @@ private fun snapValue(
return Math.round(coercedValue / stepDistance) * stepDistance
}
-@Composable
-private fun AccessibilityParams.createSemantics(
+private fun createSemantics(
+ params: AccessibilityParams,
value: Float,
valueRange: ClosedFloatingPointRange<Float>,
onValueChanged: (Float) -> Unit,
isEnabled: Boolean,
stepDistance: Float,
): SemanticsPropertyReceiver.() -> Unit {
- val semanticsContentDescription =
- disabledMessage
- ?.takeIf { !isEnabled }
- ?.let { message ->
- stringResource(R.string.volume_slider_disabled_message_template, label, message)
- } ?: label
return {
- contentDescription = semanticsContentDescription
+ contentDescription = params.contentDescription
if (isEnabled) {
- currentStateDescription?.let { stateDescription = it }
+ params.stateDescription?.let { stateDescription = it }
progressBarRangeInfo = ProgressBarRangeInfo(value, valueRange)
} else {
disabled()
@@ -253,9 +253,8 @@ private fun Haptics.createViewModel(
}
data class AccessibilityParams(
- val label: String,
- val currentStateDescription: String? = null,
- val disabledMessage: String? = null,
+ val contentDescription: String,
+ val stateDescription: String? = null,
)
sealed interface Haptics {
diff --git a/packages/SystemUI/src/com/android/systemui/volume/ui/compose/slider/SliderIcon.kt b/packages/SystemUI/src/com/android/systemui/volume/ui/compose/slider/SliderIcon.kt
new file mode 100644
index 000000000000..fd8f47794fc0
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/ui/compose/slider/SliderIcon.kt
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2025 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.volume.ui.compose.slider
+
+import androidx.compose.animation.AnimatedVisibility
+import androidx.compose.animation.core.tween
+import androidx.compose.animation.fadeIn
+import androidx.compose.animation.fadeOut
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.BoxScope
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+
+@Composable
+fun SliderIcon(
+ icon: @Composable BoxScope.() -> Unit,
+ isVisible: Boolean,
+ modifier: Modifier = Modifier,
+) {
+ Box(contentAlignment = Alignment.Center, modifier = modifier.fillMaxSize()) {
+ AnimatedVisibility(
+ visible = isVisible,
+ enter = fadeIn(animationSpec = tween(delayMillis = 33, durationMillis = 100)),
+ exit = fadeOut(animationSpec = tween(durationMillis = 50)),
+ ) {
+ icon()
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/wallet/dagger/WalletModule.java b/packages/SystemUI/src/com/android/systemui/wallet/dagger/WalletModule.java
index dd1c11d11d1e..e9180df0806b 100644
--- a/packages/SystemUI/src/com/android/systemui/wallet/dagger/WalletModule.java
+++ b/packages/SystemUI/src/com/android/systemui/wallet/dagger/WalletModule.java
@@ -28,9 +28,9 @@ import com.android.systemui.qs.pipeline.shared.TileSpec;
import com.android.systemui.qs.shared.model.TileCategory;
import com.android.systemui.qs.tileimpl.QSTileImpl;
import com.android.systemui.qs.tiles.QuickAccessWalletTile;
-import com.android.systemui.qs.tiles.viewmodel.QSTileConfig;
-import com.android.systemui.qs.tiles.viewmodel.QSTilePolicy;
-import com.android.systemui.qs.tiles.viewmodel.QSTileUIConfig;
+import com.android.systemui.qs.tiles.base.shared.model.QSTileConfig;
+import com.android.systemui.qs.tiles.base.shared.model.QSTilePolicy;
+import com.android.systemui.qs.tiles.base.shared.model.QSTileUIConfig;
import com.android.systemui.res.R;
import com.android.systemui.wallet.controller.WalletContextualLocationsService;
import com.android.systemui.wallet.ui.WalletActivity;
diff --git a/packages/SystemUI/src/com/android/systemui/wallpapers/ImageWallpaper.java b/packages/SystemUI/src/com/android/systemui/wallpapers/ImageWallpaper.java
index b1c6455a6e57..107f670eae8d 100644
--- a/packages/SystemUI/src/com/android/systemui/wallpapers/ImageWallpaper.java
+++ b/packages/SystemUI/src/com/android/systemui/wallpapers/ImageWallpaper.java
@@ -42,7 +42,6 @@ import android.service.wallpaper.WallpaperService;
import android.util.Log;
import android.view.Surface;
import android.view.SurfaceHolder;
-import android.view.WindowManager;
import androidx.annotation.NonNull;
@@ -50,6 +49,7 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.dagger.qualifiers.LongRunning;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.util.concurrency.DelayableExecutor;
+import com.android.systemui.utils.windowmanager.WindowManagerProvider;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -71,6 +71,7 @@ public class ImageWallpaper extends WallpaperService {
private boolean mPagesComputed = false;
private final UserTracker mUserTracker;
+ private final WindowManagerProvider mWindowManagerProvider;
// used to handle WallpaperService messages (e.g. DO_ATTACH, MSG_UPDATE_SURFACE)
// and to receive WallpaperService callbacks (e.g. onCreateEngine, onSurfaceRedrawNeeded)
@@ -84,10 +85,12 @@ public class ImageWallpaper extends WallpaperService {
private static final int DELAY_UNLOAD_BITMAP = 2000;
@Inject
- public ImageWallpaper(@LongRunning DelayableExecutor longExecutor, UserTracker userTracker) {
+ public ImageWallpaper(@LongRunning DelayableExecutor longExecutor, UserTracker userTracker,
+ WindowManagerProvider windowManagerProvider) {
super();
mLongExecutor = longExecutor;
mUserTracker = userTracker;
+ mWindowManagerProvider = windowManagerProvider;
}
@Override
@@ -552,8 +555,7 @@ public class ImageWallpaper extends WallpaperService {
}
private void getDisplaySizeAndUpdateColorExtractor() {
- Rect window = getDisplayContext()
- .getSystemService(WindowManager.class)
+ Rect window = mWindowManagerProvider.getWindowManager(getDisplayContext())
.getCurrentWindowMetrics()
.getBounds();
mWallpaperLocalColorExtractor.setDisplayDimensions(window.width(), window.height());
diff --git a/packages/SystemUI/src/com/android/systemui/window/data/repository/NoopWindowRootViewBlurRepository.kt b/packages/SystemUI/src/com/android/systemui/window/data/repository/NoopWindowRootViewBlurRepository.kt
index f1dd374d3e1d..1ae8bc9405dc 100644
--- a/packages/SystemUI/src/com/android/systemui/window/data/repository/NoopWindowRootViewBlurRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/window/data/repository/NoopWindowRootViewBlurRepository.kt
@@ -16,13 +16,12 @@
package com.android.systemui.window.data.repository
-import com.android.systemui.window.data.repository.WindowRootViewBlurRepository
import javax.inject.Inject
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
class NoopWindowRootViewBlurRepository @Inject constructor() : WindowRootViewBlurRepository {
- override val blurRadius: MutableStateFlow<Int> = MutableStateFlow(0)
+ override val blurRequestedByShade: MutableStateFlow<Int> = MutableStateFlow(0)
override val isBlurOpaque: MutableStateFlow<Boolean> = MutableStateFlow(true)
override val isBlurSupported: StateFlow<Boolean> = MutableStateFlow(false)
override var blurAppliedListener: BlurAppliedListener? = null
diff --git a/packages/SystemUI/src/com/android/systemui/window/data/repository/WindowRootViewBlurRepository.kt b/packages/SystemUI/src/com/android/systemui/window/data/repository/WindowRootViewBlurRepository.kt
index f98efe1e3c33..9b934bc6c866 100644
--- a/packages/SystemUI/src/com/android/systemui/window/data/repository/WindowRootViewBlurRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/window/data/repository/WindowRootViewBlurRepository.kt
@@ -39,7 +39,7 @@ typealias BlurAppliedListener = Consumer<Int>
/** Repository that maintains state for the window blur effect. */
interface WindowRootViewBlurRepository {
- val blurRadius: MutableStateFlow<Int>
+ val blurRequestedByShade: MutableStateFlow<Int>
val isBlurOpaque: MutableStateFlow<Boolean>
/** Is blur supported based on settings toggle and battery power saver mode. */
@@ -67,7 +67,7 @@ constructor(
@Main private val executor: Executor,
@Application private val scope: CoroutineScope,
) : WindowRootViewBlurRepository {
- override val blurRadius = MutableStateFlow(0)
+ override val blurRequestedByShade = MutableStateFlow(0)
override val isBlurOpaque = MutableStateFlow(false)
diff --git a/packages/SystemUI/src/com/android/systemui/window/domain/interactor/WindowRootViewBlurInteractor.kt b/packages/SystemUI/src/com/android/systemui/window/domain/interactor/WindowRootViewBlurInteractor.kt
index 5197242e8079..2994138f8181 100644
--- a/packages/SystemUI/src/com/android/systemui/window/domain/interactor/WindowRootViewBlurInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/window/domain/interactor/WindowRootViewBlurInteractor.kt
@@ -87,7 +87,7 @@ constructor(
val isBlurCurrentlySupported: StateFlow<Boolean> = repository.isBlurSupported
/** Radius of blur to be applied on the window root view. */
- val blurRadius: StateFlow<Int> = repository.blurRadius.asStateFlow()
+ val blurRadiusRequestedByShade: StateFlow<Int> = repository.blurRequestedByShade.asStateFlow()
/** Whether the blur applied is opaque or transparent. */
val isBlurOpaque: Flow<Boolean> =
@@ -128,7 +128,7 @@ constructor(
return false
}
Log.d(TAG, "requestingBlurForShade for $blurRadius $opaque")
- repository.blurRadius.value = blurRadius
+ repository.blurRequestedByShade.value = blurRadius
repository.isBlurOpaque.value = opaque
return true
}
diff --git a/packages/SystemUI/src/com/android/systemui/window/ui/WindowRootViewBinder.kt b/packages/SystemUI/src/com/android/systemui/window/ui/WindowRootViewBinder.kt
index 06532bc0cc2a..fd642f99ef54 100644
--- a/packages/SystemUI/src/com/android/systemui/window/ui/WindowRootViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/window/ui/WindowRootViewBinder.kt
@@ -59,6 +59,15 @@ object WindowRootViewBinder {
) { viewModel ->
try {
Log.d(TAG, "Launching coroutines that update window root view state")
+ launchTraced("early-wakeup") {
+ viewModel.isPersistentEarlyWakeupRequired.collect { wakeupRequired ->
+ blurUtils.setPersistentEarlyWakeup(
+ wakeupRequired,
+ view.rootView?.viewRootImpl,
+ )
+ }
+ }
+
launchTraced("WindowBlur") {
var wasUpdateScheduledForThisFrame = false
var lastScheduledBlurRadius = 0
diff --git a/packages/SystemUI/src/com/android/systemui/window/ui/viewmodel/WindowRootViewModel.kt b/packages/SystemUI/src/com/android/systemui/window/ui/viewmodel/WindowRootViewModel.kt
index e60e85335f7a..89101c961031 100644
--- a/packages/SystemUI/src/com/android/systemui/window/ui/viewmodel/WindowRootViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/window/ui/viewmodel/WindowRootViewModel.kt
@@ -19,13 +19,16 @@ package com.android.systemui.window.ui.viewmodel
import android.os.Build
import android.util.Log
import com.android.systemui.Flags
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.keyguard.ui.transitions.GlanceableHubTransition
import com.android.systemui.keyguard.ui.transitions.PrimaryBouncerTransition
+import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.window.domain.interactor.WindowRootViewBlurInteractor
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOf
@@ -41,6 +44,8 @@ constructor(
primaryBouncerTransitions: Set<@JvmSuppressWildcards PrimaryBouncerTransition>,
glanceableHubTransitions: Set<@JvmSuppressWildcards GlanceableHubTransition>,
private val blurInteractor: WindowRootViewBlurInteractor,
+ private val keyguardInteractor: KeyguardInteractor,
+ private val shadeInteractor: ShadeInteractor,
) {
private val bouncerBlurRadiusFlows =
@@ -57,7 +62,9 @@ constructor(
listOf(
*bouncerBlurRadiusFlows.toTypedArray(),
*glanceableHubBlurRadiusFlows.toTypedArray(),
- blurInteractor.blurRadius.map { it.toFloat() }.logIfPossible("ShadeBlur"),
+ blurInteractor.blurRadiusRequestedByShade
+ .map { it.toFloat() }
+ .logIfPossible("ShadeBlur"),
)
.merge()
@@ -70,6 +77,24 @@ constructor(
}
}
+ val isPersistentEarlyWakeupRequired =
+ blurInteractor.isBlurCurrentlySupported
+ .flatMapLatest { blurSupported ->
+ if (blurSupported) {
+ combine(
+ keyguardInteractor.isKeyguardShowing,
+ shadeInteractor.isUserInteracting,
+ shadeInteractor.isAnyExpanded,
+ ) { keyguardShowing, userDraggingShade, anyExpanded ->
+ keyguardShowing || userDraggingShade || anyExpanded
+ }
+ } else {
+ flowOf(false)
+ }
+ }
+ .distinctUntilChanged()
+ .logIfPossible("isPersistentEarlyWakeupRequired")
+
val isBlurOpaque =
blurInteractor.isBlurCurrentlySupported.flatMapLatest { blurSupported ->
if (blurSupported) {
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java b/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java
index 8a447f7395d4..60f1777e80bc 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java
@@ -64,6 +64,7 @@ import com.android.systemui.statusbar.NotificationLockscreenUserManager;
import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.notification.NotifPipelineFlags;
import com.android.systemui.statusbar.notification.NotificationChannelHelper;
+import com.android.systemui.statusbar.notification.collection.EntryAdapter;
import com.android.systemui.statusbar.notification.collection.NotifCollection;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
@@ -618,6 +619,28 @@ public class BubblesManager {
}
/**
+ * When a notification is set as important, make it a bubble
+ *
+ * @param entryAdapter the important notification.
+ */
+ public void onUserSetImportantConversation(EntryAdapter entryAdapter) {
+ if (entryAdapter.getSbn() != null
+ && entryAdapter.getSbn().getNotification().getBubbleMetadata() == null) {
+ // No bubble metadata, nothing to do.
+ return;
+ }
+ try {
+ int flags = Notification.BubbleMetadata.FLAG_SUPPRESS_NOTIFICATION;
+ mBarService.onNotificationBubbleChanged(entryAdapter.getKey(), true, flags);
+ } catch (RemoteException e) {
+ Log.e(TAG, e.getMessage());
+ }
+ mShadeController.collapseShade(true);
+ // NotificationGutsManager will refresh the ENR when the guts close and update the
+ // bubble button if needed
+ }
+
+ /**
* Called when a user has indicated that an active notification should be shown as a bubble.
* <p>
* This method will collapse the shade, create the bubble without a flyout or dot, and suppress
diff --git a/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
index 0769ada805a2..645e306eed07 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
@@ -80,8 +80,6 @@ import androidx.annotation.Nullable;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
-import com.android.app.viewcapture.ViewCapture;
-import com.android.app.viewcapture.ViewCaptureAwareWindowManager;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.biometrics.AuthController;
import com.android.systemui.biometrics.data.repository.FakeFacePropertyRepository;
@@ -104,7 +102,6 @@ import com.android.systemui.settings.FakeDisplayTracker;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.commandline.CommandRegistry;
import com.android.systemui.statusbar.events.PrivacyDotViewController;
-import com.android.systemui.util.concurrency.DelayableExecutor;
import com.android.systemui.util.concurrency.FakeExecutor;
import com.android.systemui.util.concurrency.FakeThreadFactory;
import com.android.systemui.util.kotlin.JavaAdapter;
@@ -112,8 +109,6 @@ import com.android.systemui.util.settings.FakeSettings;
import com.android.systemui.util.settings.SecureSettings;
import com.android.systemui.util.time.FakeSystemClock;
-import kotlin.Lazy;
-
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -132,7 +127,6 @@ public class ScreenDecorationsTest extends SysuiTestCase {
private ScreenDecorations mScreenDecorations;
private WindowManager mWindowManager;
- private ViewCaptureAwareWindowManager mViewCaptureAwareWindowManager;
private DisplayManager mDisplayManager;
private SecureSettings mSecureSettings;
private FakeExecutor mExecutor;
@@ -179,8 +173,6 @@ public class ScreenDecorationsTest extends SysuiTestCase {
private CutoutDecorProviderFactory mCutoutFactory;
@Mock
private JavaAdapter mJavaAdapter;
- @Mock
- private Lazy<ViewCapture> mLazyViewCapture;
private FakeFacePropertyRepository mFakeFacePropertyRepository =
new FakeFacePropertyRepository();
@@ -254,14 +246,12 @@ public class ScreenDecorationsTest extends SysuiTestCase {
new ScreenDecorationsLogger(logcatLogBuffer("TestLogBuffer")),
mFakeFacePropertyRepository));
- mViewCaptureAwareWindowManager = new ViewCaptureAwareWindowManager(mWindowManager,
- mLazyViewCapture, false);
mScreenDecorations = spy(new ScreenDecorations(mContext, mSecureSettings,
mCommandRegistry, mUserTracker, mDisplayTracker, mDotViewController,
mPrivacyDotDecorProviderFactory, mFaceScanningProviderFactory,
new ScreenDecorationsLogger(logcatLogBuffer("TestLogBuffer")),
mFakeFacePropertyRepository, mJavaAdapter, mCameraProtectionLoader,
- mViewCaptureAwareWindowManager, mMainHandler, mExecutor) {
+ mWindowManager, mMainHandler, mExecutor) {
@Override
public void start() {
super.start();
@@ -1276,7 +1266,7 @@ public class ScreenDecorationsTest extends SysuiTestCase {
mPrivacyDotDecorProviderFactory, mFaceScanningProviderFactory,
new ScreenDecorationsLogger(logcatLogBuffer("TestLogBuffer")),
mFakeFacePropertyRepository, mJavaAdapter, mCameraProtectionLoader,
- mViewCaptureAwareWindowManager, mMainHandler, mExecutor);
+ mWindowManager, mMainHandler, mExecutor);
screenDecorations.start();
when(mContext.getDisplay()).thenReturn(mDisplay);
when(mDisplay.getDisplayInfo(any())).thenAnswer(new Answer<Boolean>() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/IMagnificationConnectionTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/IMagnificationConnectionTest.java
index 6d75c4ca3a38..d3d4e24001cb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/IMagnificationConnectionTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/IMagnificationConnectionTest.java
@@ -43,13 +43,13 @@ import android.view.accessibility.IRemoteMagnificationAnimationCallback;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
-import com.android.app.viewcapture.ViewCaptureAwareWindowManager;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.model.SysUiState;
import com.android.systemui.recents.LauncherProxyService;
import com.android.systemui.settings.FakeDisplayTracker;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.util.settings.SecureSettings;
+import com.android.systemui.utils.windowmanager.WindowManagerProvider;
import org.junit.Before;
import org.junit.Test;
@@ -94,7 +94,7 @@ public class IMagnificationConnectionTest extends SysuiTestCase {
@Mock
private IWindowManager mIWindowManager;
@Mock
- private ViewCaptureAwareWindowManager mViewCaptureAwareWindowManager;
+ private WindowManagerProvider mWindowManagerProvider;
private IMagnificationConnection mIMagnificationConnection;
private MagnificationImpl mMagnification;
@@ -116,8 +116,7 @@ public class IMagnificationConnectionTest extends SysuiTestCase {
mTestableLooper.getLooper(), mContext.getMainExecutor(), mCommandQueue,
mModeSwitchesController, mSysUiState, mLauncherProxyService, mSecureSettings,
mDisplayTracker, getContext().getSystemService(DisplayManager.class),
- mA11yLogger, mIWindowManager, mAccessibilityManager,
- mViewCaptureAwareWindowManager);
+ mA11yLogger, mIWindowManager, mAccessibilityManager, mWindowManagerProvider);
mMagnification.mWindowMagnificationControllerSupplier =
new FakeWindowMagnificationControllerSupplier(
mContext.getSystemService(DisplayManager.class));
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationTest.java
index 8bfd2545ff2b..ae96e8fe7b8b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationTest.java
@@ -50,13 +50,13 @@ import android.view.accessibility.IMagnificationConnectionCallback;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
-import com.android.app.viewcapture.ViewCaptureAwareWindowManager;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.model.SysUiState;
import com.android.systemui.recents.LauncherProxyService;
import com.android.systemui.settings.FakeDisplayTracker;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.util.settings.SecureSettings;
+import com.android.systemui.utils.windowmanager.WindowManagerProvider;
import org.junit.Before;
import org.junit.Test;
@@ -98,7 +98,7 @@ public class MagnificationTest extends SysuiTestCase {
@Mock
private IWindowManager mIWindowManager;
@Mock
- private ViewCaptureAwareWindowManager mViewCaptureAwareWindowManager;
+ private WindowManagerProvider mWindowManagerProvider;
@Before
public void setUp() throws Exception {
@@ -132,8 +132,7 @@ public class MagnificationTest extends SysuiTestCase {
mCommandQueue, mModeSwitchesController,
mSysUiState, mLauncherProxyService, mSecureSettings, mDisplayTracker,
getContext().getSystemService(DisplayManager.class), mA11yLogger, mIWindowManager,
- getContext().getSystemService(AccessibilityManager.class),
- mViewCaptureAwareWindowManager);
+ getContext().getSystemService(AccessibilityManager.class), mWindowManagerProvider);
mMagnification.mWindowMagnificationControllerSupplier = new FakeControllerSupplier(
mContext.getSystemService(DisplayManager.class), mWindowMagnificationController);
mMagnification.mMagnificationSettingsSupplier = new FakeSettingsSupplier(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java
index a0f5b2214f80..ac0378b093c5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java
@@ -51,8 +51,6 @@ import android.view.animation.AccelerateInterpolator;
import android.window.InputTransferToken;
import androidx.test.filters.LargeTest;
-
-import com.android.app.viewcapture.ViewCaptureAwareWindowManager;
import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
import com.android.systemui.Flags;
import com.android.systemui.SysuiTestCase;
@@ -112,8 +110,6 @@ public class WindowMagnificationAnimationControllerTest extends SysuiTestCase {
SysUiState mSysUiState;
@Mock
SecureSettings mSecureSettings;
- @Mock
- ViewCaptureAwareWindowManager mViewCaptureAwareWindowManager;
private SpyWindowMagnificationController mController;
private WindowMagnificationController mSpyController;
private WindowMagnificationAnimationController mWindowMagnificationAnimationController;
@@ -167,7 +163,7 @@ public class WindowMagnificationAnimationControllerTest extends SysuiTestCase {
mSecureSettings,
scvhSupplier,
mSfVsyncFrameProvider,
- mViewCaptureAwareWindowManager);
+ mWindowManager);
mSpyController = mController.getSpyController();
}
@@ -1023,7 +1019,7 @@ public class WindowMagnificationAnimationControllerTest extends SysuiTestCase {
SecureSettings secureSettings,
Supplier<SurfaceControlViewHost> scvhSupplier,
SfVsyncFrameCallbackProvider sfVsyncFrameProvider,
- ViewCaptureAwareWindowManager viewCaptureAwareWindowManager) {
+ WindowManager windowManager) {
super(
context,
handler,
@@ -1033,7 +1029,8 @@ public class WindowMagnificationAnimationControllerTest extends SysuiTestCase {
callback,
sysUiState,
secureSettings,
- scvhSupplier);
+ scvhSupplier,
+ windowManager);
mSpyController = Mockito.mock(WindowMagnificationController.class);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
index 5b32b922d377..c8e0b14152fd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
@@ -238,7 +238,8 @@ public class WindowMagnificationControllerTest extends SysuiTestCase {
mWindowMagnifierCallback,
mSysUiState,
mSecureSettings,
- scvhSupplier);
+ scvhSupplier,
+ mWindowManager);
verify(mMirrorWindowControl).setWindowDelegate(
any(MirrorWindowControl.MirrorWindowDelegate.class));
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationSettingsTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationSettingsTest.java
index 45b9f4ad2322..dfc3ff2791b2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationSettingsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationSettingsTest.java
@@ -65,8 +65,6 @@ import androidx.test.InstrumentationRegistry;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
-import com.android.app.viewcapture.ViewCapture;
-import com.android.app.viewcapture.ViewCaptureAwareWindowManager;
import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.common.ui.view.SeekBarWithIconButtonsView;
@@ -74,8 +72,6 @@ import com.android.systemui.common.ui.view.SeekBarWithIconButtonsView.OnSeekBarW
import com.android.systemui.res.R;
import com.android.systemui.util.settings.SecureSettings;
-import kotlin.Lazy;
-
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@@ -104,8 +100,6 @@ public class WindowMagnificationSettingsTest extends SysuiTestCase {
private SecureSettings mSecureSettings;
@Mock
private WindowMagnificationSettingsCallback mWindowMagnificationSettingsCallback;
- @Mock
- private Lazy<ViewCapture> mLazyViewCapture;
private TestableWindowManager mWindowManager;
private WindowMagnificationSettings mWindowMagnificationSettings;
private MotionEventHelper mMotionEventHelper = new MotionEventHelper();
@@ -123,6 +117,7 @@ public class WindowMagnificationSettingsTest extends SysuiTestCase {
final WindowManager wm = mContext.getSystemService(WindowManager.class);
mWindowManager = spy(new TestableWindowManager(wm));
mContext.addMockSystemService(Context.WINDOW_SERVICE, mWindowManager);
+
mContext.addMockSystemService(Context.ACCESSIBILITY_SERVICE, mAccessibilityManager);
when(mSecureSettings.getIntForUser(anyString(), anyInt(), anyInt())).then(
@@ -130,11 +125,9 @@ public class WindowMagnificationSettingsTest extends SysuiTestCase {
when(mSecureSettings.getFloatForUser(anyString(), anyFloat(), anyInt())).then(
returnsSecondArg());
- ViewCaptureAwareWindowManager vwm = new ViewCaptureAwareWindowManager(mWindowManager,
- mLazyViewCapture, /* isViewCaptureEnabled= */ false);
mWindowMagnificationSettings = new WindowMagnificationSettings(mContext,
mWindowMagnificationSettingsCallback, mSfVsyncFrameProvider,
- mSecureSettings, vwm);
+ mSecureSettings, mWindowManager);
mSettingView = mWindowMagnificationSettings.getSettingView();
mZoomSeekbar = mSettingView.findViewById(R.id.magnifier_zoom_slider);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuControllerTest.java
index bc9d4c7fa0e6..b81cbe48b4ff 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuControllerTest.java
@@ -38,8 +38,6 @@ import android.view.accessibility.AccessibilityManager;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
-import com.android.app.viewcapture.ViewCapture;
-import com.android.app.viewcapture.ViewCaptureAwareWindowManager;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.KeyguardUpdateMonitorCallback;
import com.android.settingslib.bluetooth.HearingAidDeviceManager;
@@ -51,8 +49,6 @@ import com.android.systemui.navigationbar.NavigationModeController;
import com.android.systemui.settings.FakeDisplayTracker;
import com.android.systemui.util.settings.SecureSettings;
-import kotlin.Lazy;
-
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
@@ -78,7 +74,6 @@ public class AccessibilityFloatingMenuControllerTest extends SysuiTestCase {
private Context mContextWrapper;
private WindowManager mWindowManager;
- private ViewCaptureAwareWindowManager mViewCaptureAwareWindowManager;
private AccessibilityManager mAccessibilityManager;
private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
private AccessibilityFloatingMenuController mController;
@@ -93,8 +88,6 @@ public class AccessibilityFloatingMenuControllerTest extends SysuiTestCase {
@Mock
private SecureSettings mSecureSettings;
@Mock
- private Lazy<ViewCapture> mLazyViewCapture;
- @Mock
private NavigationModeController mNavigationModeController;
@Mock
private HearingAidDeviceManager mHearingAidDeviceManager;
@@ -110,8 +103,6 @@ public class AccessibilityFloatingMenuControllerTest extends SysuiTestCase {
};
mWindowManager = mContext.getSystemService(WindowManager.class);
- mViewCaptureAwareWindowManager = new ViewCaptureAwareWindowManager(mWindowManager,
- mLazyViewCapture, /* isViewCaptureEnabled= */ false);
mAccessibilityManager = mContext.getSystemService(AccessibilityManager.class);
mTestableLooper = TestableLooper.get(this);
@@ -172,8 +163,8 @@ public class AccessibilityFloatingMenuControllerTest extends SysuiTestCase {
enableAccessibilityFloatingMenuConfig();
mController = setUpController();
mController.mFloatingMenu = new MenuViewLayerController(mContextWrapper, mWindowManager,
- mViewCaptureAwareWindowManager, mAccessibilityManager, mSecureSettings,
- mNavigationModeController, mHearingAidDeviceManager);
+ mAccessibilityManager, mSecureSettings, mNavigationModeController,
+ mHearingAidDeviceManager);
captureKeyguardUpdateMonitorCallback();
mKeyguardCallback.onUserUnlocked();
@@ -200,8 +191,8 @@ public class AccessibilityFloatingMenuControllerTest extends SysuiTestCase {
enableAccessibilityFloatingMenuConfig();
mController = setUpController();
mController.mFloatingMenu = new MenuViewLayerController(mContextWrapper, mWindowManager,
- mViewCaptureAwareWindowManager, mAccessibilityManager, mSecureSettings,
- mNavigationModeController, mHearingAidDeviceManager);
+ mAccessibilityManager, mSecureSettings, mNavigationModeController,
+ mHearingAidDeviceManager);
captureKeyguardUpdateMonitorCallback();
mKeyguardCallback.onUserSwitching(fakeUserId);
@@ -215,8 +206,8 @@ public class AccessibilityFloatingMenuControllerTest extends SysuiTestCase {
enableAccessibilityFloatingMenuConfig();
mController = setUpController();
mController.mFloatingMenu = new MenuViewLayerController(mContextWrapper, mWindowManager,
- mViewCaptureAwareWindowManager, mAccessibilityManager, mSecureSettings,
- mNavigationModeController, mHearingAidDeviceManager);
+ mAccessibilityManager, mSecureSettings, mNavigationModeController,
+ mHearingAidDeviceManager);
captureKeyguardUpdateMonitorCallback();
mKeyguardCallback.onUserUnlocked();
mKeyguardCallback.onKeyguardVisibilityChanged(true);
@@ -362,18 +353,15 @@ public class AccessibilityFloatingMenuControllerTest extends SysuiTestCase {
private AccessibilityFloatingMenuController setUpController() {
final WindowManager windowManager = mContext.getSystemService(WindowManager.class);
- final ViewCaptureAwareWindowManager viewCaptureAwareWindowManager =
- new ViewCaptureAwareWindowManager(windowManager, mLazyViewCapture,
- /* isViewCaptureEnabled= */ false);
final DisplayManager displayManager = mContext.getSystemService(DisplayManager.class);
final FakeDisplayTracker displayTracker = new FakeDisplayTracker(mContext);
mKeyguardUpdateMonitor = Dependency.get(KeyguardUpdateMonitor.class);
final AccessibilityFloatingMenuController controller =
new AccessibilityFloatingMenuController(mContextWrapper, windowManager,
- viewCaptureAwareWindowManager, displayManager, mAccessibilityManager,
- mTargetsObserver, mModeObserver, mHearingAidDeviceManager,
- mKeyguardUpdateMonitor, mSecureSettings, displayTracker,
- mNavigationModeController, new Handler(mTestableLooper.getLooper()));
+ displayManager, mAccessibilityManager, mTargetsObserver, mModeObserver,
+ mHearingAidDeviceManager, mKeyguardUpdateMonitor, mSecureSettings,
+ displayTracker, mNavigationModeController,
+ new Handler(mTestableLooper.getLooper()));
controller.init();
return controller;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewAppearanceTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewAppearanceTest.java
index 146488b523ad..108f3ae86f9b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewAppearanceTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuViewAppearanceTest.java
@@ -69,4 +69,25 @@ public class MenuViewAppearanceTest extends SysuiTestCase {
assertThat(end_y).isEqualTo(end_y);
}
+
+ @Test
+ public void avoidVerticalDisplayCutout_doesNotExceedTopBounds() {
+ final int y = DRAGGABLE_BOUNDS.top - 100;
+
+ final float end_y = MenuViewAppearance.avoidVerticalDisplayCutout(
+ y, MENU_HEIGHT, DRAGGABLE_BOUNDS, new Rect(0, 5, 0, 6));
+
+ assertThat(end_y).isGreaterThan(DRAGGABLE_BOUNDS.top);
+ }
+
+
+ @Test
+ public void avoidVerticalDisplayCutout_doesNotExceedBottomBounds() {
+ final int y = DRAGGABLE_BOUNDS.bottom + 100;
+
+ final float end_y = MenuViewAppearance.avoidVerticalDisplayCutout(
+ y, MENU_HEIGHT, DRAGGABLE_BOUNDS, new Rect(0, 5, 0, 6));
+
+ assertThat(end_y).isLessThan(DRAGGABLE_BOUNDS.bottom);
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityTransitionAnimatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityTransitionAnimatorTest.kt
index 60345a358bac..dfb6afe8bd12 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityTransitionAnimatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityTransitionAnimatorTest.kt
@@ -249,10 +249,12 @@ class ActivityTransitionAnimatorTest : SysuiTestCase() {
var factory = controllerFactory(controller)
underTest.register(factory.cookie, factory, testScope)
assertEquals(2, testShellTransitions.remotes.size)
+ assertTrue(testShellTransitions.remotesForTakeover.isEmpty())
factory = controllerFactory(controller)
underTest.register(factory.cookie, factory, testScope)
assertEquals(4, testShellTransitions.remotes.size)
+ assertTrue(testShellTransitions.remotesForTakeover.isEmpty())
}
}
@@ -350,6 +352,7 @@ class ActivityTransitionAnimatorTest : SysuiTestCase() {
val controller = createController()
val runner = underTest.createEphemeralRunner(controller)
runner.onAnimationCancelled()
+ waitForIdleSync()
runner.onAnimationStart(
TRANSIT_NONE,
emptyArray(),
@@ -357,12 +360,13 @@ class ActivityTransitionAnimatorTest : SysuiTestCase() {
emptyArray(),
iCallback,
)
-
waitForIdleSync()
+
verify(controller).onTransitionAnimationCancelled()
verify(controller, never()).onTransitionAnimationStart(anyBoolean())
verify(listener).onTransitionAnimationCancelled()
verify(listener, never()).onTransitionAnimationStart()
+ verify(iCallback).onAnimationFinished()
assertNull(runner.delegate)
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt
index a1d038ad8554..a9e6a3ebdb30 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt
@@ -28,8 +28,6 @@ import android.view.WindowManager
import android.view.accessibility.AccessibilityManager
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.app.viewcapture.ViewCapture
-import com.android.app.viewcapture.ViewCaptureAwareWindowManager
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.systemui.SysuiTestCase
import com.android.systemui.animation.ActivityTransitionAnimator
@@ -62,7 +60,6 @@ import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.testKosmos
import com.android.systemui.user.domain.interactor.SelectedUserInteractor
import com.google.common.truth.Truth.assertThat
-import dagger.Lazy
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
@@ -98,7 +95,6 @@ class UdfpsControllerOverlayTest : SysuiTestCase() {
@Mock private lateinit var inflater: LayoutInflater
@Mock private lateinit var windowManager: WindowManager
- @Mock private lateinit var lazyViewCapture: kotlin.Lazy<ViewCapture>
@Mock private lateinit var accessibilityManager: AccessibilityManager
@Mock private lateinit var statusBarStateController: StatusBarStateController
@Mock private lateinit var statusBarKeyguardViewManager: StatusBarKeyguardViewManager
@@ -160,11 +156,7 @@ class UdfpsControllerOverlayTest : SysuiTestCase() {
UdfpsControllerOverlay(
context,
inflater,
- ViewCaptureAwareWindowManager(
- windowManager,
- lazyViewCapture,
- isViewCaptureEnabled = false,
- ),
+ windowManager,
accessibilityManager,
statusBarStateController,
statusBarKeyguardViewManager,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt
index 2c70249bcb06..bf79d11b2fb8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt
@@ -1472,7 +1472,7 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa
whenever(kosmos.udfpsUtils.onTouchOutsideOfSensorArea(any(), any(), any(), any(), any()))
.thenReturn("Direction")
- kosmos.promptViewModel.onAnnounceAccessibilityHint(
+ kosmos.promptViewModel.onUpdateAccessibilityHint(
obtainMotionEvent(MotionEvent.ACTION_HOVER_ENTER),
true,
)
@@ -1497,7 +1497,7 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa
whenever(kosmos.udfpsUtils.onTouchOutsideOfSensorArea(any(), any(), any(), any(), any()))
.thenReturn("Direction")
- kosmos.promptViewModel.onAnnounceAccessibilityHint(
+ kosmos.promptViewModel.onUpdateAccessibilityHint(
obtainMotionEvent(MotionEvent.ACTION_HOVER_ENTER),
true,
)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/charging/WiredChargingRippleControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/charging/WiredChargingRippleControllerTest.kt
index 57b397cfca7e..034bab855faf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/charging/WiredChargingRippleControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/charging/WiredChargingRippleControllerTest.kt
@@ -23,13 +23,11 @@ import android.view.WindowManager
import android.view.WindowMetrics
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.app.viewcapture.ViewCapture
-import com.android.app.viewcapture.ViewCaptureAwareWindowManager
import com.android.internal.logging.UiEventLogger
+import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
-import com.android.systemui.res.R
import com.android.systemui.statusbar.commandline.CommandRegistry
import com.android.systemui.statusbar.policy.BatteryController
import com.android.systemui.statusbar.policy.ConfigurationController
@@ -42,12 +40,12 @@ import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
import org.mockito.ArgumentMatchers
import org.mockito.Mock
+import org.mockito.Mockito.`when`
import org.mockito.Mockito.any
import org.mockito.Mockito.eq
import org.mockito.Mockito.never
import org.mockito.Mockito.reset
import org.mockito.Mockito.verify
-import org.mockito.Mockito.`when`
import org.mockito.MockitoAnnotations
@SmallTest
@@ -62,7 +60,6 @@ class WiredChargingRippleControllerTest : SysuiTestCase() {
@Mock private lateinit var windowManager: WindowManager
@Mock private lateinit var uiEventLogger: UiEventLogger
@Mock private lateinit var windowMetrics: WindowMetrics
- @Mock private lateinit var lazyViewCapture: Lazy<ViewCapture>
private val systemClock = FakeSystemClock()
@Before
@@ -71,9 +68,7 @@ class WiredChargingRippleControllerTest : SysuiTestCase() {
`when`(featureFlags.isEnabled(Flags.CHARGING_RIPPLE)).thenReturn(true)
controller = WiredChargingRippleController(
commandRegistry, batteryController, configurationController,
- featureFlags, context, windowManager,
- ViewCaptureAwareWindowManager(windowManager,
- lazyViewCapture, isViewCaptureEnabled = false), systemClock, uiEventLogger)
+ featureFlags, context, windowManager, systemClock, uiEventLogger)
rippleView.setupShader()
controller.rippleView = rippleView // Replace the real ripple view with a mock instance
controller.registerCallbacks()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
index 8105ae0960ad..41fdaa74e57b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
@@ -73,10 +73,10 @@ import android.view.IRemoteAnimationFinishedCallback;
import android.view.RemoteAnimationTarget;
import android.view.View;
import android.view.ViewRootImpl;
+import android.view.WindowManager;
import androidx.test.filters.SmallTest;
-import com.android.app.viewcapture.ViewCaptureAwareWindowManager;
import com.android.internal.foldables.FoldGracePeriodProvider;
import com.android.internal.logging.InstanceId;
import com.android.internal.logging.UiEventLogger;
@@ -101,7 +101,6 @@ import com.android.systemui.dreams.ui.viewmodel.DreamViewModel;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FakeFeatureFlags;
import com.android.systemui.flags.SystemPropertiesHelper;
-import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor;
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionBootInteractor;
import com.android.systemui.kosmos.KosmosJavaAdapter;
import com.android.systemui.log.SessionTracker;
@@ -176,7 +175,7 @@ public class KeyguardViewMediatorTest extends SysuiTestCase {
private @Mock BroadcastDispatcher mBroadcastDispatcher;
private @Mock DismissCallbackRegistry mDismissCallbackRegistry;
private @Mock DumpManager mDumpManager;
- private @Mock ViewCaptureAwareWindowManager mWindowManager;
+ private @Mock WindowManager mWindowManager;
private @Mock IActivityManager mActivityManager;
private @Mock ConfigurationController mConfigurationController;
private @Mock PowerManager mPowerManager;
@@ -207,7 +206,6 @@ public class KeyguardViewMediatorTest extends SysuiTestCase {
private @Mock ShadeWindowLogger mShadeWindowLogger;
private @Mock SelectedUserInteractor mSelectedUserInteractor;
private @Mock UserTracker.Callback mUserTrackerCallback;
- private @Mock KeyguardInteractor mKeyguardInteractor;
private @Mock KeyguardTransitionBootInteractor mKeyguardTransitionBootInteractor;
private @Captor ArgumentCaptor<KeyguardStateController.Callback>
mKeyguardStateControllerCallback;
@@ -1499,9 +1497,10 @@ public class KeyguardViewMediatorTest extends SysuiTestCase {
mSystemPropertiesHelper,
() -> mock(WindowManagerLockscreenVisibilityManager.class),
mSelectedUserInteractor,
- mKeyguardInteractor,
+ mKosmos.getKeyguardInteractor(),
mKeyguardTransitionBootInteractor,
mKosmos::getCommunalSceneInteractor,
+ mKosmos::getCommunalSettingsInteractor,
mock(WindowManagerOcclusionManager.class));
mViewMediator.mUserChangedCallback = mUserTrackerCallback;
mViewMediator.start();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTestKt.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTestKt.kt
index 86f7966d4ada..d6b778fe2bc2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTestKt.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTestKt.kt
@@ -35,8 +35,14 @@ import com.android.systemui.SysuiTestCase
import com.android.systemui.animation.activityTransitionAnimator
import com.android.systemui.broadcast.broadcastDispatcher
import com.android.systemui.classifier.falsingCollector
+import com.android.systemui.common.data.repository.batteryRepository
+import com.android.systemui.common.data.repository.fake
+import com.android.systemui.communal.data.model.FEATURE_AUTO_OPEN
+import com.android.systemui.communal.data.model.SuppressionReason
import com.android.systemui.communal.data.repository.communalSceneRepository
import com.android.systemui.communal.domain.interactor.communalSceneInteractor
+import com.android.systemui.communal.domain.interactor.communalSettingsInteractor
+import com.android.systemui.communal.domain.interactor.setCommunalV2Enabled
import com.android.systemui.communal.shared.model.CommunalScenes
import com.android.systemui.communal.ui.viewmodel.communalTransitionViewModel
import com.android.systemui.concurrency.fakeExecutor
@@ -81,8 +87,11 @@ import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentMatchers.any
import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.kotlin.anyOrNull
import org.mockito.kotlin.doReturn
+import org.mockito.kotlin.eq
import org.mockito.kotlin.mock
+import org.mockito.kotlin.verify
/** Kotlin version of KeyguardViewMediatorTest to allow for coroutine testing. */
@SmallTest
@@ -152,6 +161,7 @@ class KeyguardViewMediatorTestKt : SysuiTestCase() {
keyguardInteractor,
keyguardTransitionBootInteractor,
{ communalSceneInteractor },
+ { communalSettingsInteractor },
mock<WindowManagerOcclusionManager>(),
)
}
@@ -164,6 +174,10 @@ class KeyguardViewMediatorTestKt : SysuiTestCase() {
@Test
fun doKeyguardTimeout_changesCommunalScene() =
kosmos.runTest {
+ // Hub is enabled and hub condition is active.
+ setCommunalV2Enabled(true)
+ enableHubOnCharging()
+
// doKeyguardTimeout message received.
val timeoutOptions = Bundle()
timeoutOptions.putBoolean(KeyguardViewMediator.EXTRA_TRIGGER_HUB, true)
@@ -174,4 +188,56 @@ class KeyguardViewMediatorTestKt : SysuiTestCase() {
assertThat(communalSceneRepository.currentScene.value)
.isEqualTo(CommunalScenes.Communal)
}
+
+ @Test
+ fun doKeyguardTimeout_communalNotAvailable_sleeps() =
+ kosmos.runTest {
+ // Hub disabled.
+ setCommunalV2Enabled(false)
+
+ // doKeyguardTimeout message received.
+ val timeoutOptions = Bundle()
+ timeoutOptions.putBoolean(KeyguardViewMediator.EXTRA_TRIGGER_HUB, true)
+ underTest.doKeyguardTimeout(timeoutOptions)
+ testableLooper.processAllMessages()
+
+ // Sleep is requested.
+ verify(powerManager)
+ .goToSleep(anyOrNull(), eq(PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON), eq(0))
+
+ // Hub scene is not changed.
+ assertThat(communalSceneRepository.currentScene.value).isEqualTo(CommunalScenes.Blank)
+ }
+
+ @Test
+ fun doKeyguardTimeout_hubConditionNotActive_sleeps() =
+ kosmos.runTest {
+ // Communal enabled, but hub condition set to never.
+ setCommunalV2Enabled(true)
+ disableHubShowingAutomatically()
+
+ // doKeyguardTimeout message received.
+ val timeoutOptions = Bundle()
+ timeoutOptions.putBoolean(KeyguardViewMediator.EXTRA_TRIGGER_HUB, true)
+ underTest.doKeyguardTimeout(timeoutOptions)
+ testableLooper.processAllMessages()
+
+ // Sleep is requested.
+ verify(powerManager)
+ .goToSleep(anyOrNull(), eq(PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON), eq(0))
+
+ // Hub scene is not changed.
+ assertThat(communalSceneRepository.currentScene.value).isEqualTo(CommunalScenes.Blank)
+ }
+
+ private fun Kosmos.enableHubOnCharging() {
+ communalSettingsInteractor.setSuppressionReasons(emptyList())
+ batteryRepository.fake.setDevicePluggedIn(true)
+ }
+
+ private fun Kosmos.disableHubShowingAutomatically() {
+ communalSettingsInteractor.setSuppressionReasons(
+ listOf(SuppressionReason.ReasonUnknown(FEATURE_AUTO_OPEN))
+ )
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySectionTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySectionTest.kt
index df24bff43c08..78a4fbecabe8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySectionTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySectionTest.kt
@@ -31,11 +31,13 @@ import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.ui.viewmodel.DeviceEntryBackgroundViewModel
import com.android.systemui.keyguard.ui.viewmodel.DeviceEntryForegroundViewModel
import com.android.systemui.keyguard.ui.viewmodel.DeviceEntryIconViewModel
+import com.android.systemui.kosmos.testDispatcher
import com.android.systemui.log.logcatLogBuffer
import com.android.systemui.plugins.FalsingManager
import com.android.systemui.res.R
import com.android.systemui.shade.NotificationPanelView
import com.android.systemui.statusbar.VibratorHelper
+import com.android.systemui.testKosmos
import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.flow.MutableStateFlow
@@ -68,6 +70,7 @@ class DefaultDeviceEntrySectionTest : SysuiTestCase() {
underTest =
DefaultDeviceEntrySection(
TestScope().backgroundScope,
+ testKosmos().testDispatcher,
authController,
windowManager,
context,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaSwitchingControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaSwitchingControllerTest.java
index 798aa428e73e..eb72acc0dade 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaSwitchingControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaSwitchingControllerTest.java
@@ -109,6 +109,7 @@ import java.util.Collections;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
@SmallTest
@RunWith(ParameterizedAndroidJunit4.class)
@@ -151,8 +152,10 @@ public class MediaSwitchingControllerTest extends SysuiTestCase {
private MediaDevice mMediaDevice1;
@Mock
private MediaDevice mMediaDevice2;
- @Mock
- private NearbyDevice mNearbyDevice1;
+ @Mock private MediaDevice mMediaDevice3;
+ @Mock private MediaDevice mMediaDevice4;
+ @Mock private MediaDevice mMediaDevice5;
+ @Mock private NearbyDevice mNearbyDevice1;
@Mock
private NearbyDevice mNearbyDevice2;
@Mock
@@ -1550,6 +1553,89 @@ public class MediaSwitchingControllerTest extends SysuiTestCase {
assertThat(items.get(1).getMediaDevice().get()).isEqualTo(mMediaDevice2);
}
+ @EnableFlags(Flags.FLAG_ENABLE_OUTPUT_SWITCHER_DEVICE_GROUPING)
+ @Test
+ public void selectedDevicesAddedInSameOrderWhenRlpDoesNotExist() {
+ setUpSelectedDevicesAndOrdering();
+
+ mMediaSwitchingController.onDeviceListUpdate(mMediaDevices);
+
+ List<MediaDevice> devices =
+ mMediaSwitchingController.getMediaItemList().stream()
+ .filter(item -> item.getMediaDevice().isPresent())
+ .map(item -> item.getMediaDevice().orElse(null))
+ .collect(Collectors.toList());
+ assertThat(devices)
+ .containsExactly(
+ mMediaDevice4,
+ mMediaDevice3,
+ mMediaDevice5,
+ mMediaDevice1,
+ mMediaDevice2)
+ .inOrder();
+ }
+
+ @DisableFlags(Flags.FLAG_ENABLE_OUTPUT_SWITCHER_DEVICE_GROUPING)
+ @Test
+ public void selectedDevicesAddedInSortedOrderWhenRlpDoesNotExist() {
+ setUpSelectedDevicesAndOrdering();
+
+ mMediaSwitchingController.onDeviceListUpdate(mMediaDevices);
+
+ List<MediaDevice> devices =
+ mMediaSwitchingController.getMediaItemList().stream()
+ .filter(item -> item.getMediaDevice().isPresent())
+ .map(item -> item.getMediaDevice().orElse(null))
+ .collect(Collectors.toList());
+
+ assertThat(devices)
+ .containsExactly(
+ mMediaDevice5,
+ mMediaDevice4,
+ mMediaDevice3,
+ mMediaDevice1,
+ mMediaDevice2)
+ .inOrder();
+ }
+
+ private void setUpSelectedDevicesAndOrdering() {
+ when(mMediaDevice1.getId()).thenReturn(TEST_DEVICE_1_ID);
+ when(mMediaDevice2.getId()).thenReturn(TEST_DEVICE_2_ID);
+ when(mMediaDevice3.getId()).thenReturn(TEST_DEVICE_3_ID);
+ when(mMediaDevice4.getId()).thenReturn(TEST_DEVICE_4_ID);
+ when(mMediaDevice5.getId()).thenReturn(TEST_DEVICE_5_ID);
+ mMediaDevices.clear();
+ Collections.addAll(
+ mMediaDevices,
+ mMediaDevice2,
+ mMediaDevice1,
+ mMediaDevice4,
+ mMediaDevice3,
+ mMediaDevice5);
+ List<MediaDevice> selectedMediaDevices = new ArrayList<>();
+ Collections.addAll(selectedMediaDevices, mMediaDevice3, mMediaDevice4, mMediaDevice5);
+ doReturn(selectedMediaDevices).when(mLocalMediaManager).getSelectedMediaDevice();
+ // Sort the media devices in the order they appear in the deviceOrder list
+ List<MediaDevice> deviceOrder = new ArrayList<>();
+ Collections.addAll(
+ deviceOrder,
+ mMediaDevice1,
+ mMediaDevice2,
+ mMediaDevice3,
+ mMediaDevice4,
+ mMediaDevice5);
+ for (int i = 0; i < deviceOrder.size(); i++) {
+ for (int j = i + 1; j < deviceOrder.size(); j++) {
+ when(deviceOrder.get(i).compareTo(deviceOrder.get(j))).thenReturn(-1);
+ when(deviceOrder.get(j).compareTo(deviceOrder.get(i))).thenReturn(1);
+ }
+ }
+ when(mLocalMediaManager.isPreferenceRouteListingExist()).thenReturn(false);
+ mMediaSwitchingController.start(mCb);
+ reset(mCb);
+ mMediaSwitchingController.getMediaItemList().clear();
+ }
+
@DisableFlags(Flags.FLAG_ENABLE_OUTPUT_SWITCHER_DEVICE_GROUPING)
@Test
public void selectedDevicesAddedInReverseOrder() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/FakeMediaTttChipControllerReceiver.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/FakeMediaTttChipControllerReceiver.kt
index c7beb158c2de..add87686bc9f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/FakeMediaTttChipControllerReceiver.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/FakeMediaTttChipControllerReceiver.kt
@@ -20,8 +20,8 @@ import android.content.Context
import android.os.Handler
import android.os.PowerManager
import android.view.ViewGroup
+import android.view.WindowManager
import android.view.accessibility.AccessibilityManager
-import com.android.app.viewcapture.ViewCaptureAwareWindowManager
import com.android.systemui.dump.DumpManager
import com.android.systemui.statusbar.CommandQueue
import com.android.systemui.statusbar.policy.ConfigurationController
@@ -35,7 +35,7 @@ class FakeMediaTttChipControllerReceiver(
commandQueue: CommandQueue,
context: Context,
logger: MediaTttReceiverLogger,
- viewCaptureAwareWindowManager: ViewCaptureAwareWindowManager,
+ windowManager: WindowManager,
mainExecutor: DelayableExecutor,
accessibilityManager: AccessibilityManager,
configurationController: ConfigurationController,
@@ -53,7 +53,7 @@ class FakeMediaTttChipControllerReceiver(
commandQueue,
context,
logger,
- viewCaptureAwareWindowManager,
+ windowManager,
mainExecutor,
accessibilityManager,
configurationController,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt
index 378dd452d030..1aa6ac67ec27 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt
@@ -31,8 +31,6 @@ import android.view.accessibility.AccessibilityManager
import android.widget.ImageView
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.app.viewcapture.ViewCapture
-import com.android.app.viewcapture.ViewCaptureAwareWindowManager
import com.android.internal.logging.InstanceId
import com.android.internal.logging.testing.UiEventLoggerFake
import com.android.systemui.SysuiTestCase
@@ -75,8 +73,6 @@ class MediaTttChipControllerReceiverTest : SysuiTestCase() {
@Mock private lateinit var windowManager: WindowManager
@Mock private lateinit var commandQueue: CommandQueue
@Mock private lateinit var rippleController: MediaTttReceiverRippleController
- @Mock private lateinit var lazyViewCapture: Lazy<ViewCapture>
- private lateinit var viewCaptureAwareWindowManager: ViewCaptureAwareWindowManager
private lateinit var commandQueueCallback: CommandQueue.Callbacks
private lateinit var fakeAppIconDrawable: Drawable
private lateinit var uiEventLoggerFake: UiEventLoggerFake
@@ -114,18 +110,12 @@ class MediaTttChipControllerReceiverTest : SysuiTestCase() {
fakeWakeLockBuilder = WakeLockFake.Builder(context)
fakeWakeLockBuilder.setWakeLock(fakeWakeLock)
- viewCaptureAwareWindowManager =
- ViewCaptureAwareWindowManager(
- windowManager,
- lazyViewCapture,
- isViewCaptureEnabled = false,
- )
controllerReceiver =
FakeMediaTttChipControllerReceiver(
commandQueue,
context,
logger,
- viewCaptureAwareWindowManager,
+ windowManager,
fakeExecutor,
accessibilityManager,
configurationController,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderCoordinatorTest.kt
index c90ac5993c31..18de32e80da6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderCoordinatorTest.kt
@@ -34,7 +34,6 @@ import android.widget.TextView
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.app.viewcapture.ViewCapture
-import com.android.app.viewcapture.ViewCaptureAwareWindowManager
import com.android.internal.logging.testing.UiEventLoggerFake
import com.android.internal.statusbar.IUndoMediaTransferCallback
import com.android.systemui.SysuiTestCase
@@ -100,7 +99,6 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
@Mock private lateinit var windowManager: WindowManager
@Mock private lateinit var vibratorHelper: VibratorHelper
@Mock private lateinit var swipeHandler: SwipeChipbarAwayGestureHandler
- @Mock private lateinit var lazyViewCapture: Lazy<ViewCapture>
private lateinit var fakeWakeLockBuilder: WakeLockFake.Builder
private lateinit var fakeWakeLock: WakeLockFake
private lateinit var chipbarCoordinator: ChipbarCoordinator
@@ -145,11 +143,7 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
ChipbarCoordinator(
context,
chipbarLogger,
- ViewCaptureAwareWindowManager(
- windowManager,
- lazyViewCapture,
- isViewCaptureEnabled = false,
- ),
+ windowManager,
fakeExecutor,
accessibilityManager,
configurationController,
@@ -1476,7 +1470,7 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
private fun ViewGroup.getUndoButton(): View = this.requireViewById(R.id.end_button)
private fun ChipStateSender.getExpectedStateText(
- otherDeviceName: String = OTHER_DEVICE_NAME
+ otherDeviceName: String = OTHER_DEVICE_NAME,
): String? {
return this.getChipTextString(context, otherDeviceName).loadText(context)
}
@@ -1487,7 +1481,7 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
commandQueueCallback.updateMediaTapToTransferSenderDisplay(
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_TRIGGERED,
routeInfo,
- null,
+ null
)
}
@@ -1497,7 +1491,7 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
commandQueueCallback.updateMediaTapToTransferSenderDisplay(
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_TRIGGERED,
routeInfo,
- null,
+ null
)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/gestural/BackPanelControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/gestural/BackPanelControllerTest.kt
index b4db6da2000a..b169cc12f08a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/gestural/BackPanelControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/gestural/BackPanelControllerTest.kt
@@ -27,7 +27,6 @@ import android.view.ViewConfiguration
import android.view.WindowManager
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.app.viewcapture.ViewCaptureAwareWindowManager
import com.android.internal.jank.Cuj
import com.android.internal.util.LatencyTracker
import com.android.systemui.SysuiTestCase
@@ -64,7 +63,7 @@ class BackPanelControllerTest : SysuiTestCase() {
private var triggerThreshold: Float = 0.0f
private val touchSlop = ViewConfiguration.get(context).scaledEdgeSlop
@Mock private lateinit var vibratorHelper: VibratorHelper
- @Mock private lateinit var viewCaptureAwareWindowManager: ViewCaptureAwareWindowManager
+ @Mock private lateinit var windowManager: WindowManager
@Mock private lateinit var configurationController: ConfigurationController
@Mock private lateinit var latencyTracker: LatencyTracker
private val interactionJankMonitor by lazy { kosmos.interactionJankMonitor }
@@ -79,7 +78,7 @@ class BackPanelControllerTest : SysuiTestCase() {
mBackPanelController =
BackPanelController(
context,
- viewCaptureAwareWindowManager,
+ windowManager,
ViewConfiguration.get(context),
Handler.createAsync(testableLooper.looper),
systemClock,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskInitializerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskInitializerTest.kt
index 8a4f1ad13b78..5596cc7ee7da 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskInitializerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskInitializerTest.kt
@@ -38,8 +38,6 @@ import com.android.systemui.notetask.NoteTaskEntryPoint.TAIL_BUTTON
import com.android.systemui.settings.FakeUserTracker
import com.android.systemui.statusbar.CommandQueue
import com.android.systemui.util.concurrency.FakeExecutor
-import com.android.systemui.util.mockito.any
-import com.android.systemui.util.mockito.eq
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
import com.android.systemui.util.mockito.withArgCaptor
@@ -52,11 +50,14 @@ import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
+import org.mockito.Mockito.anyList
import org.mockito.Mockito.never
import org.mockito.Mockito.times
import org.mockito.Mockito.verify
import org.mockito.Mockito.verifyNoMoreInteractions
import org.mockito.MockitoAnnotations.initMocks
+import org.mockito.kotlin.any
+import org.mockito.kotlin.eq
/** atest SystemUITests:NoteTaskInitializerTest */
@OptIn(InternalNoteTaskApi::class)
@@ -180,6 +181,18 @@ internal class NoteTaskInitializerTest : SysuiTestCase() {
@Test
@EnableFlags(com.android.hardware.input.Flags.FLAG_USE_KEY_GESTURE_EVENT_HANDLER)
+ fun initialize_keyGestureTypeOpenNotes_isRegistered() {
+ val underTest = createUnderTest(isEnabled = true, bubbles = bubbles)
+ underTest.initialize()
+ verify(inputManager)
+ .registerKeyGestureEventHandler(
+ eq(listOf(KeyGestureEvent.KEY_GESTURE_TYPE_OPEN_NOTES)),
+ any(),
+ )
+ }
+
+ @Test
+ @EnableFlags(com.android.hardware.input.Flags.FLAG_USE_KEY_GESTURE_EVENT_HANDLER)
fun handlesShortcut_keyGestureTypeOpenNotes() {
val gestureEvent =
KeyGestureEvent.Builder()
@@ -189,12 +202,12 @@ internal class NoteTaskInitializerTest : SysuiTestCase() {
val underTest = createUnderTest(isEnabled = true, bubbles = bubbles)
underTest.initialize()
val callback = withArgCaptor {
- verify(inputManager).registerKeyGestureEventHandler(capture())
+ verify(inputManager).registerKeyGestureEventHandler(anyList(), capture())
}
- assertThat(callback.handleKeyGestureEvent(gestureEvent, null)).isTrue()
-
+ callback.handleKeyGestureEvent(gestureEvent, null)
executor.runAllReady()
+
verify(controller).showNoteTask(eq(KEYBOARD_SHORTCUT))
}
@@ -203,19 +216,19 @@ internal class NoteTaskInitializerTest : SysuiTestCase() {
fun handlesShortcut_stylusTailButton() {
val gestureEvent =
KeyGestureEvent.Builder()
- .setKeycodes(intArrayOf(KeyEvent.KEYCODE_STYLUS_BUTTON_TAIL))
+ .setKeycodes(intArrayOf(KEYCODE_STYLUS_BUTTON_TAIL))
.setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_OPEN_NOTES)
.setAction(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
.build()
val underTest = createUnderTest(isEnabled = true, bubbles = bubbles)
underTest.initialize()
val callback = withArgCaptor {
- verify(inputManager).registerKeyGestureEventHandler(capture())
+ verify(inputManager).registerKeyGestureEventHandler(anyList(), capture())
}
- assertThat(callback.handleKeyGestureEvent(gestureEvent, null)).isTrue()
-
+ callback.handleKeyGestureEvent(gestureEvent, null)
executor.runAllReady()
+
verify(controller).showNoteTask(eq(TAIL_BUTTON))
}
@@ -224,19 +237,19 @@ internal class NoteTaskInitializerTest : SysuiTestCase() {
fun ignoresUnrelatedShortcuts() {
val gestureEvent =
KeyGestureEvent.Builder()
- .setKeycodes(intArrayOf(KeyEvent.KEYCODE_STYLUS_BUTTON_TAIL))
+ .setKeycodes(intArrayOf(KEYCODE_STYLUS_BUTTON_TAIL))
.setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_HOME)
.setAction(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
.build()
val underTest = createUnderTest(isEnabled = true, bubbles = bubbles)
underTest.initialize()
val callback = withArgCaptor {
- verify(inputManager).registerKeyGestureEventHandler(capture())
+ verify(inputManager).registerKeyGestureEventHandler(anyList(), capture())
}
- assertThat(callback.handleKeyGestureEvent(gestureEvent, null)).isFalse()
-
+ callback.handleKeyGestureEvent(gestureEvent, null)
executor.runAllReady()
+
verify(controller, never()).showNoteTask(any())
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/panels/ui/compose/DragAndDropTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/panels/ui/compose/DragAndDropTest.kt
index 7e42ec7e83b1..1551375f6879 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/panels/ui/compose/DragAndDropTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/panels/ui/compose/DragAndDropTest.kt
@@ -37,6 +37,7 @@ import com.android.systemui.common.shared.model.Icon
import com.android.systemui.qs.panels.shared.model.SizedTile
import com.android.systemui.qs.panels.shared.model.SizedTileImpl
import com.android.systemui.qs.panels.ui.compose.infinitegrid.DefaultEditTileGrid
+import com.android.systemui.qs.panels.ui.viewmodel.AvailableEditActions
import com.android.systemui.qs.panels.ui.viewmodel.EditTileViewModel
import com.android.systemui.qs.pipeline.shared.TileSpec
import com.android.systemui.qs.shared.model.TileCategory
@@ -63,7 +64,7 @@ class DragAndDropTest : SysuiTestCase() {
columns = 4,
largeTilesSpan = 4,
modifier = Modifier.fillMaxSize(),
- onAddTile = {},
+ onAddTile = { _, _ -> },
onRemoveTile = {},
onSetTiles = onSetTiles,
onResize = { _, _ -> },
@@ -84,7 +85,7 @@ class DragAndDropTest : SysuiTestCase() {
}
composeRule.waitForIdle()
- listState.onStarted(TestEditTiles[0], DragType.Add)
+ listState.onStarted(TestEditTiles[0], DragType.Move)
// Tile is being dragged, it should be replaced with a placeholder
composeRule.onNodeWithContentDescription("tileA").assertDoesNotExist()
@@ -103,6 +104,45 @@ class DragAndDropTest : SysuiTestCase() {
}
@Test
+ fun nonRemovableDraggedTile_removeHeaderShouldNotExist() {
+ val nonRemovableTile = createEditTile("tileA", isRemovable = false)
+ val listState = EditTileListState(listOf(nonRemovableTile), columns = 4, largeTilesSpan = 2)
+ composeRule.setContent { EditTileGridUnderTest(listState) {} }
+ composeRule.waitForIdle()
+
+ listState.onStarted(nonRemovableTile, DragType.Move)
+
+ // Tile is being dragged, it should be replaced with a placeholder
+ composeRule.onNodeWithContentDescription("tileA").assertDoesNotExist()
+
+ // Remove drop zone should not appear
+ composeRule.onNodeWithText("Remove").assertDoesNotExist()
+ }
+
+ @Test
+ fun droppedNonRemovableDraggedTile_shouldStayInGrid() {
+ val nonRemovableTile = createEditTile("tileA", isRemovable = false)
+ val listState = EditTileListState(listOf(nonRemovableTile), columns = 4, largeTilesSpan = 2)
+ composeRule.setContent { EditTileGridUnderTest(listState) {} }
+ composeRule.waitForIdle()
+
+ listState.onStarted(nonRemovableTile, DragType.Move)
+
+ // Tile is being dragged, it should be replaced with a placeholder
+ composeRule.onNodeWithContentDescription("tileA").assertDoesNotExist()
+
+ // Remove drop zone should not appear
+ composeRule.onNodeWithText("Remove").assertDoesNotExist()
+
+ // Drop tile outside of the grid
+ listState.movedOutOfBounds()
+ listState.onDrop()
+
+ // Tile A is still in the grid
+ composeRule.assertGridContainsExactly(CURRENT_TILES_GRID_TEST_TAG, listOf("tileA"))
+ }
+
+ @Test
fun draggedTile_shouldChangePosition() {
var tiles by mutableStateOf(TestEditTiles)
val listState = EditTileListState(tiles, columns = 4, largeTilesSpan = 2)
@@ -113,7 +153,11 @@ class DragAndDropTest : SysuiTestCase() {
}
composeRule.waitForIdle()
- listState.onStarted(TestEditTiles[0], DragType.Add)
+ listState.onStarted(TestEditTiles[0], DragType.Move)
+
+ // Remove drop zone should appear
+ composeRule.onNodeWithText("Remove").assertExists()
+
listState.onTargeting(1, false)
listState.onDrop()
@@ -141,7 +185,11 @@ class DragAndDropTest : SysuiTestCase() {
}
composeRule.waitForIdle()
- listState.onStarted(TestEditTiles[0], DragType.Add)
+ listState.onStarted(TestEditTiles[0], DragType.Move)
+
+ // Remove drop zone should appear
+ composeRule.onNodeWithText("Remove").assertExists()
+
listState.movedOutOfBounds()
listState.onDrop()
@@ -169,7 +217,11 @@ class DragAndDropTest : SysuiTestCase() {
}
composeRule.waitForIdle()
- listState.onStarted(createEditTile("tile_new"), DragType.Add)
+ listState.onStarted(createEditTile("tile_new", isRemovable = false), DragType.Add)
+
+ // Remove drop zone should appear
+ composeRule.onNodeWithText("Remove").assertExists()
+
// Insert after tileD, which is at index 4
// [ a ] [ b ] [ c ] [ empty ]
// [ tile d ] [ e ]
@@ -193,7 +245,10 @@ class DragAndDropTest : SysuiTestCase() {
private const val CURRENT_TILES_GRID_TEST_TAG = "CurrentTilesGrid"
private const val AVAILABLE_TILES_GRID_TEST_TAG = "AvailableTilesGrid"
- private fun createEditTile(tileSpec: String): SizedTile<EditTileViewModel> {
+ private fun createEditTile(
+ tileSpec: String,
+ isRemovable: Boolean = true,
+ ): SizedTile<EditTileViewModel> {
return SizedTileImpl(
EditTileViewModel(
tileSpec = TileSpec.create(tileSpec),
@@ -205,7 +260,8 @@ class DragAndDropTest : SysuiTestCase() {
label = AnnotatedString(tileSpec),
appName = null,
isCurrent = true,
- availableEditActions = emptySet(),
+ availableEditActions =
+ if (isRemovable) setOf(AvailableEditActions.REMOVE) else emptySet(),
category = TileCategory.UNKNOWN,
),
getWidth(tileSpec),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/panels/ui/compose/EditModeTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/panels/ui/compose/EditModeTest.kt
index 9d4a425c678b..acb441c3765d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/panels/ui/compose/EditModeTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/panels/ui/compose/EditModeTest.kt
@@ -23,6 +23,7 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
+import androidx.compose.ui.test.doubleClick
import androidx.compose.ui.test.junit4.ComposeContentTestRule
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.test.onAllNodesWithText
@@ -30,6 +31,7 @@ import androidx.compose.ui.test.onFirst
import androidx.compose.ui.test.onNodeWithContentDescription
import androidx.compose.ui.test.onNodeWithText
import androidx.compose.ui.test.performClick
+import androidx.compose.ui.test.performTouchInput
import androidx.compose.ui.text.AnnotatedString
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
@@ -40,6 +42,7 @@ import com.android.systemui.common.shared.model.Icon
import com.android.systemui.qs.panels.shared.model.SizedTile
import com.android.systemui.qs.panels.shared.model.SizedTileImpl
import com.android.systemui.qs.panels.ui.compose.infinitegrid.DefaultEditTileGrid
+import com.android.systemui.qs.panels.ui.viewmodel.AvailableEditActions
import com.android.systemui.qs.panels.ui.viewmodel.EditTileViewModel
import com.android.systemui.qs.pipeline.shared.TileSpec
import com.android.systemui.qs.shared.model.TileCategory
@@ -53,8 +56,8 @@ class EditModeTest : SysuiTestCase() {
@get:Rule val composeRule = createComposeRule()
@Composable
- private fun EditTileGridUnderTest() {
- var tiles by remember { mutableStateOf(TestEditTiles) }
+ private fun EditTileGridUnderTest(sizedTiles: List<SizedTile<EditTileViewModel>>) {
+ var tiles by remember { mutableStateOf(sizedTiles) }
val (currentTiles, otherTiles) = tiles.partition { it.tile.isCurrent }
val listState = EditTileListState(currentTiles, columns = 4, largeTilesSpan = 2)
@@ -65,7 +68,7 @@ class EditModeTest : SysuiTestCase() {
columns = 4,
largeTilesSpan = 4,
modifier = Modifier.fillMaxSize(),
- onAddTile = { tiles = tiles.add(it) },
+ onAddTile = { spec, _ -> tiles = tiles.add(spec) },
onRemoveTile = { tiles = tiles.remove(it) },
onSetTiles = {},
onResize = { _, _ -> },
@@ -77,7 +80,7 @@ class EditModeTest : SysuiTestCase() {
@Test
fun clickAvailableTile_shouldAdd() {
- composeRule.setContent { EditTileGridUnderTest() }
+ composeRule.setContent { EditTileGridUnderTest(TestEditTiles) }
composeRule.waitForIdle()
composeRule.onNodeWithContentDescription("tileF").performClick() // Tap to add
@@ -93,7 +96,7 @@ class EditModeTest : SysuiTestCase() {
@Test
fun clickRemoveTarget_shouldRemoveSelection() {
- composeRule.setContent { EditTileGridUnderTest() }
+ composeRule.setContent { EditTileGridUnderTest(TestEditTiles) }
composeRule.waitForIdle()
// Selects first "tileA", i.e. the one in the current grid
@@ -110,6 +113,36 @@ class EditModeTest : SysuiTestCase() {
)
}
+ @Test
+ fun selectNonRemovableTile_removeTargetShouldHide() {
+ val nonRemovableTile = createEditTile("tileA", isRemovable = false)
+ composeRule.setContent { EditTileGridUnderTest(listOf(nonRemovableTile)) }
+ composeRule.waitForIdle()
+
+ // Selects first "tileA", i.e. the one in the current grid
+ composeRule.onAllNodesWithText("tileA").onFirst().performClick()
+
+ // Assert the remove target isn't shown
+ composeRule.onNodeWithText("Remove").assertDoesNotExist()
+ }
+
+ @Test
+ fun placementMode_shouldRepositionTile() {
+ composeRule.setContent { EditTileGridUnderTest(TestEditTiles) }
+ composeRule.waitForIdle()
+
+ // Double tap first "tileA", i.e. the one in the current grid
+ composeRule.onAllNodesWithText("tileA").onFirst().performTouchInput { doubleClick() }
+
+ // Tap on tileE to position tileA in its spot
+ composeRule.onAllNodesWithText("tileE").onFirst().performClick()
+
+ // Assert tileA moved to tileE's position
+ composeRule.assertCurrentTilesGridContainsExactly(
+ listOf("tileB", "tileC", "tileD_large", "tileE", "tileA")
+ )
+ }
+
private fun ComposeContentTestRule.assertCurrentTilesGridContainsExactly(specs: List<String>) =
assertGridContainsExactly(CURRENT_TILES_GRID_TEST_TAG, specs)
@@ -148,6 +181,7 @@ class EditModeTest : SysuiTestCase() {
private fun createEditTile(
tileSpec: String,
isCurrent: Boolean = true,
+ isRemovable: Boolean = true,
): SizedTile<EditTileViewModel> {
return SizedTileImpl(
EditTileViewModel(
@@ -160,7 +194,8 @@ class EditModeTest : SysuiTestCase() {
label = AnnotatedString(tileSpec),
appName = null,
isCurrent = isCurrent,
- availableEditActions = emptySet(),
+ availableEditActions =
+ if (isRemovable) setOf(AvailableEditActions.REMOVE) else emptySet(),
category = TileCategory.UNKNOWN,
),
getWidth(tileSpec),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/panels/ui/compose/ResizingTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/panels/ui/compose/ResizingTest.kt
index 5e76000cc7f0..274c44cef949 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/panels/ui/compose/ResizingTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/panels/ui/compose/ResizingTest.kt
@@ -69,7 +69,7 @@ class ResizingTest : SysuiTestCase() {
columns = 4,
largeTilesSpan = 4,
modifier = Modifier.fillMaxSize(),
- onAddTile = {},
+ onAddTile = { _, _ -> },
onRemoveTile = {},
onSetTiles = {},
onResize = onResize,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDetailsContentControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDetailsContentControllerTest.java
index e4a4953063bb..e949c8a10c9c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDetailsContentControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDetailsContentControllerTest.java
@@ -62,7 +62,6 @@ import android.view.WindowManager;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
-import com.android.app.viewcapture.ViewCaptureAwareWindowManager;
import com.android.internal.logging.UiEventLogger;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.settingslib.wifi.WifiUtils;
@@ -162,7 +161,7 @@ public class InternetDetailsContentControllerTest extends SysuiTestCase {
@Mock
InternetDetailsContentController.InternetDialogCallback mInternetDialogCallback;
@Mock
- private ViewCaptureAwareWindowManager mWindowManager;
+ private WindowManager mWindowManager;
@Mock
private ToastFactory mToastFactory;
@Mock
@@ -234,9 +233,8 @@ public class InternetDetailsContentControllerTest extends SysuiTestCase {
mSubscriptionManager, mTelephonyManager, mWifiManager,
mConnectivityManager, mHandler, mExecutor, mBroadcastDispatcher,
mock(KeyguardUpdateMonitor.class), mGlobalSettings, mKeyguardStateController,
- mWindowManager, mToastFactory, mWorkerHandler,
- mCarrierConfigTracker, mLocationController, mDialogTransitionAnimator,
- mWifiStateWorker, mFlags);
+ mWindowManager, mToastFactory, mWorkerHandler, mCarrierConfigTracker,
+ mLocationController, mDialogTransitionAnimator, mWifiStateWorker, mFlags);
mSubscriptionManager.addOnSubscriptionsChangedListener(mExecutor,
mInternetDetailsContentController.mOnSubscriptionsChangedListener);
mInternetDetailsContentController.onStart(mInternetDialogCallback, true);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/reardisplay/RearDisplayCoreStartableTest.kt b/packages/SystemUI/tests/src/com/android/systemui/reardisplay/RearDisplayCoreStartableTest.kt
index 997cf417fe10..f4d0c26f12ee 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/reardisplay/RearDisplayCoreStartableTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/reardisplay/RearDisplayCoreStartableTest.kt
@@ -18,9 +18,11 @@ package com.android.systemui.reardisplay
import android.hardware.devicestate.feature.flags.Flags.FLAG_DEVICE_STATE_RDM_V2
import android.hardware.display.rearDisplay
+import android.os.fakeExecutorHandler
import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
import android.view.Display
+import android.view.accessibility.accessibilityManager
import androidx.test.filters.SmallTest
import com.android.keyguard.keyguardUpdateMonitor
import com.android.systemui.SysuiTestCase
@@ -62,6 +64,8 @@ class RearDisplayCoreStartableTest : SysuiTestCase() {
kosmos.rearDisplayInnerDialogDelegateFactory,
kosmos.testScope,
kosmos.keyguardUpdateMonitor,
+ kosmos.accessibilityManager,
+ kosmos.fakeExecutorHandler,
)
@Before
@@ -69,7 +73,7 @@ class RearDisplayCoreStartableTest : SysuiTestCase() {
whenever(kosmos.rearDisplay.flags).thenReturn(Display.FLAG_REAR)
whenever(kosmos.rearDisplay.displayAdjustments)
.thenReturn(mContext.display.displayAdjustments)
- whenever(kosmos.rearDisplayInnerDialogDelegateFactory.create(any(), any()))
+ whenever(kosmos.rearDisplayInnerDialogDelegateFactory.create(any(), any(), any()))
.thenReturn(mockDelegate)
whenever(mockDelegate.createDialog()).thenReturn(mockDialog)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/reardisplay/RearDisplayInnerDialogDelegateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/reardisplay/RearDisplayInnerDialogDelegateTest.kt
index fc7661666825..477e5babdcc3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/reardisplay/RearDisplayInnerDialogDelegateTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/reardisplay/RearDisplayInnerDialogDelegateTest.kt
@@ -17,7 +17,10 @@
package com.android.systemui.reardisplay
import android.testing.TestableLooper
+import android.view.View
+import android.widget.Button
import android.widget.SeekBar
+import android.widget.TextView
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.haptics.msdl.msdlPlayer
@@ -28,6 +31,7 @@ import com.android.systemui.res.R
import com.android.systemui.statusbar.phone.systemUIDialogDotFactory
import com.android.systemui.testKosmos
import com.android.systemui.util.time.systemClock
+import com.google.common.truth.Truth.assertThat
import junit.framework.Assert.assertFalse
import junit.framework.Assert.assertTrue
import org.junit.Test
@@ -49,6 +53,7 @@ class RearDisplayInnerDialogDelegateTest : SysuiTestCase() {
RearDisplayInnerDialogDelegate(
kosmos.systemUIDialogDotFactory,
mContext,
+ false /* touchExplorationEnabled */,
kosmos.vibratorHelper,
kosmos.msdlPlayer,
kosmos.systemClock,
@@ -68,6 +73,7 @@ class RearDisplayInnerDialogDelegateTest : SysuiTestCase() {
RearDisplayInnerDialogDelegate(
kosmos.systemUIDialogDotFactory,
mContext,
+ false /* touchExplorationEnabled */,
kosmos.vibratorHelper,
kosmos.msdlPlayer,
kosmos.systemClock,
@@ -78,6 +84,9 @@ class RearDisplayInnerDialogDelegateTest : SysuiTestCase() {
.apply {
show()
val seekbar = findViewById<SeekBar>(R.id.seekbar)
+ assertThat(seekbar.visibility).isEqualTo(View.VISIBLE)
+ assertThat(findViewById<TextView>(R.id.seekbar_instructions).visibility)
+ .isEqualTo(View.VISIBLE)
seekbar.progress = 50
seekbar.progress = 100
verify(mockCallback).run()
@@ -90,6 +99,7 @@ class RearDisplayInnerDialogDelegateTest : SysuiTestCase() {
RearDisplayInnerDialogDelegate(
kosmos.systemUIDialogDotFactory,
mContext,
+ false /* touchExplorationEnabled */,
kosmos.vibratorHelper,
kosmos.msdlPlayer,
kosmos.systemClock,
@@ -118,4 +128,33 @@ class RearDisplayInnerDialogDelegateTest : SysuiTestCase() {
// Progress is reset
verify(mockSeekbar).setProgress(eq(0))
}
+
+ @Test
+ fun testTouchExplorationEnabled() {
+ val mockCallback = mock<Runnable>()
+
+ RearDisplayInnerDialogDelegate(
+ kosmos.systemUIDialogDotFactory,
+ mContext,
+ true /* touchExplorationEnabled */,
+ kosmos.vibratorHelper,
+ kosmos.msdlPlayer,
+ kosmos.systemClock,
+ ) {
+ mockCallback.run()
+ }
+ .createDialog()
+ .apply {
+ show()
+ assertThat(findViewById<SeekBar>(R.id.seekbar).visibility).isEqualTo(View.GONE)
+ assertThat(findViewById<TextView>(R.id.seekbar_instructions).visibility)
+ .isEqualTo(View.GONE)
+
+ val cancelButton = findViewById<Button>(R.id.cancel_button)
+ assertThat(cancelButton.visibility).isEqualTo(View.VISIBLE)
+
+ cancelButton.performClick()
+ verify(mockCallback).run()
+ }
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyboardShortcutListSearchTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyboardShortcutListSearchTest.java
index 8045a13ff9be..07204ee814d2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyboardShortcutListSearchTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyboardShortcutListSearchTest.java
@@ -34,6 +34,7 @@ import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.utils.windowmanager.WindowManagerProvider;
import com.google.android.material.bottomsheet.BottomSheetDialog;
@@ -61,6 +62,7 @@ public class KeyboardShortcutListSearchTest extends SysuiTestCase {
@Mock private BottomSheetDialog mBottomSheetDialog;
@Mock WindowManager mWindowManager;
@Mock Handler mHandler;
+ @Mock WindowManagerProvider mWindowManagerProvider;
@Before
public void setUp() {
@@ -77,7 +79,7 @@ public class KeyboardShortcutListSearchTest extends SysuiTestCase {
public void toggle_isShowingTrue_instanceShouldBeNull() {
when(mBottomSheetDialog.isShowing()).thenReturn(true);
- mKeyboardShortcutListSearch.toggle(mContext, DEVICE_ID);
+ mKeyboardShortcutListSearch.toggle(mContext, DEVICE_ID, mWindowManagerProvider);
assertThat(mKeyboardShortcutListSearch.sInstance).isNull();
}
@@ -86,7 +88,7 @@ public class KeyboardShortcutListSearchTest extends SysuiTestCase {
public void toggle_isShowingFalse_showKeyboardShortcuts() {
when(mBottomSheetDialog.isShowing()).thenReturn(false);
- mKeyboardShortcutListSearch.toggle(mContext, DEVICE_ID);
+ mKeyboardShortcutListSearch.toggle(mContext, DEVICE_ID, mWindowManagerProvider);
verify(mWindowManager).requestAppKeyboardShortcuts(any(), anyInt());
verify(mWindowManager).requestImeKeyboardShortcuts(any(), anyInt());
@@ -96,7 +98,7 @@ public class KeyboardShortcutListSearchTest extends SysuiTestCase {
public void requestAppKeyboardShortcuts_callback_sanitisesIcons() {
KeyboardShortcutGroup group = createKeyboardShortcutGroupForIconTests();
- mKeyboardShortcutListSearch.toggle(mContext, DEVICE_ID);
+ mKeyboardShortcutListSearch.toggle(mContext, DEVICE_ID, mWindowManagerProvider);
ArgumentCaptor<WindowManager.KeyboardShortcutsReceiver> callbackCaptor =
ArgumentCaptor.forClass(WindowManager.KeyboardShortcutsReceiver.class);
@@ -114,7 +116,7 @@ public class KeyboardShortcutListSearchTest extends SysuiTestCase {
public void requestImeKeyboardShortcuts_callback_sanitisesIcons() {
KeyboardShortcutGroup group = createKeyboardShortcutGroupForIconTests();
- mKeyboardShortcutListSearch.toggle(mContext, DEVICE_ID);
+ mKeyboardShortcutListSearch.toggle(mContext, DEVICE_ID, mWindowManagerProvider);
ArgumentCaptor<WindowManager.KeyboardShortcutsReceiver> callbackCaptor =
ArgumentCaptor.forClass(WindowManager.KeyboardShortcutsReceiver.class);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyboardShortcutsReceiverTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyboardShortcutsReceiverTest.java
index 2cb9791cc159..0bd9b29a1a37 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyboardShortcutsReceiverTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyboardShortcutsReceiverTest.java
@@ -36,6 +36,7 @@ import com.android.systemui.SysuiTestCase;
import com.android.systemui.flags.FakeFeatureFlags;
import com.android.systemui.flags.Flags;
import com.android.systemui.shared.recents.utilities.Utilities;
+import com.android.systemui.utils.windowmanager.WindowManagerProvider;
import org.junit.After;
import org.junit.Before;
@@ -59,6 +60,7 @@ public class KeyboardShortcutsReceiverTest extends SysuiTestCase {
@Mock private KeyboardShortcuts mKeyboardShortcuts;
@Mock private KeyboardShortcutListSearch mKeyboardShortcutListSearch;
+ @Mock private WindowManagerProvider mWindowManagerProvider;
@Before
public void setUp() {
@@ -69,7 +71,8 @@ public class KeyboardShortcutsReceiverTest extends SysuiTestCase {
KeyboardShortcuts.sInstance = mKeyboardShortcuts;
KeyboardShortcutListSearch.sInstance = mKeyboardShortcutListSearch;
- mKeyboardShortcutsReceiver = spy(new KeyboardShortcutsReceiver(mFeatureFlags));
+ mKeyboardShortcutsReceiver = spy(new KeyboardShortcutsReceiver(mFeatureFlags,
+ mWindowManagerProvider));
}
@Before
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyboardShortcutsTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyboardShortcutsTest.java
index 20ecaf75c625..939f2b899dbe 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyboardShortcutsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyboardShortcutsTest.java
@@ -39,6 +39,7 @@ import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.utils.windowmanager.WindowManagerProvider;
import org.junit.Before;
import org.junit.Rule;
@@ -67,6 +68,7 @@ public class KeyboardShortcutsTest extends SysuiTestCase {
@Mock private Dialog mDialog;
@Mock WindowManager mWindowManager;
@Mock Handler mHandler;
+ @Mock WindowManagerProvider mWindowManagerProvider;
@Before
public void setUp() {
@@ -92,7 +94,7 @@ public class KeyboardShortcutsTest extends SysuiTestCase {
public void toggle_isShowingTrue_instanceShouldBeNull() {
when(mDialog.isShowing()).thenReturn(true);
- KeyboardShortcuts.toggle(mContext, DEVICE_ID);
+ KeyboardShortcuts.toggle(mContext, DEVICE_ID, mWindowManagerProvider);
assertThat(KeyboardShortcuts.sInstance).isNull();
}
@@ -101,7 +103,7 @@ public class KeyboardShortcutsTest extends SysuiTestCase {
public void toggle_isShowingFalse_showKeyboardShortcuts() {
when(mDialog.isShowing()).thenReturn(false);
- KeyboardShortcuts.toggle(mContext, DEVICE_ID);
+ KeyboardShortcuts.toggle(mContext, DEVICE_ID, mWindowManagerProvider);
verify(mWindowManager).requestAppKeyboardShortcuts(any(), anyInt());
verify(mWindowManager).requestImeKeyboardShortcuts(any(), anyInt());
@@ -131,7 +133,7 @@ public class KeyboardShortcutsTest extends SysuiTestCase {
@Test
public void requestAppKeyboardShortcuts_callback_sanitisesIcons() {
KeyboardShortcutGroup group = createKeyboardShortcutGroupForIconTests();
- KeyboardShortcuts.toggle(mContext, DEVICE_ID);
+ KeyboardShortcuts.toggle(mContext, DEVICE_ID, mWindowManagerProvider);
emitAppShortcuts(singletonList(group), DEVICE_ID);
@@ -142,7 +144,7 @@ public class KeyboardShortcutsTest extends SysuiTestCase {
@Test
public void requestImeKeyboardShortcuts_callback_sanitisesIcons() {
KeyboardShortcutGroup group = createKeyboardShortcutGroupForIconTests();
- KeyboardShortcuts.toggle(mContext, DEVICE_ID);
+ KeyboardShortcuts.toggle(mContext, DEVICE_ID, mWindowManagerProvider);
emitImeShortcuts(singletonList(group), DEVICE_ID);
@@ -153,7 +155,7 @@ public class KeyboardShortcutsTest extends SysuiTestCase {
@Test
public void onImeAndAppShortcutsReceived_appShortcutsNull_doesNotCrash() {
KeyboardShortcutGroup group = createKeyboardShortcutGroupForIconTests();
- KeyboardShortcuts.toggle(mContext, DEVICE_ID);
+ KeyboardShortcuts.toggle(mContext, DEVICE_ID, mWindowManagerProvider);
emitImeShortcuts(singletonList(group), DEVICE_ID);
emitAppShortcuts(/* groups= */ null, DEVICE_ID);
@@ -162,7 +164,7 @@ public class KeyboardShortcutsTest extends SysuiTestCase {
@Test
public void onImeAndAppShortcutsReceived_imeShortcutsNull_doesNotCrash() {
KeyboardShortcutGroup group = createKeyboardShortcutGroupForIconTests();
- KeyboardShortcuts.toggle(mContext, DEVICE_ID);
+ KeyboardShortcuts.toggle(mContext, DEVICE_ID, mWindowManagerProvider);
emitAppShortcuts(singletonList(group), DEVICE_ID);
emitImeShortcuts(/* groups= */ null, DEVICE_ID);
@@ -170,7 +172,7 @@ public class KeyboardShortcutsTest extends SysuiTestCase {
@Test
public void onImeAndAppShortcutsReceived_bothNull_doesNotCrash() {
- KeyboardShortcuts.toggle(mContext, DEVICE_ID);
+ KeyboardShortcuts.toggle(mContext, DEVICE_ID, mWindowManagerProvider);
emitImeShortcuts(/* groups= */ null, DEVICE_ID);
emitAppShortcuts(/* groups= */ null, DEVICE_ID);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
index 2ea4e7f67b3c..eae23e70027b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
@@ -78,6 +78,8 @@ import com.android.systemui.statusbar.notification.collection.EntryAdapter;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntryAdapter;
import com.android.systemui.statusbar.notification.collection.coordinator.VisualStabilityCoordinator;
+import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider;
+import com.android.systemui.statusbar.notification.headsup.HeadsUpManager;
import com.android.systemui.statusbar.notification.headsup.PinnedStatus;
import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier;
import com.android.systemui.statusbar.notification.promoted.PromotedNotificationUi;
@@ -582,7 +584,7 @@ public class ExpandableNotificationRowTest extends SysuiTestCase {
public void testIconScrollXAfterTranslationAndReset() throws Exception {
ExpandableNotificationRow group = mNotificationTestHelper.createGroup();
- group.setDismissUsingRowTranslationX(false);
+ group.setDismissUsingRowTranslationX(false, false);
group.setTranslation(50);
assertEquals(50, -group.getEntry().getIcons().getShelfIcon().getScrollX());
@@ -1023,6 +1025,8 @@ public class ExpandableNotificationRowTest extends SysuiTestCase {
mock(NotificationIconStyleProvider.class),
mock(VisualStabilityCoordinator.class),
mock(NotificationActionClickManager.class),
+ mock(HighPriorityProvider.class),
+ mock(HeadsUpManager.class),
entry);
row.setEntryAdapter(entryAdapter);
} else {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.kt
index cf8278eb8ac6..82082cc778b6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2022 The Android Open Source Project
+ * Copyright (C) 2025 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.
@@ -38,9 +38,13 @@ import com.android.internal.widget.NotificationExpandButton
import com.android.systemui.SysuiTestCase
import com.android.systemui.statusbar.notification.FeedbackIcon
import com.android.systemui.statusbar.notification.collection.EntryAdapter
+import com.android.systemui.statusbar.notification.collection.EntryAdapterFactory
import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import com.android.systemui.statusbar.notification.collection.msgStyleBubbleableFullPerson
+import com.android.systemui.statusbar.notification.people.peopleNotificationIdentifier
import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier
import com.android.systemui.statusbar.notification.shared.NotificationBundleUi
+import com.android.systemui.testKosmos
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
@@ -51,7 +55,6 @@ import org.junit.After
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
-import org.mockito.Mock
import org.mockito.Mockito
import org.mockito.Mockito.anyBoolean
import org.mockito.Mockito.anyInt
@@ -67,10 +70,12 @@ import org.mockito.MockitoAnnotations.initMocks
@RunWith(AndroidJUnit4::class)
@TestableLooper.RunWithLooper
class NotificationContentViewTest : SysuiTestCase() {
+ private val kosmos = testKosmos()
+
+ private val factory: EntryAdapterFactory = kosmos.entryAdapterFactory
private lateinit var row: ExpandableNotificationRow
private lateinit var fakeParent: ViewGroup
- @Mock private lateinit var mPeopleNotificationIdentifier: PeopleNotificationIdentifier
private val testableResources = mContext.getOrCreateTestableResources()
private val contractedHeight =
@@ -82,24 +87,19 @@ class NotificationContentViewTest : SysuiTestCase() {
fun setup() {
initMocks(this)
fakeParent =
- spy(FrameLayout(mContext, /* attrs= */ null).also { it.visibility = View.GONE })
- val mockEntry = createMockNotificationEntry()
- val mockEntryAdapter = createMockNotificationEntryAdapter()
+ spy(FrameLayout(mContext, /* attrs= */ null)).also { it.visibility = View.GONE }
+ val entry = kosmos.msgStyleBubbleableFullPerson
+ val mockEntryAdapter = factory.create(entry)
row =
spy(
when (NotificationBundleUi.isEnabled) {
true -> {
- ExpandableNotificationRow(
- mContext,
- /* attrs= */ null,
- UserHandle.CURRENT
- ).apply {
- entryAdapter = mockEntryAdapter
- }
+ ExpandableNotificationRow(mContext, /* attrs= */ null, UserHandle.CURRENT)
+ .apply { entryAdapter = mockEntryAdapter }
}
false -> {
- ExpandableNotificationRow(mContext, /* attrs= */ null, mockEntry).apply {
- entryLegacy = mockEntry
+ ExpandableNotificationRow(mContext, /* attrs= */ null, entry).apply {
+ entryLegacy = entry
}
}
}
@@ -406,11 +406,11 @@ class NotificationContentViewTest : SysuiTestCase() {
fun setExpandedChild_notShowBubbleButton_marginTargetBottomMarginShouldNotChange() {
// Given: bottom margin of actionListMarginTarget is notificationContentMargin
// Bubble button should not be shown for the given NotificationEntry
- val mockNotificationEntry = createMockNotificationEntry()
+ val mockNotificationEntry = kosmos.msgStyleBubbleableFullPerson
val mockContainingNotification = createMockContainingNotification(mockNotificationEntry)
val actionListMarginTarget =
spy(createLinearLayoutWithBottomMargin(notificationContentMargin))
- val mockExpandedChild = createMockExpandedChild(mockNotificationEntry)
+ val mockExpandedChild = createMockExpandedChild()
whenever(
mockExpandedChild.findViewById<LinearLayout>(
R.id.notification_action_list_margin_target
@@ -434,11 +434,11 @@ class NotificationContentViewTest : SysuiTestCase() {
fun setExpandedChild_showBubbleButton_marginTargetBottomMarginShouldChangeToZero() {
// Given: bottom margin of actionListMarginTarget is notificationContentMargin
// Bubble button should be shown for the given NotificationEntry
- val mockNotificationEntry = createMockNotificationEntry()
+ val mockNotificationEntry = kosmos.msgStyleBubbleableFullPerson
val mockContainingNotification = createMockContainingNotification(mockNotificationEntry)
val actionListMarginTarget =
spy(createLinearLayoutWithBottomMargin(notificationContentMargin))
- val mockExpandedChild = createMockExpandedChild(mockNotificationEntry)
+ val mockExpandedChild = createMockExpandedChild()
whenever(
mockExpandedChild.findViewById<LinearLayout>(
R.id.notification_action_list_margin_target
@@ -463,11 +463,11 @@ class NotificationContentViewTest : SysuiTestCase() {
@DisableFlags(android.app.Flags.FLAG_NOTIFICATIONS_REDESIGN_TEMPLATES)
fun onNotificationUpdated_notShowBubbleButton_marginTargetBottomMarginShouldNotChange() {
// Given: bottom margin of actionListMarginTarget is notificationContentMargin
- val mockNotificationEntry = createMockNotificationEntry()
+ val mockNotificationEntry = kosmos.msgStyleBubbleableFullPerson
val mockContainingNotification = createMockContainingNotification(mockNotificationEntry)
val actionListMarginTarget =
spy(createLinearLayoutWithBottomMargin(notificationContentMargin))
- val mockExpandedChild = createMockExpandedChild(mockNotificationEntry)
+ val mockExpandedChild = createMockExpandedChild()
whenever(
mockExpandedChild.findViewById<LinearLayout>(
R.id.notification_action_list_margin_target
@@ -482,7 +482,7 @@ class NotificationContentViewTest : SysuiTestCase() {
// When: call NotificationContentView.onNotificationUpdated() to update the
// NotificationEntry, which should not show bubble button
- view.onNotificationUpdated(createMockNotificationEntry())
+ view.onNotificationUpdated(kosmos.msgStyleBubbleableFullPerson)
// Then: bottom margin of actionListMarginTarget should not change, still be 20
assertEquals(notificationContentMargin, getMarginBottom(actionListMarginTarget))
@@ -492,11 +492,11 @@ class NotificationContentViewTest : SysuiTestCase() {
@DisableFlags(android.app.Flags.FLAG_NOTIFICATIONS_REDESIGN_TEMPLATES)
fun onNotificationUpdated_showBubbleButton_marginTargetBottomMarginShouldChangeToZero() {
// Given: bottom margin of actionListMarginTarget is notificationContentMargin
- val mockNotificationEntry = createMockNotificationEntry()
+ val mockNotificationEntry = kosmos.msgStyleBubbleableFullPerson
val mockContainingNotification = createMockContainingNotification(mockNotificationEntry)
val actionListMarginTarget =
spy(createLinearLayoutWithBottomMargin(notificationContentMargin))
- val mockExpandedChild = createMockExpandedChild(mockNotificationEntry)
+ val mockExpandedChild = createMockExpandedChild()
whenever(
mockExpandedChild.findViewById<LinearLayout>(
R.id.notification_action_list_margin_target
@@ -510,7 +510,7 @@ class NotificationContentViewTest : SysuiTestCase() {
// When: call NotificationContentView.onNotificationUpdated() to update the
// NotificationEntry, which should show bubble button
- view.onNotificationUpdated(createMockNotificationEntry(/*true*/ ))
+ view.onNotificationUpdated(kosmos.msgStyleBubbleableFullPerson)
// Then: no bubble yet
assertEquals(notificationContentMargin, getMarginBottom(actionListMarginTarget))
@@ -615,15 +615,17 @@ class NotificationContentViewTest : SysuiTestCase() {
private fun createMockContainingNotification(notificationEntry: NotificationEntry) =
mock<ExpandableNotificationRow>().apply {
- whenever(this.entry).thenReturn(notificationEntry)
+ if (!NotificationBundleUi.isEnabled) {
+ whenever(this.entryLegacy).thenReturn(notificationEntry)
+ }
whenever(this.context).thenReturn(mContext)
whenever(this.bubbleClickListener).thenReturn(View.OnClickListener {})
- whenever(this.entryAdapter).thenReturn(createMockNotificationEntryAdapter())
+ whenever(this.entryAdapter).thenReturn(factory.create(notificationEntry))
}
private fun createMockNotificationEntry() =
mock<NotificationEntry>().apply {
- whenever(mPeopleNotificationIdentifier.getPeopleNotificationType(this))
+ whenever(kosmos.peopleNotificationIdentifier.getPeopleNotificationType(this))
.thenReturn(PeopleNotificationIdentifier.TYPE_FULL_PERSON)
whenever(this.bubbleMetadata).thenReturn(mock())
val sbnMock: StatusBarNotification = mock()
@@ -632,7 +634,8 @@ class NotificationContentViewTest : SysuiTestCase() {
whenever(sbnMock.user).thenReturn(userMock)
}
- private fun createMockNotificationEntryAdapter() = mock<EntryAdapter>()
+ private fun createMockNotificationEntryAdapter() =
+ mock<EntryAdapter>()
private fun createLinearLayoutWithBottomMargin(bottomMargin: Int): LinearLayout {
val outerLayout = LinearLayout(mContext)
@@ -643,7 +646,7 @@ class NotificationContentViewTest : SysuiTestCase() {
return innerLayout
}
- private fun createMockExpandedChild(notificationEntry: NotificationEntry) =
+ private fun createMockExpandedChild() =
spy(createViewWithHeight(expandedHeight)).apply {
whenever(this.findViewById<ImageView>(R.id.bubble_button)).thenReturn(mock())
whenever(this.findViewById<View>(R.id.actions_container)).thenReturn(mock())
@@ -664,9 +667,16 @@ class NotificationContentViewTest : SysuiTestCase() {
val height = if (isSystemExpanded) expandedHeight else contractedHeight
doReturn(height).whenever(row).intrinsicHeight
- return spy(NotificationContentView(mContext, /* attrs= */ null))
+ return NotificationContentView(mContext, /* attrs= */ null)
.apply {
- initialize(mPeopleNotificationIdentifier, mock(), mock(), mock(), mock(), mock())
+ initialize(
+ kosmos.peopleNotificationIdentifier,
+ mock(),
+ mock(),
+ mock(),
+ mock(),
+ mock(),
+ )
setContainingNotification(row)
setHeights(
/* smallHeight= */ contractedHeight,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java
index 3061842c386c..2c800bd87ef5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java
@@ -89,8 +89,16 @@ import com.android.systemui.res.R;
import com.android.systemui.shade.ShadeController;
import com.android.systemui.statusbar.RankingBuilder;
import com.android.systemui.statusbar.SbnBuilder;
+import com.android.systemui.statusbar.notification.NotificationActivityStarter;
+import com.android.systemui.statusbar.notification.collection.EntryAdapter;
+import com.android.systemui.statusbar.notification.collection.EntryAdapterFactoryImpl;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
+import com.android.systemui.statusbar.notification.collection.coordinator.VisualStabilityCoordinator;
+import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider;
+import com.android.systemui.statusbar.notification.headsup.HeadsUpManager;
+import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier;
+import com.android.systemui.statusbar.notification.row.icon.NotificationIconStyleProvider;
import com.android.systemui.wmshell.BubblesManager;
import com.android.systemui.wmshell.BubblesTestActivity;
@@ -127,6 +135,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase {
private NotificationChannel mConversationChannel;
private StatusBarNotification mSbn;
private NotificationEntry mEntry;
+ private EntryAdapter mEntryAdapter;
private StatusBarNotification mBubbleSbn;
private NotificationEntry mBubbleEntry;
@Mock
@@ -228,7 +237,21 @@ public class NotificationConversationInfoTest extends SysuiTestCase {
notification.extras.putParcelable(EXTRA_BUILDER_APPLICATION_INFO, applicationInfo);
mSbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME, 0, null, TEST_UID, 0,
notification, UserHandle.CURRENT, null, 0);
- mEntry = new NotificationEntryBuilder().setSbn(mSbn).setShortcutInfo(mShortcutInfo).build();
+ mEntry = new NotificationEntryBuilder().setSbn(mSbn).setShortcutInfo(mShortcutInfo)
+ .updateRanking(rankingBuilder -> {
+ rankingBuilder.setChannel(mNotificationChannel);
+ })
+ .build();
+ mEntryAdapter = new EntryAdapterFactoryImpl(
+ mock(NotificationActivityStarter.class),
+ mock(MetricsLogger.class),
+ mock(PeopleNotificationIdentifier.class),
+ mock(NotificationIconStyleProvider.class),
+ mock(VisualStabilityCoordinator.class),
+ mock(NotificationActionClickManager.class),
+ mock(HighPriorityProvider.class),
+ mock(HeadsUpManager.class)
+ ).create(mEntry);
PendingIntent bubbleIntent = PendingIntent.getActivity(mContext, 0,
new Intent(mContext, BubblesTestActivity.class),
@@ -264,9 +287,10 @@ public class NotificationConversationInfoTest extends SysuiTestCase {
mMockINotificationManager,
mOnUserInteractionCallback,
TEST_PACKAGE_NAME,
- mNotificationChannel,
mEntry,
- mBubbleMetadata,
+ mEntryAdapter,
+ mEntry.getRanking(),
+ mSbn,
null,
null,
mIconFactory,
@@ -367,9 +391,10 @@ public class NotificationConversationInfoTest extends SysuiTestCase {
mMockINotificationManager,
mOnUserInteractionCallback,
TEST_PACKAGE_NAME,
- mNotificationChannel,
- entry,
- mBubbleMetadata,
+ mEntry,
+ mEntryAdapter,
+ mEntry.getRanking(),
+ mSbn,
null,
null,
mIconFactory,
@@ -404,9 +429,10 @@ public class NotificationConversationInfoTest extends SysuiTestCase {
mMockINotificationManager,
mOnUserInteractionCallback,
TEST_PACKAGE_NAME,
- mNotificationChannel,
mEntry,
- mBubbleMetadata,
+ mEntryAdapter,
+ mEntry.getRanking(),
+ mSbn,
null,
(View v, Intent intent) -> {
latch.countDown();
@@ -444,9 +470,10 @@ public class NotificationConversationInfoTest extends SysuiTestCase {
mMockINotificationManager,
mOnUserInteractionCallback,
TEST_PACKAGE_NAME,
- mNotificationChannel,
mEntry,
- mBubbleMetadata,
+ mEntryAdapter,
+ mEntry.getRanking(),
+ mSbn,
(View v, NotificationChannel c, int appUid) -> {
assertEquals(mConversationChannel, c);
latch.countDown();
@@ -483,9 +510,10 @@ public class NotificationConversationInfoTest extends SysuiTestCase {
mMockINotificationManager,
mOnUserInteractionCallback,
TEST_PACKAGE_NAME,
- mNotificationChannel,
mEntry,
- mBubbleMetadata,
+ mEntryAdapter,
+ mEntry.getRanking(),
+ mSbn,
(View v, NotificationChannel c, int appUid) -> {
assertEquals(mNotificationChannel, c);
latch.countDown();
@@ -553,6 +581,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase {
mConversationChannel.setImportance(IMPORTANCE_HIGH);
mConversationChannel.setImportantConversation(false);
mConversationChannel.setAllowBubbles(false);
+ mSbn.getNotification().setBubbleMetadata(null);
mNotificationInfo.bindNotification(
mShortcutManager,
mMockPackageManager,
@@ -561,9 +590,10 @@ public class NotificationConversationInfoTest extends SysuiTestCase {
mMockINotificationManager,
mOnUserInteractionCallback,
TEST_PACKAGE_NAME,
- mNotificationChannel,
mEntry,
- null,
+ mEntryAdapter,
+ mEntry.getRanking(),
+ mSbn,
null,
null,
mIconFactory,
@@ -583,6 +613,7 @@ public class NotificationConversationInfoTest extends SysuiTestCase {
mConversationChannel.setImportance(IMPORTANCE_HIGH);
mConversationChannel.setImportantConversation(false);
mConversationChannel.setAllowBubbles(false);
+ mSbn.getNotification().setBubbleMetadata(null);
mNotificationInfo.bindNotification(
mShortcutManager,
mMockPackageManager,
@@ -591,9 +622,10 @@ public class NotificationConversationInfoTest extends SysuiTestCase {
mMockINotificationManager,
mOnUserInteractionCallback,
TEST_PACKAGE_NAME,
- mNotificationChannel,
mEntry,
- null,
+ mEntryAdapter,
+ mEntry.getRanking(),
+ mSbn,
null,
null,
mIconFactory,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerWithScenesTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerWithScenesTest.kt
index 10de86644015..49ebc8c83ea7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerWithScenesTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerWithScenesTest.kt
@@ -63,13 +63,16 @@ import com.android.systemui.statusbar.NotificationEntryHelper
import com.android.systemui.statusbar.NotificationPresenter
import com.android.systemui.statusbar.notification.AssistantFeedbackController
import com.android.systemui.statusbar.notification.NotificationActivityStarter
+import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider
+import com.android.systemui.statusbar.notification.collection.provider.mockHighPriorityProvider
import com.android.systemui.statusbar.notification.domain.interactor.activeNotificationsInteractor
import com.android.systemui.statusbar.notification.headsup.mockHeadsUpManager
import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier
import com.android.systemui.statusbar.notification.promoted.domain.interactor.PackageDemotionInteractor
import com.android.systemui.statusbar.notification.row.icon.appIconProvider
import com.android.systemui.statusbar.notification.row.icon.notificationIconStyleProvider
+import com.android.systemui.statusbar.notification.shared.NotificationBundleUi
import com.android.systemui.statusbar.notification.stack.NotificationListContainer
import com.android.systemui.statusbar.notificationLockscreenUserManager
import com.android.systemui.statusbar.policy.deviceProvisionedController
@@ -233,14 +236,22 @@ class NotificationGutsManagerWithScenesTest : SysuiTestCase() {
assertEquals(View.INVISIBLE.toLong(), guts.visibility.toLong())
executor.runAllReady()
verify(guts).openControls(any<Int>(), any<Int>(), any<Boolean>(), any<Runnable>())
- verify(headsUpManager).setGutsShown(realRow!!.entry, true)
+ if (NotificationBundleUi.isEnabled) {
+ verify(kosmos.mockHeadsUpManager).setGutsShown(any<NotificationEntry>(), eq(true))
+ } else {
+ verify(headsUpManager).setGutsShown(realRow!!.entry, true)
+ }
assertEquals(View.VISIBLE.toLong(), guts.visibility.toLong())
gutsManager.closeAndSaveGuts(false, false, true, 0, 0, false)
verify(guts)
.closeControls(any<Boolean>(), any<Boolean>(), any<Int>(), any<Int>(), any<Boolean>())
verify(row, times(1)).setGutsView(any())
executor.runAllReady()
- verify(headsUpManager).setGutsShown(realRow.entry, false)
+ if (NotificationBundleUi.isEnabled) {
+ verify(kosmos.mockHeadsUpManager).setGutsShown(any<NotificationEntry>(), eq(false))
+ } else {
+ verify(headsUpManager).setGutsShown(realRow!!.entry, false)
+ }
}
@Test
@@ -385,16 +396,23 @@ class NotificationGutsManagerWithScenesTest : SysuiTestCase() {
@Throws(Exception::class)
fun testInitializeNotificationInfoView_highPriority() {
val notificationInfoView = mock<NotificationInfo>()
- val row = spy(helper.createRow())
- val entry = row.entry
+ val row = createTestNotificationRow()
+ val entry = row!!.entry
NotificationEntryHelper.modifyRanking(entry)
.setUserSentiment(Ranking.USER_SENTIMENT_NEGATIVE)
.setImportance(NotificationManager.IMPORTANCE_HIGH)
.build()
- whenever(row.canViewBeDismissed()).thenReturn(true)
+
whenever(highPriorityProvider.isHighPriority(entry)).thenReturn(true)
+ whenever(kosmos.mockHighPriorityProvider.isHighPriority(entry)).thenReturn(true)
val statusBarNotification = entry.sbn
- gutsManager.initializeNotificationInfo(row, notificationInfoView)
+
+ gutsManager.initializeNotificationInfo(
+ row,
+ statusBarNotification,
+ entry.ranking,
+ notificationInfoView,
+ )
verify(notificationInfoView)
.bindNotification(
any<PackageManager>(),
@@ -405,15 +423,17 @@ class NotificationGutsManagerWithScenesTest : SysuiTestCase() {
eq(channelEditorDialogController),
eq(packageDemotionInteractor),
eq(statusBarNotification.packageName),
- any<NotificationChannel>(),
- eq(entry),
+ eq(entry.ranking),
+ eq(statusBarNotification),
+ if (NotificationBundleUi.isEnabled) eq(null) else eq(entry),
+ if (NotificationBundleUi.isEnabled) eq(row.entryAdapter) else eq(null),
any<NotificationInfo.OnSettingsClickListener>(),
any<NotificationInfo.OnAppSettingsClickListener>(),
any<NotificationInfo.OnFeedbackClickListener>(),
any<UiEventLogger>(),
eq(true),
eq(false),
- eq(true),
+ eq(false),
eq(true),
eq(assistantFeedbackController),
any<MetricsLogger>(),
@@ -425,14 +445,19 @@ class NotificationGutsManagerWithScenesTest : SysuiTestCase() {
@Throws(Exception::class)
fun testInitializeNotificationInfoView_PassesAlongProvisionedState() {
val notificationInfoView = mock<NotificationInfo>()
- val row = spy(helper.createRow())
+ val row = createTestNotificationRow()
NotificationEntryHelper.modifyRanking(row.entry)
.setUserSentiment(Ranking.USER_SENTIMENT_NEGATIVE)
.build()
- whenever(row.canViewBeDismissed()).thenReturn(true)
val statusBarNotification = row.entry.sbn
val entry = row.entry
- gutsManager.initializeNotificationInfo(row, notificationInfoView)
+
+ gutsManager.initializeNotificationInfo(
+ row,
+ statusBarNotification,
+ entry.ranking,
+ notificationInfoView,
+ )
verify(notificationInfoView)
.bindNotification(
any<PackageManager>(),
@@ -443,15 +468,17 @@ class NotificationGutsManagerWithScenesTest : SysuiTestCase() {
eq(channelEditorDialogController),
eq(packageDemotionInteractor),
eq(statusBarNotification.packageName),
- any<NotificationChannel>(),
- eq(entry),
+ eq(entry.ranking),
+ eq(statusBarNotification),
+ if (NotificationBundleUi.isEnabled) eq(null) else eq(entry),
+ if (NotificationBundleUi.isEnabled) eq(row.entryAdapter) else eq(null),
any<NotificationInfo.OnSettingsClickListener>(),
any<NotificationInfo.OnAppSettingsClickListener>(),
any<NotificationInfo.OnFeedbackClickListener>(),
any<UiEventLogger>(),
eq(true),
eq(false),
- eq(true), /* wasShownHighPriority */
+ eq(false), /* wasShownHighPriority */
eq(false),
eq(assistantFeedbackController),
any<MetricsLogger>(),
@@ -470,7 +497,15 @@ class NotificationGutsManagerWithScenesTest : SysuiTestCase() {
whenever(row.canViewBeDismissed()).thenReturn(true)
val statusBarNotification = row.entry.sbn
val entry = row.entry
- gutsManager.initializeNotificationInfo(row, notificationInfoView)
+ val entryAdapter = kosmos.entryAdapterFactory.create(entry)
+ row.entryAdapter = entryAdapter
+
+ gutsManager.initializeNotificationInfo(
+ row,
+ statusBarNotification,
+ entry.ranking,
+ notificationInfoView,
+ )
verify(notificationInfoView)
.bindNotification(
any<PackageManager>(),
@@ -481,8 +516,10 @@ class NotificationGutsManagerWithScenesTest : SysuiTestCase() {
eq(channelEditorDialogController),
eq(packageDemotionInteractor),
eq(statusBarNotification.packageName),
- any<NotificationChannel>(),
- eq(entry),
+ eq(entry.ranking),
+ eq(statusBarNotification),
+ if (NotificationBundleUi.isEnabled) eq(null) else eq(entry),
+ if (NotificationBundleUi.isEnabled) eq(entryAdapter) else eq(null),
any<NotificationInfo.OnSettingsClickListener>(),
any<NotificationInfo.OnAppSettingsClickListener>(),
any<NotificationInfo.OnFeedbackClickListener>(),
@@ -497,7 +534,7 @@ class NotificationGutsManagerWithScenesTest : SysuiTestCase() {
)
}
- private fun createTestNotificationRow(): ExpandableNotificationRow? {
+ private fun createTestNotificationRow(): ExpandableNotificationRow {
val nb =
Notification.Builder(mContext, testNotificationChannel.id)
.setContentTitle("foo")
@@ -505,16 +542,10 @@ class NotificationGutsManagerWithScenesTest : SysuiTestCase() {
.setColor(Color.RED)
.setFlag(Notification.FLAG_CAN_COLORIZE, true)
.setSmallIcon(R.drawable.sym_def_app_icon)
- return try {
- val row = helper.createRow(nb.build())
- NotificationEntryHelper.modifyRanking(row.entry)
- .setChannel(testNotificationChannel)
- .build()
- row
- } catch (_: Exception) {
- Assert.fail()
- null
- }
+ val row = helper.createRow(nb.build())
+ NotificationEntryHelper.modifyRanking(row.entry).setChannel(testNotificationChannel).build()
+ row.entryAdapter = kosmos.entryAdapterFactory.create(row.entry)
+ return row
}
private fun setIsLockscreenOrShadeVisible(isVisible: Boolean) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
index 8fb2a245921a..6a9a485151de 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
@@ -734,6 +734,7 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase {
ExpandableNotificationRow row = mock(ExpandableNotificationRow.class);
NotificationEntry entry = mock(NotificationEntry.class);
when(row.getEntry()).thenReturn(entry);
+ when(row.getEntryLegacy()).thenReturn(entry);
when(entry.isAmbient()).thenReturn(false);
EntryAdapter entryAdapter = mock(EntryAdapter.class);
when(entryAdapter.isAmbient()).thenReturn(false);
@@ -753,6 +754,7 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase {
ExpandableNotificationRow row = mock(ExpandableNotificationRow.class);
NotificationEntry entry = mock(NotificationEntry.class);
when(row.getEntry()).thenReturn(entry);
+ when(row.getEntryLegacy()).thenReturn(entry);
when(entry.isAmbient()).thenReturn(true);
EntryAdapter entryAdapter = mock(EntryAdapter.class);
when(entryAdapter.isAmbient()).thenReturn(true);
@@ -772,6 +774,7 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase {
ExpandableNotificationRow row = mock(ExpandableNotificationRow.class);
NotificationEntry entry = mock(NotificationEntry.class);
when(row.getEntry()).thenReturn(entry);
+ when(row.getEntryLegacy()).thenReturn(entry);
when(entry.isAmbient()).thenReturn(false);
EntryAdapter entryAdapter = mock(EntryAdapter.class);
when(entryAdapter.isAmbient()).thenReturn(false);
@@ -1384,6 +1387,7 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase {
NotificationEntry entry = mock(NotificationEntry.class);
when(entry.isSeenInShade()).thenReturn(true);
ExpandableNotificationRow row = mock(ExpandableNotificationRow.class);
+ when(row.getEntryLegacy()).thenReturn(entry);
when(row.getEntry()).thenReturn(entry);
// WHEN we generate an add event
@@ -1440,6 +1444,7 @@ public class NotificationStackScrollLayoutTest extends SysuiTestCase {
NotificationEntry entry = mock(NotificationEntry.class);
when(row.canViewBeCleared()).thenReturn(true);
when(row.getEntry()).thenReturn(entry);
+ when(row.getEntryLegacy()).thenReturn(entry);
when(entry.isClearable()).thenReturn(true);
EntryAdapter entryAdapter = mock(EntryAdapter.class);
when(entryAdapter.isClearable()).thenReturn(true);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
index a3616d20e11f..0c5cbc299aee 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
@@ -28,8 +28,8 @@ import static android.provider.Settings.Global.HEADS_UP_NOTIFICATIONS_ENABLED;
import static android.provider.Settings.Global.HEADS_UP_ON;
import static com.android.systemui.Flags.FLAG_KEYBOARD_SHORTCUT_HELPER_REWRITE;
-import static com.android.systemui.Flags.FLAG_LIGHT_REVEAL_MIGRATION;
import static com.android.systemui.flags.Flags.SHORTCUT_LIST_SEARCH_LAYOUT;
+import static com.android.systemui.shared.Flags.FLAG_AMBIENT_AOD;
import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;
import static com.android.systemui.statusbar.StatusBarState.SHADE;
import static com.android.systemui.statusbar.phone.CentralSurfaces.MSG_DISMISS_KEYBOARD_SHORTCUTS_MENU;
@@ -93,7 +93,6 @@ import android.view.WindowMetrics;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
-import com.android.app.viewcapture.ViewCaptureAwareWindowManager;
import com.android.compose.animation.scene.ObservableTransitionState;
import com.android.internal.colorextraction.ColorExtractor;
import com.android.internal.logging.UiEventLogger;
@@ -214,6 +213,7 @@ import com.android.systemui.util.settings.FakeGlobalSettings;
import com.android.systemui.util.settings.FakeSettings;
import com.android.systemui.util.settings.SystemSettings;
import com.android.systemui.util.time.FakeSystemClock;
+import com.android.systemui.utils.windowmanager.WindowManagerProvider;
import com.android.systemui.volume.VolumeComponent;
import com.android.systemui.wallet.controller.QuickAccessWalletController;
import com.android.wm.shell.bubbles.Bubbles;
@@ -242,7 +242,7 @@ import javax.inject.Provider;
@SmallTest
@RunWith(AndroidJUnit4.class)
@RunWithLooper(setAsMainLooper = true)
-@EnableFlags(FLAG_LIGHT_REVEAL_MIGRATION)
+@EnableFlags(FLAG_AMBIENT_AOD)
public class CentralSurfacesImplTest extends SysuiTestCase {
private static final DeviceState FOLD_STATE_FOLDED = new DeviceState(
@@ -372,9 +372,10 @@ public class CentralSurfacesImplTest extends SysuiTestCase {
@Mock private GlanceableHubContainerController mGlanceableHubContainerController;
@Mock private EmergencyGestureIntentFactory mEmergencyGestureIntentFactory;
@Mock private NotificationSettingsInteractor mNotificationSettingsInteractor;
- @Mock private ViewCaptureAwareWindowManager mViewCaptureAwareWindowManager;
@Mock private StatusBarLongPressGestureDetector mStatusBarLongPressGestureDetector;
@Mock private QuickAccessWalletController mQuickAccessWalletController;
+ @Mock private WindowManager mWindowManager;
+ @Mock private WindowManagerProvider mWindowManagerProvider;
private ShadeController mShadeController;
private final FakeSystemClock mFakeSystemClock = new FakeSystemClock();
private final FakeGlobalSettings mFakeGlobalSettings = new FakeGlobalSettings();
@@ -642,8 +643,9 @@ public class CentralSurfacesImplTest extends SysuiTestCase {
mBrightnessMirrorShowingRepository,
mGlanceableHubContainerController,
mEmergencyGestureIntentFactory,
- mViewCaptureAwareWindowManager,
- mQuickAccessWalletController
+ mQuickAccessWalletController,
+ mWindowManager,
+ mWindowManagerProvider
);
mScreenLifecycle.addObserver(mCentralSurfaces.mScreenObserver);
mCentralSurfaces.initShadeVisibilityListener();
@@ -1363,15 +1365,13 @@ public class CentralSurfacesImplTest extends SysuiTestCase {
private void switchToScreenSize(int widthDp, int heightDp) {
WindowMetrics windowMetrics = Mockito.mock(WindowMetrics.class);
- WindowManager windowManager = Mockito.mock(WindowManager.class);
Configuration configuration = new Configuration();
configuration.densityDpi = DisplayMetrics.DENSITY_DEFAULT;
mContext.getOrCreateTestableResources().overrideConfiguration(configuration);
when(windowMetrics.getBounds()).thenReturn(new Rect(0, 0, widthDp, heightDp));
- when(windowManager.getCurrentWindowMetrics()).thenReturn(windowMetrics);
- mContext.addMockSystemService(WindowManager.class, windowManager);
+ when(mWindowManager.getCurrentWindowMetrics()).thenReturn(windowMetrics);
}
/**
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt
index 68f66611c981..574b2c010a37 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt
@@ -471,6 +471,51 @@ class PhoneStatusBarViewControllerTest : SysuiTestCase() {
}
@Test
+ @EnableFlags(ShadeWindowGoesAround.FLAG_NAME)
+ fun onTouch_withMouseOnEndSideIcons_flagOn_propagatedToShadeDisplayPolicy() {
+ val view = createViewMock()
+ InstrumentationRegistry.getInstrumentation().runOnMainSync {
+ controller = createAndInitController(view)
+ }
+ val event = getActionUpEventFromSource(InputDevice.SOURCE_MOUSE)
+
+ val statusContainer = view.requireViewById<View>(R.id.system_icons)
+ statusContainer.dispatchTouchEvent(event)
+
+ verify(statusBarTouchShadeDisplayPolicy).onStatusBarTouched(eq(event), any())
+ }
+
+ @Test
+ @EnableFlags(ShadeWindowGoesAround.FLAG_NAME)
+ fun onTouch_withMouseOnStartSideIcons_flagOn_propagatedToShadeDisplayPolicy() {
+ val view = createViewMock()
+ InstrumentationRegistry.getInstrumentation().runOnMainSync {
+ controller = createAndInitController(view)
+ }
+ val event = getActionUpEventFromSource(InputDevice.SOURCE_MOUSE)
+
+ val statusContainer = view.requireViewById<View>(R.id.status_bar_start_side_content)
+ statusContainer.dispatchTouchEvent(event)
+
+ verify(statusBarTouchShadeDisplayPolicy).onStatusBarTouched(eq(event), any())
+ }
+
+ @Test
+ @DisableFlags(ShadeWindowGoesAround.FLAG_NAME)
+ fun onTouch_withMouseOnSystemIcons_flagOff_notPropagatedToShadeDisplayPolicy() {
+ val view = createViewMock()
+ InstrumentationRegistry.getInstrumentation().runOnMainSync {
+ controller = createAndInitController(view)
+ }
+ val event = getActionUpEventFromSource(InputDevice.SOURCE_MOUSE)
+
+ val statusContainer = view.requireViewById<View>(R.id.system_icons)
+ statusContainer.dispatchTouchEvent(event)
+
+ verify(statusBarTouchShadeDisplayPolicy, never()).onStatusBarTouched(eq(event), any())
+ }
+
+ @Test
fun shadeIsExpandedOnStatusIconMouseClick() {
val view = createViewMock()
InstrumentationRegistry.getInstrumentation().runOnMainSync {
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 0d99c0e8cab8..320a87e7db17 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
@@ -176,6 +176,7 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase {
private FakeKeyguardStateController mKeyguardStateController =
spy(new FakeKeyguardStateController());
private final FakeExecutor mExecutor = new FakeExecutor(new FakeSystemClock());
+ private final static String TEST_REASON = "reason";
@Mock
private ViewRootImpl mViewRootImpl;
@@ -272,14 +273,15 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase {
mStatusBarKeyguardViewManager.dismissWithAction(
action, cancelAction, false /* afterKeyguardGone */);
verify(mPrimaryBouncerInteractor).setDismissAction(eq(action), eq(cancelAction));
- verify(mPrimaryBouncerInteractor).show(eq(true));
+ verify(mPrimaryBouncerInteractor).show(eq(true),
+ eq("StatusBarKeyguardViewManager#dismissWithAction"));
}
@Test
public void showPrimaryBouncer_onlyWhenShowing() {
mStatusBarKeyguardViewManager.hide(0 /* startTime */, 0 /* fadeoutDuration */);
- mStatusBarKeyguardViewManager.showPrimaryBouncer(true /* scrimmed */);
- verify(mPrimaryBouncerInteractor, never()).show(anyBoolean());
+ mStatusBarKeyguardViewManager.showPrimaryBouncer(true /* scrimmed */, TEST_REASON);
+ verify(mPrimaryBouncerInteractor, never()).show(anyBoolean(), eq(TEST_REASON));
verify(mDeviceEntryInteractor, never()).attemptDeviceEntry();
verify(mSceneInteractor, never()).changeScene(any(), any());
}
@@ -289,8 +291,8 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase {
mStatusBarKeyguardViewManager.hide(0 /* startTime */, 0 /* fadeoutDuration */);
when(mKeyguardSecurityModel.getSecurityMode(anyInt())).thenReturn(
KeyguardSecurityModel.SecurityMode.Password);
- mStatusBarKeyguardViewManager.showPrimaryBouncer(true /* scrimmed */);
- verify(mPrimaryBouncerInteractor, never()).show(anyBoolean());
+ mStatusBarKeyguardViewManager.showPrimaryBouncer(true /* scrimmed */, TEST_REASON);
+ verify(mPrimaryBouncerInteractor, never()).show(anyBoolean(), eq(TEST_REASON));
verify(mDeviceEntryInteractor, never()).attemptDeviceEntry();
verify(mSceneInteractor, never()).changeScene(any(), any());
}
@@ -298,8 +300,8 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase {
@Test
@DisableSceneContainer
public void showBouncer_showsTheBouncer() {
- mStatusBarKeyguardViewManager.showPrimaryBouncer(true /* scrimmed */);
- verify(mPrimaryBouncerInteractor).show(eq(true));
+ mStatusBarKeyguardViewManager.showPrimaryBouncer(true /* scrimmed */, TEST_REASON);
+ verify(mPrimaryBouncerInteractor).show(eq(true), eq(TEST_REASON));
}
@Test
@@ -344,19 +346,20 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase {
public void onPanelExpansionChanged_showsBouncerWhenSwiping() {
mKeyguardStateController.setCanDismissLockScreen(false);
mStatusBarKeyguardViewManager.onPanelExpansionChanged(EXPANSION_EVENT);
- verify(mPrimaryBouncerInteractor).show(eq(false));
+ verify(mPrimaryBouncerInteractor).show(eq(false),
+ eq("StatusBarKeyguardViewManager#onPanelExpansionChanged"));
// But not when it's already visible
reset(mPrimaryBouncerInteractor);
when(mPrimaryBouncerInteractor.isFullyShowing()).thenReturn(true);
mStatusBarKeyguardViewManager.onPanelExpansionChanged(EXPANSION_EVENT);
- verify(mPrimaryBouncerInteractor, never()).show(eq(false));
+ verify(mPrimaryBouncerInteractor, never()).show(eq(false), eq(TEST_REASON));
// Or animating away
reset(mPrimaryBouncerInteractor);
when(mPrimaryBouncerInteractor.isAnimatingAway()).thenReturn(true);
mStatusBarKeyguardViewManager.onPanelExpansionChanged(EXPANSION_EVENT);
- verify(mPrimaryBouncerInteractor, never()).show(eq(false));
+ verify(mPrimaryBouncerInteractor, never()).show(eq(false), eq(TEST_REASON));
}
@Test
@@ -546,7 +549,7 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase {
when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true);
// WHEN showBouncer is called
- mStatusBarKeyguardViewManager.showPrimaryBouncer(true);
+ mStatusBarKeyguardViewManager.showPrimaryBouncer(true, TEST_REASON);
// THEN alt bouncer should be hidden
verify(mAlternateBouncerInteractor).hide();
@@ -571,10 +574,10 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase {
// WHEN showGenericBouncer is called
final boolean scrimmed = true;
- mStatusBarKeyguardViewManager.showBouncer(scrimmed);
+ mStatusBarKeyguardViewManager.showBouncer(scrimmed, TEST_REASON);
// THEN regular bouncer is shown
- verify(mPrimaryBouncerInteractor).show(eq(scrimmed));
+ verify(mPrimaryBouncerInteractor).show(eq(scrimmed), eq(TEST_REASON));
}
@Test
@@ -835,7 +838,7 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase {
when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(false);
// WHEN request to show primary bouncer
- mStatusBarKeyguardViewManager.showPrimaryBouncer(true);
+ mStatusBarKeyguardViewManager.showPrimaryBouncer(true, TEST_REASON);
// THEN the scrim isn't updated from StatusBarKeyguardViewManager
verify(mCentralSurfaces, never()).updateScrimController();
@@ -847,9 +850,9 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase {
public void testShowBouncerOrKeyguard_needsFullScreen() {
when(mKeyguardSecurityModel.getSecurityMode(anyInt())).thenReturn(
KeyguardSecurityModel.SecurityMode.SimPin);
- mStatusBarKeyguardViewManager.showBouncerOrKeyguard(false, false);
+ mStatusBarKeyguardViewManager.showBouncerOrKeyguard(false, false, TEST_REASON);
verify(mCentralSurfaces).hideKeyguard();
- verify(mPrimaryBouncerInteractor).show(true);
+ verify(mPrimaryBouncerInteractor).show(true, TEST_REASON);
}
@Test
@@ -859,7 +862,7 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase {
when(mKeyguardSecurityModel.getSecurityMode(anyInt())).thenReturn(
KeyguardSecurityModel.SecurityMode.SimPin);
// Returning false means unable to show the bouncer
- when(mPrimaryBouncerInteractor.show(true)).thenReturn(false);
+ when(mPrimaryBouncerInteractor.show(true, TEST_REASON)).thenReturn(false);
when(mKeyguardTransitionInteractor.getTransitionState().getValue().getTo())
.thenReturn(KeyguardState.LOCKSCREEN);
mStatusBarKeyguardViewManager.onStartedWakingUp();
@@ -868,8 +871,8 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase {
// Advance past reattempts
mStatusBarKeyguardViewManager.setAttemptsToShowBouncer(10);
- mStatusBarKeyguardViewManager.showBouncerOrKeyguard(false, false);
- verify(mPrimaryBouncerInteractor).show(true);
+ mStatusBarKeyguardViewManager.showBouncerOrKeyguard(false, false, TEST_REASON);
+ verify(mPrimaryBouncerInteractor).show(true, TEST_REASON);
verify(mCentralSurfaces).showKeyguard();
}
@@ -884,7 +887,7 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase {
reset(mCentralSurfaces);
reset(mPrimaryBouncerInteractor);
mStatusBarKeyguardViewManager.showBouncerOrKeyguard(
- /* hideBouncerWhenShowing= */true, false);
+ /* hideBouncerWhenShowing= */true, false, TEST_REASON);
verify(mCentralSurfaces).showKeyguard();
verify(mPrimaryBouncerInteractor).hide();
}
@@ -897,9 +900,9 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase {
when(mKeyguardSecurityModel.getSecurityMode(anyInt())).thenReturn(
KeyguardSecurityModel.SecurityMode.SimPin);
when(mPrimaryBouncerInteractor.isFullyShowing()).thenReturn(true);
- mStatusBarKeyguardViewManager.showBouncerOrKeyguard(false, isFalsingReset);
+ mStatusBarKeyguardViewManager.showBouncerOrKeyguard(false, isFalsingReset, TEST_REASON);
verify(mCentralSurfaces, never()).hideKeyguard();
- verify(mPrimaryBouncerInteractor).show(true);
+ verify(mPrimaryBouncerInteractor).show(true, TEST_REASON);
}
@Test
@@ -909,24 +912,24 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase {
when(mKeyguardSecurityModel.getSecurityMode(anyInt())).thenReturn(
KeyguardSecurityModel.SecurityMode.SimPin);
when(mPrimaryBouncerInteractor.isFullyShowing()).thenReturn(true);
- mStatusBarKeyguardViewManager.showBouncerOrKeyguard(false, isFalsingReset);
+ mStatusBarKeyguardViewManager.showBouncerOrKeyguard(false, isFalsingReset, TEST_REASON);
verify(mCentralSurfaces, never()).hideKeyguard();
// Do not refresh the full screen bouncer if the call is from falsing
- verify(mPrimaryBouncerInteractor, never()).show(true);
+ verify(mPrimaryBouncerInteractor, never()).show(true, TEST_REASON);
}
@Test
@EnableSceneContainer
public void showBouncer_attemptDeviceEntry() {
- mStatusBarKeyguardViewManager.showBouncer(false);
+ mStatusBarKeyguardViewManager.showBouncer(false, TEST_REASON);
verify(mDeviceEntryInteractor).attemptDeviceEntry();
}
@Test
@EnableSceneContainer
public void showPrimaryBouncer() {
- mStatusBarKeyguardViewManager.showPrimaryBouncer(false);
+ mStatusBarKeyguardViewManager.showPrimaryBouncer(false, TEST_REASON);
verify(mSceneInteractor).showOverlay(eq(Overlays.Bouncer), anyString());
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionKairosAdapterTelephonySmokeTests.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionKairosAdapterTelephonySmokeTests.kt
new file mode 100644
index 000000000000..5695df5c307d
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionKairosAdapterTelephonySmokeTests.kt
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2025 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.pipeline.mobile.data.repository.prod
+
+import androidx.test.filters.SmallTest
+import com.android.systemui.activated
+import com.android.systemui.kairos.ExperimentalKairosApi
+import com.android.systemui.kairos.launchKairosNetwork
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionRepository
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.test.runCurrent
+import org.mockito.Mockito
+
+@OptIn(ExperimentalKairosApi::class, ExperimentalCoroutinesApi::class)
+@SmallTest
+class MobileConnectionKairosAdapterTelephonySmokeTests : MobileConnectionTelephonySmokeTestsBase() {
+
+ var job: Job? = null
+ val kairosNetwork = testScope.backgroundScope.launchKairosNetwork()
+
+ override fun recreateRepo(): MobileConnectionRepository {
+ lateinit var adapter: MobileConnectionRepositoryKairosAdapter
+ job?.cancel()
+ Mockito.clearInvocations(telephonyManager)
+ job =
+ testScope.backgroundScope.launch {
+ kairosNetwork.activateSpec {
+ val repo = activated {
+ MobileConnectionRepositoryKairosImpl(
+ MobileConnectionRepositoryTest.SUB_1_ID,
+ context,
+ subscriptionModel.toState(),
+ MobileConnectionRepositoryTest.DEFAULT_NAME_MODEL,
+ MobileConnectionRepositoryTest.SEP,
+ connectivityManager,
+ telephonyManager,
+ systemUiCarrierConfig,
+ fakeBroadcastDispatcher,
+ mobileMappings,
+ testDispatcher,
+ logger,
+ tableLogger,
+ flags,
+ )
+ }
+ adapter = MobileConnectionRepositoryKairosAdapter(repo, systemUiCarrierConfig)
+ Unit
+ }
+ }
+ testScope.runCurrent()
+ return adapter
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryKairosAdapterTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryKairosAdapterTest.kt
new file mode 100644
index 000000000000..0cb7c1eea268
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryKairosAdapterTest.kt
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2025 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.pipeline.mobile.data.repository.prod
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.activated
+import com.android.systemui.kairos.ExperimentalKairosApi
+import com.android.systemui.kairos.launchKairosNetwork
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionRepository
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.test.runCurrent
+import org.junit.runner.RunWith
+import org.mockito.Mockito
+
+@OptIn(ExperimentalKairosApi::class, ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class MobileConnectionRepositoryKairosAdapterTest : MobileConnectionRepositoryTest() {
+
+ var job: Job? = null
+ val kairosNetwork = testScope.backgroundScope.launchKairosNetwork()
+
+ override fun recreateRepo(): MobileConnectionRepository {
+ lateinit var adapter: MobileConnectionRepositoryKairosAdapter
+ job?.cancel()
+ Mockito.clearInvocations(telephonyManager)
+ job =
+ testScope.backgroundScope.launch {
+ kairosNetwork.activateSpec {
+ val repo = activated {
+ MobileConnectionRepositoryKairosImpl(
+ SUB_1_ID,
+ context,
+ subscriptionModel.toState(),
+ DEFAULT_NAME_MODEL,
+ SEP,
+ connectivityManager,
+ telephonyManager,
+ systemUiCarrierConfig,
+ fakeBroadcastDispatcher,
+ mobileMappings,
+ testDispatcher,
+ logger,
+ tableLogger,
+ flags,
+ )
+ }
+ adapter = MobileConnectionRepositoryKairosAdapter(repo, systemUiCarrierConfig)
+ Unit
+ }
+ }
+ testScope.runCurrent() // ensure the lateinit is set
+ return adapter
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryTest.kt
index ed8be9b253ab..2636195c0021 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryTest.kt
@@ -88,6 +88,7 @@ import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionMod
import com.android.systemui.statusbar.pipeline.mobile.data.model.SystemUiCarrierConfig
import com.android.systemui.statusbar.pipeline.mobile.data.model.testCarrierConfig
import com.android.systemui.statusbar.pipeline.mobile.data.model.testCarrierConfigWithOverride
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionRepository
import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionRepository.Companion.DEFAULT_NUM_LEVELS
import com.android.systemui.statusbar.pipeline.mobile.data.repository.prod.MobileTelephonyHelpers.signalStrength
import com.android.systemui.statusbar.pipeline.mobile.data.repository.prod.MobileTelephonyHelpers.telephonyDisplayInfo
@@ -116,25 +117,49 @@ import org.mockito.kotlin.argumentCaptor
@Suppress("EXPERIMENTAL_IS_NOT_ENABLED")
@SmallTest
@RunWith(AndroidJUnit4::class)
-class MobileConnectionRepositoryTest : SysuiTestCase() {
- private lateinit var underTest: MobileConnectionRepositoryImpl
+class MobileConnectionRepositoryImplTest : MobileConnectionRepositoryTest() {
+ override fun recreateRepo(): MobileConnectionRepository =
+ MobileConnectionRepositoryImpl(
+ SUB_1_ID,
+ context,
+ subscriptionModel,
+ DEFAULT_NAME_MODEL,
+ SEP,
+ connectivityManager,
+ telephonyManager,
+ systemUiCarrierConfig,
+ fakeBroadcastDispatcher,
+ mobileMappings,
+ testDispatcher,
+ logger,
+ tableLogger,
+ flags,
+ testScope.backgroundScope,
+ )
+}
+
+abstract class MobileConnectionRepositoryTest : SysuiTestCase() {
+
+ abstract fun recreateRepo(): MobileConnectionRepository
+
+ lateinit var underTest: MobileConnectionRepository
- private val flags =
+ protected val flags =
FakeFeatureFlagsClassic().also { it.set(ROAMING_INDICATOR_VIA_DISPLAY_INFO, true) }
- @Mock private lateinit var connectivityManager: ConnectivityManager
- @Mock private lateinit var telephonyManager: TelephonyManager
- @Mock private lateinit var logger: MobileInputLogger
- @Mock private lateinit var tableLogger: TableLogBuffer
- @Mock private lateinit var context: Context
+ @Mock protected lateinit var connectivityManager: ConnectivityManager
+ @Mock protected lateinit var telephonyManager: TelephonyManager
+ @Mock protected lateinit var logger: MobileInputLogger
+ @Mock protected lateinit var tableLogger: TableLogBuffer
+ @Mock protected lateinit var context: Context
- private val mobileMappings = FakeMobileMappingsProxy()
- private val systemUiCarrierConfig = SystemUiCarrierConfig(SUB_1_ID, testCarrierConfig())
+ protected val mobileMappings = FakeMobileMappingsProxy()
+ protected val systemUiCarrierConfig = SystemUiCarrierConfig(SUB_1_ID, testCarrierConfig())
- private val testDispatcher = UnconfinedTestDispatcher()
- private val testScope = TestScope(testDispatcher)
+ protected val testDispatcher = UnconfinedTestDispatcher()
+ protected val testScope = TestScope(testDispatcher)
- private val subscriptionModel: MutableStateFlow<SubscriptionModel?> =
+ protected val subscriptionModel: MutableStateFlow<SubscriptionModel?> =
MutableStateFlow(
SubscriptionModel(
subscriptionId = SUB_1_ID,
@@ -144,28 +169,11 @@ class MobileConnectionRepositoryTest : SysuiTestCase() {
)
@Before
- fun setUp() {
+ fun setUpBase() {
MockitoAnnotations.initMocks(this)
whenever(telephonyManager.subscriptionId).thenReturn(SUB_1_ID)
- underTest =
- MobileConnectionRepositoryImpl(
- SUB_1_ID,
- context,
- subscriptionModel,
- DEFAULT_NAME_MODEL,
- SEP,
- connectivityManager,
- telephonyManager,
- systemUiCarrierConfig,
- fakeBroadcastDispatcher,
- mobileMappings,
- testDispatcher,
- logger,
- tableLogger,
- flags,
- testScope.backgroundScope,
- )
+ underTest = recreateRepo()
}
@Test
@@ -400,6 +408,7 @@ class MobileConnectionRepositoryTest : SysuiTestCase() {
fun carrierId_initialValueCaptured() =
testScope.runTest {
whenever(telephonyManager.simCarrierId).thenReturn(1234)
+ underTest = recreateRepo()
var latest: Int? = null
val job = underTest.carrierId.onEach { latest = it }.launchIn(this)
@@ -430,6 +439,8 @@ class MobileConnectionRepositoryTest : SysuiTestCase() {
@Test
fun carrierNetworkChange() =
testScope.runTest {
+ underTest = recreateRepo()
+
var latest: Boolean? = null
val job = underTest.carrierNetworkChangeActive.onEach { latest = it }.launchIn(this)
@@ -622,24 +633,7 @@ class MobileConnectionRepositoryTest : SysuiTestCase() {
flags.set(ROAMING_INDICATOR_VIA_DISPLAY_INFO, true)
// Re-create the repository, because the flag is read at init
- underTest =
- MobileConnectionRepositoryImpl(
- SUB_1_ID,
- context,
- subscriptionModel,
- DEFAULT_NAME_MODEL,
- SEP,
- connectivityManager,
- telephonyManager,
- systemUiCarrierConfig,
- fakeBroadcastDispatcher,
- mobileMappings,
- testDispatcher,
- logger,
- tableLogger,
- flags,
- testScope.backgroundScope,
- )
+ underTest = recreateRepo()
var latest: Boolean? = null
val job = underTest.isRoaming.onEach { latest = it }.launchIn(this)
@@ -671,24 +665,7 @@ class MobileConnectionRepositoryTest : SysuiTestCase() {
flags.set(ROAMING_INDICATOR_VIA_DISPLAY_INFO, false)
// Re-create the repository, because the flag is read at init
- underTest =
- MobileConnectionRepositoryImpl(
- SUB_1_ID,
- context,
- subscriptionModel,
- DEFAULT_NAME_MODEL,
- SEP,
- connectivityManager,
- telephonyManager,
- systemUiCarrierConfig,
- fakeBroadcastDispatcher,
- mobileMappings,
- testDispatcher,
- logger,
- tableLogger,
- flags,
- testScope.backgroundScope,
- )
+ underTest = recreateRepo()
var latest: Boolean? = null
val job = underTest.isRoaming.onEach { latest = it }.launchIn(this)
@@ -1441,14 +1418,14 @@ class MobileConnectionRepositoryTest : SysuiTestCase() {
}
companion object {
- private const val SUB_1_ID = 1
+ const val SUB_1_ID = 1
- private const val DEFAULT_NAME = "Fake Mobile Network"
- private val DEFAULT_NAME_MODEL = NetworkNameModel.Default(DEFAULT_NAME)
- private const val SEP = "-"
+ const val DEFAULT_NAME = "Fake Mobile Network"
+ val DEFAULT_NAME_MODEL = NetworkNameModel.Default(DEFAULT_NAME)
+ const val SEP = "-"
- private const val SPN = "testSpn"
- private const val DATA_SPN = "testDataSpn"
- private const val PLMN = "testPlmn"
+ const val SPN = "testSpn"
+ const val DATA_SPN = "testDataSpn"
+ const val PLMN = "testPlmn"
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionTelephonySmokeTests.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionTelephonySmokeTests.kt
index 6f21e795532b..caa6e21fd883 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionTelephonySmokeTests.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionTelephonySmokeTests.kt
@@ -42,6 +42,7 @@ import com.android.systemui.statusbar.pipeline.mobile.data.model.ResolvedNetwork
import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel
import com.android.systemui.statusbar.pipeline.mobile.data.model.SystemUiCarrierConfig
import com.android.systemui.statusbar.pipeline.mobile.data.model.testCarrierConfig
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionRepository
import com.android.systemui.statusbar.pipeline.mobile.data.repository.prod.MobileTelephonyHelpers.getTelephonyCallbackForType
import com.android.systemui.statusbar.pipeline.mobile.data.repository.prod.MobileTelephonyHelpers.signalStrength
import com.android.systemui.statusbar.pipeline.mobile.util.FakeMobileMappingsProxy
@@ -92,47 +93,53 @@ import org.mockito.MockitoAnnotations
*/
@Suppress("EXPERIMENTAL_IS_NOT_ENABLED")
@SmallTest
-class MobileConnectionTelephonySmokeTests : SysuiTestCase() {
- private lateinit var underTest: MobileConnectionRepositoryImpl
+class MobileConnectionTelephonySmokeTests : MobileConnectionTelephonySmokeTestsBase() {
+ override fun recreateRepo(): MobileConnectionRepository =
+ MobileConnectionRepositoryImpl(
+ SUB_1_ID,
+ context,
+ subscriptionModel,
+ DEFAULT_NAME,
+ SEP,
+ connectivityManager,
+ telephonyManager,
+ systemUiCarrierConfig,
+ fakeBroadcastDispatcher,
+ mobileMappings,
+ testDispatcher,
+ logger,
+ tableLogger,
+ flags,
+ testScope.backgroundScope,
+ )
+}
+
+abstract class MobileConnectionTelephonySmokeTestsBase : SysuiTestCase() {
+ protected lateinit var underTest: MobileConnectionRepository
- private val flags =
+ protected val flags =
FakeFeatureFlagsClassic().also { it.set(Flags.ROAMING_INDICATOR_VIA_DISPLAY_INFO, true) }
- @Mock private lateinit var connectivityManager: ConnectivityManager
- @Mock private lateinit var telephonyManager: TelephonyManager
- @Mock private lateinit var logger: MobileInputLogger
- @Mock private lateinit var tableLogger: TableLogBuffer
- @Mock private lateinit var subscriptionModel: StateFlow<SubscriptionModel?>
+ @Mock protected lateinit var connectivityManager: ConnectivityManager
+ @Mock protected lateinit var telephonyManager: TelephonyManager
+ @Mock protected lateinit var logger: MobileInputLogger
+ @Mock protected lateinit var tableLogger: TableLogBuffer
+ @Mock protected lateinit var subscriptionModel: StateFlow<SubscriptionModel?>
- private val mobileMappings = FakeMobileMappingsProxy()
- private val systemUiCarrierConfig = SystemUiCarrierConfig(SUB_1_ID, testCarrierConfig())
+ protected val mobileMappings = FakeMobileMappingsProxy()
+ protected val systemUiCarrierConfig = SystemUiCarrierConfig(SUB_1_ID, testCarrierConfig())
- private val testDispatcher = UnconfinedTestDispatcher()
- private val testScope = TestScope(testDispatcher)
+ protected val testDispatcher = UnconfinedTestDispatcher()
+ protected val testScope = TestScope(testDispatcher)
+
+ abstract fun recreateRepo(): MobileConnectionRepository
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
whenever(telephonyManager.subscriptionId).thenReturn(SUB_1_ID)
- underTest =
- MobileConnectionRepositoryImpl(
- SUB_1_ID,
- context,
- subscriptionModel,
- DEFAULT_NAME,
- SEP,
- connectivityManager,
- telephonyManager,
- systemUiCarrierConfig,
- fakeBroadcastDispatcher,
- mobileMappings,
- testDispatcher,
- logger,
- tableLogger,
- flags,
- testScope.backgroundScope,
- )
+ underTest = recreateRepo()
}
@Test
@@ -329,9 +336,9 @@ class MobileConnectionTelephonySmokeTests : SysuiTestCase() {
}
companion object {
- private const val SUB_1_ID = 1
+ const val SUB_1_ID = 1
- private val DEFAULT_NAME = NetworkNameModel.Default("default name")
- private const val SEP = "-"
+ val DEFAULT_NAME = NetworkNameModel.Default("default name")
+ const val SEP = "-"
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryKairosAdapterTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryKairosAdapterTest.kt
new file mode 100644
index 000000000000..65849ae103c4
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryKairosAdapterTest.kt
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2025 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.pipeline.mobile.data.repository.prod
+
+import android.testing.TestableLooper
+import androidx.test.filters.SmallTest
+import com.android.systemui.kairos.ExperimentalKairosApi
+import com.android.systemui.kairos.launchKairosNetwork
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeCarrierConfigRepository
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionsRepositoryKairosAdapter
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.test.runCurrent
+import org.mockito.Mockito
+import org.mockito.kotlin.mock
+
+@OptIn(ExperimentalKairosApi::class, ExperimentalCoroutinesApi::class)
+@SmallTest
+// This is required because our [SubscriptionManager.OnSubscriptionsChangedListener] uses a looper
+// to run the callback and this makes the looper place nicely with TestScope etc.
+@TestableLooper.RunWithLooper
+class MobileConnectionsRepositoryKairosAdapterTest :
+ MobileConnectionsRepositoryTest<MobileConnectionsRepositoryKairosAdapter>() {
+
+ var job: Job? = null
+ val kairosNetwork = testScope.backgroundScope.launchKairosNetwork()
+
+ override fun recreateRepo(): MobileConnectionsRepositoryKairosAdapter {
+ val carrierConfigRepo = FakeCarrierConfigRepository()
+ lateinit var connectionsRepo: MobileConnectionsRepositoryKairosImpl
+ connectionsRepo =
+ MobileConnectionsRepositoryKairosImpl(
+ connectivityRepository = connectivityRepository,
+ subscriptionManager = subscriptionManager,
+ subscriptionManagerProxy = subscriptionManagerProxy,
+ telephonyManager = telephonyManager,
+ logger = logger,
+ tableLogger = summaryLogger,
+ mobileMappingsProxy = mobileMappings,
+ broadcastDispatcher = fakeBroadcastDispatcher,
+ context = context,
+ bgDispatcher = testDispatcher,
+ mainDispatcher = testDispatcher,
+ airplaneModeRepository = airplaneModeRepository,
+ wifiRepository = wifiRepository,
+ keyguardUpdateMonitor = updateMonitor,
+ dumpManager = mock(),
+ mobileRepoFactory = {
+ MobileConnectionRepositoryKairosFactoryImpl(
+ context = context,
+ connectionsRepo = connectionsRepo,
+ logFactory = logBufferFactory,
+ carrierConfigRepo = carrierConfigRepo,
+ telephonyManager = telephonyManager,
+ mobileRepoFactory = {
+ subId,
+ mobileLogger,
+ subscriptionModel,
+ defaultNetworkName,
+ networkNameSeparator,
+ systemUiCarrierConfig,
+ telephonyManager ->
+ MobileConnectionRepositoryKairosImpl(
+ subId = subId,
+ context = context,
+ subscriptionModel = subscriptionModel,
+ defaultNetworkName = defaultNetworkName,
+ networkNameSeparator = networkNameSeparator,
+ connectivityManager = connectivityManager,
+ telephonyManager = telephonyManager,
+ systemUiCarrierConfig = systemUiCarrierConfig,
+ broadcastDispatcher = fakeBroadcastDispatcher,
+ mobileMappingsProxy = mobileMappings,
+ bgDispatcher = testDispatcher,
+ logger = logger,
+ tableLogBuffer = mobileLogger,
+ flags = flags,
+ )
+ },
+ mergedRepoFactory =
+ CarrierMergedConnectionRepositoryKairos.Factory(
+ telephonyManager,
+ wifiRepository,
+ ),
+ )
+ },
+ )
+
+ val adapter =
+ MobileConnectionsRepositoryKairosAdapter(
+ kairosRepo = connectionsRepo,
+ kairosNetwork = kairosNetwork,
+ scope = testScope.backgroundScope,
+ connectivityRepository = connectivityRepository,
+ context = context,
+ carrierConfigRepo = carrierConfigRepo,
+ )
+
+ job?.cancel()
+ Mockito.clearInvocations(telephonyManager)
+ job =
+ testScope.backgroundScope.launch {
+ kairosNetwork.activateSpec {
+ connectionsRepo.run { activate() }
+ adapter.run { activate() }
+ }
+ }
+ testScope.runCurrent() // ensure everything is activated
+ return adapter
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt
index d1d6e27332b0..c3662880c5cf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt
@@ -37,6 +37,7 @@ import android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID
import android.telephony.SubscriptionManager.PROFILE_CLASS_UNSET
import android.telephony.TelephonyCallback
import android.telephony.TelephonyCallback.ActiveDataSubscriptionIdListener
+import android.telephony.TelephonyCallback.EmergencyCallbackModeListener
import android.telephony.TelephonyManager
import android.testing.TestableLooper
import androidx.test.filters.SmallTest
@@ -58,6 +59,7 @@ import com.android.systemui.statusbar.pipeline.mobile.data.MobileInputLogger
import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel
import com.android.systemui.statusbar.pipeline.mobile.data.repository.CarrierConfigRepository
import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionRepository
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionsRepository
import com.android.systemui.statusbar.pipeline.mobile.data.repository.carrierConfigRepository
import com.android.systemui.statusbar.pipeline.mobile.data.repository.prod.FullMobileConnectionRepository.Factory.Companion.tableBufferLogName
import com.android.systemui.statusbar.pipeline.mobile.util.FakeMobileMappingsProxy
@@ -78,6 +80,7 @@ import com.android.wifitrackerlib.MergedCarrierEntry
import com.android.wifitrackerlib.WifiEntry
import com.android.wifitrackerlib.WifiPickerTracker
import com.google.common.truth.Truth.assertThat
+import java.time.Duration
import java.util.UUID
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.launchIn
@@ -93,6 +96,7 @@ import org.junit.Test
import org.mockito.ArgumentMatchers.anyInt
import org.mockito.ArgumentMatchers.anyString
import org.mockito.Mock
+import org.mockito.Mockito.atLeast
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
import org.mockito.kotlin.any
@@ -104,34 +108,311 @@ import org.mockito.kotlin.whenever
// This is required because our [SubscriptionManager.OnSubscriptionsChangedListener] uses a looper
// to run the callback and this makes the looper place nicely with TestScope etc.
@TestableLooper.RunWithLooper
-class MobileConnectionsRepositoryTest : SysuiTestCase() {
+class MobileConnectionsRepositoryImplTest :
+ MobileConnectionsRepositoryTest<MobileConnectionsRepositoryImpl>() {
+ override fun recreateRepo() =
+ MobileConnectionsRepositoryImpl(
+ connectivityRepository = connectivityRepository,
+ subscriptionManager = subscriptionManager,
+ subscriptionManagerProxy = subscriptionManagerProxy,
+ telephonyManager = telephonyManager,
+ logger = logger,
+ tableLogger = summaryLogger,
+ mobileMappingsProxy = mobileMappings,
+ broadcastDispatcher = fakeBroadcastDispatcher,
+ context = context,
+ bgDispatcher = testDispatcher,
+ scope = testScope.backgroundScope,
+ mainDispatcher = testDispatcher,
+ airplaneModeRepository = airplaneModeRepository,
+ wifiRepository = wifiRepository,
+ fullMobileRepoFactory = fullConnectionFactory,
+ keyguardUpdateMonitor = updateMonitor,
+ dumpManager = mock(),
+ )
+
+ @Test
+ fun activeDataSentBeforeSubscriptionList_subscriptionReusesActiveDataRepo() =
+ testScope.runTest {
+ val activeRepo by collectLastValue(underTest.activeMobileDataRepository)
+ collectLastValue(underTest.subscriptions)
+
+ // GIVEN active repo is updated before the subscription list updates
+ getTelephonyCallbackForType<ActiveDataSubscriptionIdListener>()
+ .onActiveDataSubscriptionIdChanged(SUB_2_ID)
+
+ assertThat(activeRepo).isNotNull()
+
+ // GIVEN the subscription list is then updated which includes the active data sub id
+ whenever(subscriptionManager.completeActiveSubscriptionInfoList)
+ .thenReturn(listOf(SUB_2))
+ getSubscriptionCallback().onSubscriptionsChanged()
+
+ // WHEN requesting a connection repository for the subscription
+ val newRepo = underTest.getRepoForSubId(SUB_2_ID)
+
+ // THEN the newly request repo has been cached and reused
+ assertThat(activeRepo).isSameInstanceAs(newRepo)
+ }
+
+ @Test
+ fun testConnectionRepository_invalidSubId_doesNotThrow() =
+ testScope.runTest {
+ underTest.getRepoForSubId(SUB_1_ID)
+ // No exception
+ }
+
+ @Test
+ fun testConnectionRepository_carrierMergedAndMobileSubs_usesCorrectRepos() =
+ testScope.runTest {
+ collectLastValue(underTest.subscriptions)
+
+ getDefaultNetworkCallback().onCapabilitiesChanged(NETWORK, WIFI_NETWORK_CAPS_CM)
+ setWifiState(isCarrierMerged = true)
+ whenever(subscriptionManager.completeActiveSubscriptionInfoList)
+ .thenReturn(listOf(SUB_1, SUB_CM))
+ getSubscriptionCallback().onSubscriptionsChanged()
+
+ val carrierMergedRepo = underTest.getRepoForSubId(SUB_CM_ID)
+ val mobileRepo = underTest.getRepoForSubId(SUB_1_ID)
+ assertThat(carrierMergedRepo.getIsCarrierMerged()).isTrue()
+ assertThat(mobileRepo.getIsCarrierMerged()).isFalse()
+ }
+
+ @Test
+ fun testSubscriptions_subNoLongerCarrierMerged_repoUpdates() =
+ testScope.runTest {
+ collectLastValue(underTest.subscriptions)
+
+ getDefaultNetworkCallback().onCapabilitiesChanged(NETWORK, WIFI_NETWORK_CAPS_CM)
+ setWifiState(isCarrierMerged = true)
+ whenever(subscriptionManager.completeActiveSubscriptionInfoList)
+ .thenReturn(listOf(SUB_1, SUB_CM))
+ getSubscriptionCallback().onSubscriptionsChanged()
+
+ val carrierMergedRepo = underTest.getRepoForSubId(SUB_CM_ID)
+ var mobileRepo = underTest.getRepoForSubId(SUB_1_ID)
+ assertThat(carrierMergedRepo.getIsCarrierMerged()).isTrue()
+ assertThat(mobileRepo.getIsCarrierMerged()).isFalse()
+
+ // WHEN the wifi network updates to be not carrier merged
+ getDefaultNetworkCallback().onCapabilitiesChanged(NETWORK, WIFI_NETWORK_CAPS_ACTIVE)
+ setWifiState(isCarrierMerged = false)
+ runCurrent()
+
+ // THEN the repos update
+ val noLongerCarrierMergedRepo = underTest.getRepoForSubId(SUB_CM_ID)
+ mobileRepo = underTest.getRepoForSubId(SUB_1_ID)
+ assertThat(noLongerCarrierMergedRepo.getIsCarrierMerged()).isFalse()
+ assertThat(mobileRepo.getIsCarrierMerged()).isFalse()
+ }
+
+ @Test
+ fun testSubscriptions_subBecomesCarrierMerged_repoUpdates() =
+ testScope.runTest {
+ collectLastValue(underTest.subscriptions)
+
+ getDefaultNetworkCallback().onCapabilitiesChanged(NETWORK, WIFI_NETWORK_CAPS_ACTIVE)
+ setWifiState(isCarrierMerged = false)
+ whenever(subscriptionManager.completeActiveSubscriptionInfoList)
+ .thenReturn(listOf(SUB_1, SUB_CM))
+ getSubscriptionCallback().onSubscriptionsChanged()
+ runCurrent()
+
+ val notYetCarrierMergedRepo = underTest.getRepoForSubId(SUB_CM_ID)
+ var mobileRepo = underTest.getRepoForSubId(SUB_1_ID)
+ assertThat(notYetCarrierMergedRepo.getIsCarrierMerged()).isFalse()
+ assertThat(mobileRepo.getIsCarrierMerged()).isFalse()
+
+ // WHEN the wifi network updates to be carrier merged
+ getDefaultNetworkCallback().onCapabilitiesChanged(NETWORK, WIFI_NETWORK_CAPS_CM)
+ setWifiState(isCarrierMerged = true)
+ runCurrent()
+
+ // THEN the repos update
+ val carrierMergedRepo = underTest.getRepoForSubId(SUB_CM_ID)
+ mobileRepo = underTest.getRepoForSubId(SUB_1_ID)
+ assertThat(carrierMergedRepo.getIsCarrierMerged()).isTrue()
+ assertThat(mobileRepo.getIsCarrierMerged()).isFalse()
+ }
+
+ @Test
+ @Ignore("b/333912012")
+ fun testConnectionCache_clearsInvalidSubscriptions() =
+ testScope.runTest {
+ collectLastValue(underTest.subscriptions)
+
+ whenever(subscriptionManager.completeActiveSubscriptionInfoList)
+ .thenReturn(listOf(SUB_1, SUB_2))
+ getSubscriptionCallback().onSubscriptionsChanged()
+
+ // Get repos to trigger caching
+ val repo1 = underTest.getRepoForSubId(SUB_1_ID)
+ val repo2 = underTest.getRepoForSubId(SUB_2_ID)
+
+ assertThat(underTest.getSubIdRepoCache())
+ .containsExactly(SUB_1_ID, repo1, SUB_2_ID, repo2)
+
+ // SUB_2 disappears
+ whenever(subscriptionManager.completeActiveSubscriptionInfoList)
+ .thenReturn(listOf(SUB_1))
+ getSubscriptionCallback().onSubscriptionsChanged()
+
+ assertThat(underTest.getSubIdRepoCache()).containsExactly(SUB_1_ID, repo1)
+ }
+
+ @Test
+ @Ignore("b/333912012")
+ fun testConnectionCache_clearsInvalidSubscriptions_includingCarrierMerged() =
+ testScope.runTest {
+ collectLastValue(underTest.subscriptions)
+
+ getDefaultNetworkCallback().onCapabilitiesChanged(NETWORK, WIFI_NETWORK_CAPS_CM)
+ setWifiState(isCarrierMerged = true)
+ whenever(subscriptionManager.completeActiveSubscriptionInfoList)
+ .thenReturn(listOf(SUB_1, SUB_2, SUB_CM))
+ getSubscriptionCallback().onSubscriptionsChanged()
+
+ // Get repos to trigger caching
+ val repo1 = underTest.getRepoForSubId(SUB_1_ID)
+ val repo2 = underTest.getRepoForSubId(SUB_2_ID)
+ val repoCarrierMerged = underTest.getRepoForSubId(SUB_CM_ID)
+
+ assertThat(underTest.getSubIdRepoCache())
+ .containsExactly(SUB_1_ID, repo1, SUB_2_ID, repo2, SUB_CM_ID, repoCarrierMerged)
+
+ // SUB_2 and SUB_CM disappear
+ whenever(subscriptionManager.completeActiveSubscriptionInfoList)
+ .thenReturn(listOf(SUB_1))
+ getSubscriptionCallback().onSubscriptionsChanged()
+
+ assertThat(underTest.getSubIdRepoCache()).containsExactly(SUB_1_ID, repo1)
+ }
+
+ /** Regression test for b/261706421 */
+ @Test
+ @Ignore("b/333912012")
+ fun testConnectionsCache_clearMultipleSubscriptionsAtOnce_doesNotThrow() =
+ testScope.runTest {
+ collectLastValue(underTest.subscriptions)
+
+ whenever(subscriptionManager.completeActiveSubscriptionInfoList)
+ .thenReturn(listOf(SUB_1, SUB_2))
+ getSubscriptionCallback().onSubscriptionsChanged()
+
+ // Get repos to trigger caching
+ val repo1 = underTest.getRepoForSubId(SUB_1_ID)
+ val repo2 = underTest.getRepoForSubId(SUB_2_ID)
+
+ assertThat(underTest.getSubIdRepoCache())
+ .containsExactly(SUB_1_ID, repo1, SUB_2_ID, repo2)
+
+ // All subscriptions disappear
+ whenever(subscriptionManager.completeActiveSubscriptionInfoList).thenReturn(listOf())
+ getSubscriptionCallback().onSubscriptionsChanged()
+
+ assertThat(underTest.getSubIdRepoCache()).isEmpty()
+ }
+
+ @Test
+ fun getRepoForSubId_activeDataSubIdIsRequestedBeforeSubscriptionsUpdate() =
+ testScope.runTest {
+ var latestActiveRepo: MobileConnectionRepository? = null
+ collectLastValue(
+ underTest.activeMobileDataSubscriptionId.filterNotNull().onEach {
+ latestActiveRepo = underTest.getRepoForSubId(it)
+ }
+ )
+
+ val latestSubscriptions by collectLastValue(underTest.subscriptions)
+
+ // Active data subscription id is sent, but no subscription change has been posted yet
+ getTelephonyCallbackForType<ActiveDataSubscriptionIdListener>()
+ .onActiveDataSubscriptionIdChanged(SUB_2_ID)
+
+ // Subscriptions list is empty
+ assertThat(latestSubscriptions).isEmpty()
+ // getRepoForSubId does not throw
+ assertThat(latestActiveRepo).isNotNull()
+ }
+
+ @Test
+ fun testConnectionsCache_keepsReposCached() =
+ testScope.runTest {
+ // Collect subscriptions to start the job
+ collectLastValue(underTest.subscriptions)
+
+ whenever(subscriptionManager.completeActiveSubscriptionInfoList)
+ .thenReturn(listOf(SUB_1))
+ getSubscriptionCallback().onSubscriptionsChanged()
+
+ val repo1_1 = underTest.getRepoForSubId(SUB_1_ID)
+
+ // All subscriptions disappear
+ whenever(subscriptionManager.completeActiveSubscriptionInfoList).thenReturn(listOf())
+ getSubscriptionCallback().onSubscriptionsChanged()
+
+ // Sub1 comes back
+ whenever(subscriptionManager.completeActiveSubscriptionInfoList)
+ .thenReturn(listOf(SUB_1))
+ getSubscriptionCallback().onSubscriptionsChanged()
+
+ val repo1_2 = underTest.getRepoForSubId(SUB_1_ID)
+
+ assertThat(repo1_1).isSameInstanceAs(repo1_2)
+ }
+
+ @Test
+ fun testConnectionsCache_doesNotDropReferencesThatHaveBeenRealized() =
+ testScope.runTest {
+ // Collect subscriptions to start the job
+ collectLastValue(underTest.subscriptions)
+
+ whenever(subscriptionManager.completeActiveSubscriptionInfoList)
+ .thenReturn(listOf(SUB_1))
+ getSubscriptionCallback().onSubscriptionsChanged()
+
+ // Client grabs a reference to a repository, but doesn't keep it around
+ underTest.getRepoForSubId(SUB_1_ID)
+
+ // All subscriptions disappear
+ whenever(subscriptionManager.completeActiveSubscriptionInfoList).thenReturn(listOf())
+ getSubscriptionCallback().onSubscriptionsChanged()
+
+ val repo1 = underTest.getRepoForSubId(SUB_1_ID)
+
+ assertThat(repo1).isNotNull()
+ }
+}
+
+abstract class MobileConnectionsRepositoryTest<T : MobileConnectionsRepository> : SysuiTestCase() {
private val kosmos = testKosmos()
- private val flags =
+ protected val flags =
FakeFeatureFlagsClassic().also { it.set(Flags.ROAMING_INDICATOR_VIA_DISPLAY_INFO, true) }
private lateinit var connectionFactory: MobileConnectionRepositoryImpl.Factory
private lateinit var carrierMergedFactory: CarrierMergedConnectionRepository.Factory
- private lateinit var fullConnectionFactory: FullMobileConnectionRepository.Factory
- private lateinit var connectivityRepository: ConnectivityRepository
- private lateinit var airplaneModeRepository: FakeAirplaneModeRepository
- private lateinit var wifiRepository: WifiRepository
+ protected lateinit var fullConnectionFactory: FullMobileConnectionRepository.Factory
+ protected lateinit var connectivityRepository: ConnectivityRepository
+ protected lateinit var airplaneModeRepository: FakeAirplaneModeRepository
+ protected lateinit var wifiRepository: WifiRepository
private lateinit var carrierConfigRepository: CarrierConfigRepository
- @Mock private lateinit var connectivityManager: ConnectivityManager
- @Mock private lateinit var subscriptionManager: SubscriptionManager
- @Mock private lateinit var telephonyManager: TelephonyManager
- @Mock private lateinit var logger: MobileInputLogger
- private val summaryLogger = logcatTableLogBuffer(kosmos, "summaryLogger")
- @Mock private lateinit var logBufferFactory: TableLogBufferFactory
- @Mock private lateinit var updateMonitor: KeyguardUpdateMonitor
+ @Mock protected lateinit var connectivityManager: ConnectivityManager
+ @Mock protected lateinit var subscriptionManager: SubscriptionManager
+ @Mock protected lateinit var telephonyManager: TelephonyManager
+ @Mock protected lateinit var logger: MobileInputLogger
+ protected val summaryLogger = logcatTableLogBuffer(kosmos, "summaryLogger")
+ @Mock protected lateinit var logBufferFactory: TableLogBufferFactory
+ @Mock protected lateinit var updateMonitor: KeyguardUpdateMonitor
@Mock private lateinit var wifiManager: WifiManager
@Mock private lateinit var wifiPickerTrackerFactory: WifiPickerTrackerFactory
@Mock private lateinit var wifiPickerTracker: WifiPickerTracker
private val wifiTableLogBuffer = logcatTableLogBuffer(kosmos, "wifiTableLog")
- private val mobileMappings = FakeMobileMappingsProxy()
- private val subscriptionManagerProxy = FakeSubscriptionManagerProxy()
+ protected val mobileMappings = FakeMobileMappingsProxy()
+ protected val subscriptionManagerProxy = FakeSubscriptionManagerProxy()
private val mainExecutor = FakeExecutor(FakeSystemClock())
private val wifiLogBuffer = LogBuffer("wifi", maxSize = 100, logcatEchoTracker = mock())
private val wifiPickerTrackerCallback =
@@ -139,10 +420,10 @@ class MobileConnectionsRepositoryTest : SysuiTestCase() {
private val vcnTransportInfo = VcnTransportInfo.Builder().build()
private val userRepository = kosmos.fakeUserRepository
- private val testDispatcher = StandardTestDispatcher()
- private val testScope = TestScope(testDispatcher)
+ protected val testDispatcher = StandardTestDispatcher()
+ protected val testScope = TestScope(testDispatcher)
- private lateinit var underTest: MobileConnectionsRepositoryImpl
+ protected lateinit var underTest: T
@Before
fun setUp() {
@@ -237,30 +518,13 @@ class MobileConnectionsRepositoryTest : SysuiTestCase() {
carrierMergedRepoFactory = carrierMergedFactory,
)
- underTest =
- MobileConnectionsRepositoryImpl(
- connectivityRepository,
- subscriptionManager,
- subscriptionManagerProxy,
- telephonyManager,
- logger,
- summaryLogger,
- mobileMappings,
- fakeBroadcastDispatcher,
- context,
- /* bgDispatcher = */ testDispatcher,
- testScope.backgroundScope,
- /* mainDispatcher = */ testDispatcher,
- airplaneModeRepository,
- wifiRepository,
- fullConnectionFactory,
- updateMonitor,
- mock(),
- )
+ underTest = recreateRepo()
testScope.runCurrent()
}
+ abstract fun recreateRepo(): T
+
@Test
fun testSubscriptions_initiallyEmpty() =
testScope.runTest {
@@ -410,9 +674,17 @@ class MobileConnectionsRepositoryTest : SysuiTestCase() {
fun activeRepo_updatesWithActiveDataId() =
testScope.runTest {
val latest by collectLastValue(underTest.activeMobileDataRepository)
+ runCurrent()
- getTelephonyCallbackForType<ActiveDataSubscriptionIdListener>()
- .onActiveDataSubscriptionIdChanged(SUB_2_ID)
+ whenever(subscriptionManager.completeActiveSubscriptionInfoList)
+ .thenReturn(listOf(SUB_2))
+ getSubscriptionCallbacks().forEach { it.onSubscriptionsChanged() }
+ runCurrent()
+
+ getTelephonyCallbacksForType<ActiveDataSubscriptionIdListener>().forEach {
+ it.onActiveDataSubscriptionIdChanged(SUB_2_ID)
+ }
+ runCurrent()
assertThat(latest?.subId).isEqualTo(SUB_2_ID)
}
@@ -422,6 +694,10 @@ class MobileConnectionsRepositoryTest : SysuiTestCase() {
testScope.runTest {
val latest by collectLastValue(underTest.activeMobileDataRepository)
+ whenever(subscriptionManager.completeActiveSubscriptionInfoList)
+ .thenReturn(listOf(SUB_2))
+ getSubscriptionCallbacks().forEach { it.onSubscriptionsChanged() }
+
getTelephonyCallbackForType<ActiveDataSubscriptionIdListener>()
.onActiveDataSubscriptionIdChanged(SUB_2_ID)
@@ -437,60 +713,15 @@ class MobileConnectionsRepositoryTest : SysuiTestCase() {
/** Regression test for b/268146648. */
fun activeSubIdIsSetBeforeSubscriptionsAreUpdated_doesNotThrow() =
testScope.runTest {
- val activeRepo by collectLastValue(underTest.activeMobileDataRepository)
+ val activeRepo = collectLastValue(underTest.activeMobileDataRepository)
val subscriptions by collectLastValue(underTest.subscriptions)
- getTelephonyCallbackForType<ActiveDataSubscriptionIdListener>()
- .onActiveDataSubscriptionIdChanged(SUB_2_ID)
+ getTelephonyCallbacksForType<ActiveDataSubscriptionIdListener>().forEach {
+ it.onActiveDataSubscriptionIdChanged(SUB_2_ID)
+ }
assertThat(subscriptions).isEmpty()
- assertThat(activeRepo).isNotNull()
- }
-
- @Test
- fun getRepoForSubId_activeDataSubIdIsRequestedBeforeSubscriptionsUpdate() =
- testScope.runTest {
- var latestActiveRepo: MobileConnectionRepository? = null
- collectLastValue(
- underTest.activeMobileDataSubscriptionId.filterNotNull().onEach {
- latestActiveRepo = underTest.getRepoForSubId(it)
- }
- )
-
- val latestSubscriptions by collectLastValue(underTest.subscriptions)
-
- // Active data subscription id is sent, but no subscription change has been posted yet
- getTelephonyCallbackForType<ActiveDataSubscriptionIdListener>()
- .onActiveDataSubscriptionIdChanged(SUB_2_ID)
-
- // Subscriptions list is empty
- assertThat(latestSubscriptions).isEmpty()
- // getRepoForSubId does not throw
- assertThat(latestActiveRepo).isNotNull()
- }
-
- @Test
- fun activeDataSentBeforeSubscriptionList_subscriptionReusesActiveDataRepo() =
- testScope.runTest {
- val activeRepo by collectLastValue(underTest.activeMobileDataRepository)
- collectLastValue(underTest.subscriptions)
-
- // GIVEN active repo is updated before the subscription list updates
- getTelephonyCallbackForType<ActiveDataSubscriptionIdListener>()
- .onActiveDataSubscriptionIdChanged(SUB_2_ID)
-
- assertThat(activeRepo).isNotNull()
-
- // GIVEN the subscription list is then updated which includes the active data sub id
- whenever(subscriptionManager.completeActiveSubscriptionInfoList)
- .thenReturn(listOf(SUB_2))
- getSubscriptionCallback().onSubscriptionsChanged()
-
- // WHEN requesting a connection repository for the subscription
- val newRepo = underTest.getRepoForSubId(SUB_2_ID)
-
- // THEN the newly request repo has been cached and reused
- assertThat(activeRepo).isSameInstanceAs(newRepo)
+ activeRepo.invoke() // does not throw
}
@Test
@@ -501,6 +732,7 @@ class MobileConnectionsRepositoryTest : SysuiTestCase() {
whenever(subscriptionManager.completeActiveSubscriptionInfoList)
.thenReturn(listOf(SUB_1))
getSubscriptionCallback().onSubscriptionsChanged()
+ runCurrent()
val repo1 = underTest.getRepoForSubId(SUB_1_ID)
val repo2 = underTest.getRepoForSubId(SUB_1_ID)
@@ -525,80 +757,6 @@ class MobileConnectionsRepositoryTest : SysuiTestCase() {
assertThat(repo1).isSameInstanceAs(repo2)
}
- @Test
- fun testConnectionRepository_carrierMergedAndMobileSubs_usesCorrectRepos() =
- testScope.runTest {
- collectLastValue(underTest.subscriptions)
-
- getDefaultNetworkCallback().onCapabilitiesChanged(NETWORK, WIFI_NETWORK_CAPS_CM)
- setWifiState(isCarrierMerged = true)
- whenever(subscriptionManager.completeActiveSubscriptionInfoList)
- .thenReturn(listOf(SUB_1, SUB_CM))
- getSubscriptionCallback().onSubscriptionsChanged()
-
- val carrierMergedRepo = underTest.getRepoForSubId(SUB_CM_ID)
- val mobileRepo = underTest.getRepoForSubId(SUB_1_ID)
- assertThat(carrierMergedRepo.getIsCarrierMerged()).isTrue()
- assertThat(mobileRepo.getIsCarrierMerged()).isFalse()
- }
-
- @Test
- fun testSubscriptions_subNoLongerCarrierMerged_repoUpdates() =
- testScope.runTest {
- collectLastValue(underTest.subscriptions)
-
- getDefaultNetworkCallback().onCapabilitiesChanged(NETWORK, WIFI_NETWORK_CAPS_CM)
- setWifiState(isCarrierMerged = true)
- whenever(subscriptionManager.completeActiveSubscriptionInfoList)
- .thenReturn(listOf(SUB_1, SUB_CM))
- getSubscriptionCallback().onSubscriptionsChanged()
-
- val carrierMergedRepo = underTest.getRepoForSubId(SUB_CM_ID)
- var mobileRepo = underTest.getRepoForSubId(SUB_1_ID)
- assertThat(carrierMergedRepo.getIsCarrierMerged()).isTrue()
- assertThat(mobileRepo.getIsCarrierMerged()).isFalse()
-
- // WHEN the wifi network updates to be not carrier merged
- getDefaultNetworkCallback().onCapabilitiesChanged(NETWORK, WIFI_NETWORK_CAPS_ACTIVE)
- setWifiState(isCarrierMerged = false)
- runCurrent()
-
- // THEN the repos update
- val noLongerCarrierMergedRepo = underTest.getRepoForSubId(SUB_CM_ID)
- mobileRepo = underTest.getRepoForSubId(SUB_1_ID)
- assertThat(noLongerCarrierMergedRepo.getIsCarrierMerged()).isFalse()
- assertThat(mobileRepo.getIsCarrierMerged()).isFalse()
- }
-
- @Test
- fun testSubscriptions_subBecomesCarrierMerged_repoUpdates() =
- testScope.runTest {
- collectLastValue(underTest.subscriptions)
-
- getDefaultNetworkCallback().onCapabilitiesChanged(NETWORK, WIFI_NETWORK_CAPS_ACTIVE)
- setWifiState(isCarrierMerged = false)
- whenever(subscriptionManager.completeActiveSubscriptionInfoList)
- .thenReturn(listOf(SUB_1, SUB_CM))
- getSubscriptionCallback().onSubscriptionsChanged()
- runCurrent()
-
- val notYetCarrierMergedRepo = underTest.getRepoForSubId(SUB_CM_ID)
- var mobileRepo = underTest.getRepoForSubId(SUB_1_ID)
- assertThat(notYetCarrierMergedRepo.getIsCarrierMerged()).isFalse()
- assertThat(mobileRepo.getIsCarrierMerged()).isFalse()
-
- // WHEN the wifi network updates to be carrier merged
- getDefaultNetworkCallback().onCapabilitiesChanged(NETWORK, WIFI_NETWORK_CAPS_CM)
- setWifiState(isCarrierMerged = true)
- runCurrent()
-
- // THEN the repos update
- val carrierMergedRepo = underTest.getRepoForSubId(SUB_CM_ID)
- mobileRepo = underTest.getRepoForSubId(SUB_1_ID)
- assertThat(carrierMergedRepo.getIsCarrierMerged()).isTrue()
- assertThat(mobileRepo.getIsCarrierMerged()).isFalse()
- }
-
@SuppressLint("UnspecifiedRegisterReceiverFlag")
@Test
fun testDeviceEmergencyCallState_eagerlyChecksState() =
@@ -674,139 +832,6 @@ class MobileConnectionsRepositoryTest : SysuiTestCase() {
}
@Test
- @Ignore("b/333912012")
- fun testConnectionCache_clearsInvalidSubscriptions() =
- testScope.runTest {
- collectLastValue(underTest.subscriptions)
-
- whenever(subscriptionManager.completeActiveSubscriptionInfoList)
- .thenReturn(listOf(SUB_1, SUB_2))
- getSubscriptionCallback().onSubscriptionsChanged()
-
- // Get repos to trigger caching
- val repo1 = underTest.getRepoForSubId(SUB_1_ID)
- val repo2 = underTest.getRepoForSubId(SUB_2_ID)
-
- assertThat(underTest.getSubIdRepoCache())
- .containsExactly(SUB_1_ID, repo1, SUB_2_ID, repo2)
-
- // SUB_2 disappears
- whenever(subscriptionManager.completeActiveSubscriptionInfoList)
- .thenReturn(listOf(SUB_1))
- getSubscriptionCallback().onSubscriptionsChanged()
-
- assertThat(underTest.getSubIdRepoCache()).containsExactly(SUB_1_ID, repo1)
- }
-
- @Test
- @Ignore("b/333912012")
- fun testConnectionCache_clearsInvalidSubscriptions_includingCarrierMerged() =
- testScope.runTest {
- collectLastValue(underTest.subscriptions)
-
- getDefaultNetworkCallback().onCapabilitiesChanged(NETWORK, WIFI_NETWORK_CAPS_CM)
- setWifiState(isCarrierMerged = true)
- whenever(subscriptionManager.completeActiveSubscriptionInfoList)
- .thenReturn(listOf(SUB_1, SUB_2, SUB_CM))
- getSubscriptionCallback().onSubscriptionsChanged()
-
- // Get repos to trigger caching
- val repo1 = underTest.getRepoForSubId(SUB_1_ID)
- val repo2 = underTest.getRepoForSubId(SUB_2_ID)
- val repoCarrierMerged = underTest.getRepoForSubId(SUB_CM_ID)
-
- assertThat(underTest.getSubIdRepoCache())
- .containsExactly(SUB_1_ID, repo1, SUB_2_ID, repo2, SUB_CM_ID, repoCarrierMerged)
-
- // SUB_2 and SUB_CM disappear
- whenever(subscriptionManager.completeActiveSubscriptionInfoList)
- .thenReturn(listOf(SUB_1))
- getSubscriptionCallback().onSubscriptionsChanged()
-
- assertThat(underTest.getSubIdRepoCache()).containsExactly(SUB_1_ID, repo1)
- }
-
- /** Regression test for b/261706421 */
- @Test
- @Ignore("b/333912012")
- fun testConnectionsCache_clearMultipleSubscriptionsAtOnce_doesNotThrow() =
- testScope.runTest {
- collectLastValue(underTest.subscriptions)
-
- whenever(subscriptionManager.completeActiveSubscriptionInfoList)
- .thenReturn(listOf(SUB_1, SUB_2))
- getSubscriptionCallback().onSubscriptionsChanged()
-
- // Get repos to trigger caching
- val repo1 = underTest.getRepoForSubId(SUB_1_ID)
- val repo2 = underTest.getRepoForSubId(SUB_2_ID)
-
- assertThat(underTest.getSubIdRepoCache())
- .containsExactly(SUB_1_ID, repo1, SUB_2_ID, repo2)
-
- // All subscriptions disappear
- whenever(subscriptionManager.completeActiveSubscriptionInfoList).thenReturn(listOf())
- getSubscriptionCallback().onSubscriptionsChanged()
-
- assertThat(underTest.getSubIdRepoCache()).isEmpty()
- }
-
- @Test
- fun testConnectionsCache_keepsReposCached() =
- testScope.runTest {
- // Collect subscriptions to start the job
- collectLastValue(underTest.subscriptions)
-
- whenever(subscriptionManager.completeActiveSubscriptionInfoList)
- .thenReturn(listOf(SUB_1))
- getSubscriptionCallback().onSubscriptionsChanged()
-
- val repo1_1 = underTest.getRepoForSubId(SUB_1_ID)
-
- // All subscriptions disappear
- whenever(subscriptionManager.completeActiveSubscriptionInfoList).thenReturn(listOf())
- getSubscriptionCallback().onSubscriptionsChanged()
-
- // Sub1 comes back
- whenever(subscriptionManager.completeActiveSubscriptionInfoList)
- .thenReturn(listOf(SUB_1))
- getSubscriptionCallback().onSubscriptionsChanged()
-
- val repo1_2 = underTest.getRepoForSubId(SUB_1_ID)
-
- assertThat(repo1_1).isSameInstanceAs(repo1_2)
- }
-
- @Test
- fun testConnectionsCache_doesNotDropReferencesThatHaveBeenRealized() =
- testScope.runTest {
- // Collect subscriptions to start the job
- collectLastValue(underTest.subscriptions)
-
- whenever(subscriptionManager.completeActiveSubscriptionInfoList)
- .thenReturn(listOf(SUB_1))
- getSubscriptionCallback().onSubscriptionsChanged()
-
- // Client grabs a reference to a repository, but doesn't keep it around
- underTest.getRepoForSubId(SUB_1_ID)
-
- // All subscriptions disappear
- whenever(subscriptionManager.completeActiveSubscriptionInfoList).thenReturn(listOf())
- getSubscriptionCallback().onSubscriptionsChanged()
-
- val repo1 = underTest.getRepoForSubId(SUB_1_ID)
-
- assertThat(repo1).isNotNull()
- }
-
- @Test
- fun testConnectionRepository_invalidSubId_doesNotThrow() =
- testScope.runTest {
- underTest.getRepoForSubId(SUB_1_ID)
- // No exception
- }
-
- @Test
fun connectionRepository_logBufferContainsSubIdInItsName() =
testScope.runTest {
collectLastValue(underTest.subscriptions)
@@ -814,6 +839,7 @@ class MobileConnectionsRepositoryTest : SysuiTestCase() {
whenever(subscriptionManager.completeActiveSubscriptionInfoList)
.thenReturn(listOf(SUB_1, SUB_2))
getSubscriptionCallback().onSubscriptionsChanged()
+ runCurrent()
// Get repos to trigger creation
underTest.getRepoForSubId(SUB_1_ID)
@@ -848,15 +874,27 @@ class MobileConnectionsRepositoryTest : SysuiTestCase() {
fun defaultDataSubId_fetchesInitialValueOnStart() =
testScope.runTest {
subscriptionManagerProxy.defaultDataSubId = 2
+ underTest = recreateRepo()
+
val latest by collectLastValue(underTest.defaultDataSubId)
assertThat(latest).isEqualTo(2)
}
+ private fun setDefaultDataSubId(subId: Int) {
+ subscriptionManagerProxy.defaultDataSubId = subId
+ fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly(
+ context,
+ Intent(TelephonyManager.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED).apply {
+ putExtra(PhoneConstants.SUBSCRIPTION_KEY, subId)
+ },
+ )
+ }
+
@Test
fun defaultDataSubId_filtersOutInvalidSubIds() =
testScope.runTest {
- subscriptionManagerProxy.defaultDataSubId = INVALID_SUBSCRIPTION_ID
+ setDefaultDataSubId(INVALID_SUBSCRIPTION_ID)
val latest by collectLastValue(underTest.defaultDataSubId)
assertThat(latest).isNull()
@@ -865,15 +903,12 @@ class MobileConnectionsRepositoryTest : SysuiTestCase() {
@Test
fun defaultDataSubId_filtersOutInvalidSubIds_fromValidToInvalid() =
testScope.runTest {
- subscriptionManagerProxy.defaultDataSubId = 2
+ setDefaultDataSubId(2)
val latest by collectLastValue(underTest.defaultDataSubId)
assertThat(latest).isEqualTo(2)
- val intent =
- Intent(TelephonyManager.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED)
- .putExtra(PhoneConstants.SUBSCRIPTION_KEY, INVALID_SUBSCRIPTION_ID)
- fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly(context, intent)
+ setDefaultDataSubId(INVALID_SUBSCRIPTION_ID)
assertThat(latest).isNull()
}
@@ -881,7 +916,7 @@ class MobileConnectionsRepositoryTest : SysuiTestCase() {
@Test
fun defaultDataSubId_fetchesCurrentOnRestart() =
testScope.runTest {
- subscriptionManagerProxy.defaultDataSubId = 2
+ setDefaultDataSubId(2)
var latest: Int? = null
var job = underTest.defaultDataSubId.onEach { latest = it }.launchIn(this)
runCurrent()
@@ -894,7 +929,7 @@ class MobileConnectionsRepositoryTest : SysuiTestCase() {
latest = null
- subscriptionManagerProxy.defaultDataSubId = 1
+ setDefaultDataSubId(1)
job = underTest.defaultDataSubId.onEach { latest = it }.launchIn(this)
runCurrent()
@@ -1281,26 +1316,7 @@ class MobileConnectionsRepositoryTest : SysuiTestCase() {
// The initial value will be fetched when the repo is created, so we need to override
// the resources and then re-create the repo.
- underTest =
- MobileConnectionsRepositoryImpl(
- connectivityRepository,
- subscriptionManager,
- subscriptionManagerProxy,
- telephonyManager,
- logger,
- summaryLogger,
- mobileMappings,
- fakeBroadcastDispatcher,
- context,
- testDispatcher,
- testScope.backgroundScope,
- testDispatcher,
- airplaneModeRepository,
- wifiRepository,
- fullConnectionFactory,
- updateMonitor,
- mock(),
- )
+ underTest = recreateRepo()
val latest by collectLastValue(underTest.defaultDataSubRatConfig)
@@ -1428,6 +1444,12 @@ class MobileConnectionsRepositoryTest : SysuiTestCase() {
assertThat(underTest.getIsAnySimSecure()).isFalse()
whenever(updateMonitor.isSimPinSecure).thenReturn(true)
+ org.mockito.kotlin
+ .argumentCaptor<KeyguardUpdateMonitorCallback>()
+ .apply { verify(updateMonitor, atLeast(0)).registerCallback(capture()) }
+ .allValues
+ .forEach { it.onSimStateChanged(0, 0, 0) }
+ runCurrent()
assertThat(underTest.getIsAnySimSecure()).isTrue()
}
@@ -1447,19 +1469,26 @@ class MobileConnectionsRepositoryTest : SysuiTestCase() {
testScope.runTest {
whenever(telephonyManager.emergencyCallbackMode).thenReturn(true)
+ getTelephonyCallbacksForType<EmergencyCallbackModeListener>().forEach {
+ it.onCallbackModeStarted(
+ TelephonyManager.EMERGENCY_CALLBACK_MODE_SMS,
+ Duration.ZERO,
+ 0,
+ )
+ }
runCurrent()
assertThat(underTest.isInEcmMode()).isTrue()
}
- private fun TestScope.getDefaultNetworkCallback(): ConnectivityManager.NetworkCallback {
+ protected fun TestScope.getDefaultNetworkCallback(): ConnectivityManager.NetworkCallback {
runCurrent()
val callbackCaptor = argumentCaptor<ConnectivityManager.NetworkCallback>()
verify(connectivityManager).registerDefaultNetworkCallback(callbackCaptor.capture())
return callbackCaptor.value!!
}
- private fun setWifiState(isCarrierMerged: Boolean) {
+ protected fun setWifiState(isCarrierMerged: Boolean) {
if (isCarrierMerged) {
val mergedEntry =
mock<MergedCarrierEntry>().apply {
@@ -1481,7 +1510,7 @@ class MobileConnectionsRepositoryTest : SysuiTestCase() {
wifiPickerTrackerCallback.value.onWifiEntriesChanged()
}
- private fun TestScope.getSubscriptionCallback():
+ protected fun TestScope.getSubscriptionCallback():
SubscriptionManager.OnSubscriptionsChangedListener {
runCurrent()
val callbackCaptor = argumentCaptor<SubscriptionManager.OnSubscriptionsChangedListener>()
@@ -1490,25 +1519,39 @@ class MobileConnectionsRepositoryTest : SysuiTestCase() {
return callbackCaptor.value!!
}
- private fun TestScope.getTelephonyCallbacks(): List<TelephonyCallback> {
+ protected fun TestScope.getSubscriptionCallbacks():
+ List<SubscriptionManager.OnSubscriptionsChangedListener> {
+ runCurrent()
+ val callbackCaptor = argumentCaptor<SubscriptionManager.OnSubscriptionsChangedListener>()
+ verify(subscriptionManager, atLeast(0))
+ .addOnSubscriptionsChangedListener(any(), callbackCaptor.capture())
+ return callbackCaptor.allValues
+ }
+
+ fun TestScope.getTelephonyCallbacks(): List<TelephonyCallback> {
runCurrent()
val callbackCaptor = argumentCaptor<TelephonyCallback>()
- verify(telephonyManager).registerTelephonyCallback(any(), callbackCaptor.capture())
+ verify(telephonyManager, atLeast(0))
+ .registerTelephonyCallback(any(), callbackCaptor.capture())
return callbackCaptor.allValues
}
- private inline fun <reified T> TestScope.getTelephonyCallbackForType(): T {
- val cbs = this.getTelephonyCallbacks().filterIsInstance<T>()
+ inline fun <reified T> TestScope.getTelephonyCallbackForType(): T {
+ val cbs = getTelephonyCallbacksForType<T>()
assertThat(cbs.size).isEqualTo(1)
return cbs[0]
}
+ inline fun <reified T> TestScope.getTelephonyCallbacksForType(): List<T> {
+ return getTelephonyCallbacks().filterIsInstance<T>()
+ }
+
companion object {
// Subscription 1
- private const val SUB_1_ID = 1
+ const val SUB_1_ID = 1
private const val SUB_1_NAME = "Carrier $SUB_1_ID"
private val GROUP_1 = ParcelUuid(UUID.randomUUID())
- private val SUB_1 =
+ val SUB_1 =
mock<SubscriptionInfo>().also {
whenever(it.subscriptionId).thenReturn(SUB_1_ID)
whenever(it.groupUuid).thenReturn(GROUP_1)
@@ -1524,10 +1567,10 @@ class MobileConnectionsRepositoryTest : SysuiTestCase() {
)
// Subscription 2
- private const val SUB_2_ID = 2
+ const val SUB_2_ID = 2
private const val SUB_2_NAME = "Carrier $SUB_2_ID"
private val GROUP_2 = ParcelUuid(UUID.randomUUID())
- private val SUB_2 =
+ val SUB_2 =
mock<SubscriptionInfo>().also {
whenever(it.subscriptionId).thenReturn(SUB_2_ID)
whenever(it.groupUuid).thenReturn(GROUP_2)
@@ -1552,6 +1595,7 @@ class MobileConnectionsRepositoryTest : SysuiTestCase() {
whenever(it.subscriptionId).thenReturn(SUB_3_ID_GROUPED)
whenever(it.groupUuid).thenReturn(GROUP_ID_3_4)
whenever(it.profileClass).thenReturn(PROFILE_CLASS_UNSET)
+ whenever(it.carrierName).thenReturn("")
}
// Subscription 4
@@ -1561,17 +1605,18 @@ class MobileConnectionsRepositoryTest : SysuiTestCase() {
whenever(it.subscriptionId).thenReturn(SUB_4_ID_GROUPED)
whenever(it.groupUuid).thenReturn(GROUP_ID_3_4)
whenever(it.profileClass).thenReturn(PROFILE_CLASS_UNSET)
+ whenever(it.carrierName).thenReturn("")
}
// Subs 3 and 4 are considered to be in the same group ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
private const val NET_ID = 123
- private val NETWORK = mock<Network>().apply { whenever(getNetId()).thenReturn(NET_ID) }
+ val NETWORK = mock<Network>().apply { whenever(getNetId()).thenReturn(NET_ID) }
// Carrier merged subscription
- private const val SUB_CM_ID = 5
+ const val SUB_CM_ID = 5
private const val SUB_CM_NAME = "Carrier $SUB_CM_ID"
- private val SUB_CM =
+ val SUB_CM =
mock<SubscriptionInfo>().also {
whenever(it.subscriptionId).thenReturn(SUB_CM_ID)
whenever(it.carrierName).thenReturn(SUB_CM_NAME)
@@ -1590,7 +1635,7 @@ class MobileConnectionsRepositoryTest : SysuiTestCase() {
whenever(this.isCarrierMerged).thenReturn(true)
whenever(this.subscriptionId).thenReturn(SUB_CM_ID)
}
- private val WIFI_NETWORK_CAPS_CM =
+ val WIFI_NETWORK_CAPS_CM =
mock<NetworkCapabilities>().also {
whenever(it.hasTransport(TRANSPORT_WIFI)).thenReturn(true)
whenever(it.transportInfo).thenReturn(WIFI_INFO_CM)
@@ -1602,7 +1647,7 @@ class MobileConnectionsRepositoryTest : SysuiTestCase() {
whenever(this.isPrimary).thenReturn(true)
whenever(this.isCarrierMerged).thenReturn(false)
}
- private val WIFI_NETWORK_CAPS_ACTIVE =
+ val WIFI_NETWORK_CAPS_ACTIVE =
mock<NetworkCapabilities>().also {
whenever(it.hasTransport(TRANSPORT_WIFI)).thenReturn(true)
whenever(it.transportInfo).thenReturn(WIFI_INFO_ACTIVE)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingControllerTest.java
index 3247a1ab6eb0..8ff8fe6cc3d6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingControllerTest.java
@@ -44,6 +44,7 @@ import androidx.test.filters.SmallTest;
import com.android.internal.R;
import com.android.internal.view.RotationPolicy;
+import com.android.settingslib.devicestate.AndroidSecureSettings;
import com.android.settingslib.devicestate.DeviceStateRotationLockSettingsManager;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.dump.DumpManager;
@@ -117,7 +118,8 @@ public class DeviceStateRotationLockSettingControllerTest extends SysuiTestCase
ArgumentCaptor.forClass(DeviceStateManager.DeviceStateCallback.class);
mContentResolver = mContext.getContentResolver();
- mSettingsManager = DeviceStateRotationLockSettingsManager.getInstance(mContext);
+ mSettingsManager = new DeviceStateRotationLockSettingsManager(mContext,
+ new AndroidSecureSettings(mContentResolver));
mDeviceStateRotationLockSettingController =
new DeviceStateRotationLockSettingController(
mFakeRotationPolicy,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayControllerTest.kt
index 54df9e99baa5..bb6ba46f1a0b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayControllerTest.kt
@@ -25,13 +25,12 @@ import android.view.WindowManager
import android.view.accessibility.AccessibilityManager
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.app.viewcapture.ViewCaptureAwareWindowManager
import com.android.internal.logging.InstanceId
import com.android.internal.logging.testing.UiEventLoggerFake
+import com.android.systemui.res.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.dump.DumpManager
-import com.android.systemui.res.R
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener
import com.android.systemui.util.concurrency.DelayableExecutor
@@ -78,7 +77,7 @@ class TemporaryViewDisplayControllerTest : SysuiTestCase() {
@Mock
private lateinit var dumpManager: DumpManager
@Mock
- private lateinit var windowManager: ViewCaptureAwareWindowManager
+ private lateinit var windowManager: WindowManager
@Mock
private lateinit var powerManager: PowerManager
@@ -1143,7 +1142,7 @@ class TemporaryViewDisplayControllerTest : SysuiTestCase() {
inner class TestController(
context: Context,
logger: TemporaryViewLogger<ViewInfo>,
- viewCaptureAwareWindowManager: ViewCaptureAwareWindowManager,
+ windowManager: WindowManager,
@Main mainExecutor: DelayableExecutor,
accessibilityManager: AccessibilityManager,
configurationController: ConfigurationController,
@@ -1155,7 +1154,7 @@ class TemporaryViewDisplayControllerTest : SysuiTestCase() {
) : TemporaryViewDisplayController<ViewInfo, TemporaryViewLogger<ViewInfo>>(
context,
logger,
- viewCaptureAwareWindowManager,
+ windowManager,
mainExecutor,
accessibilityManager,
configurationController,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinatorTest.kt
index 4260b6558950..664f2df62782 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinatorTest.kt
@@ -30,8 +30,6 @@ import android.widget.TextView
import androidx.core.animation.doOnCancel
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.app.viewcapture.ViewCapture
-import com.android.app.viewcapture.ViewCaptureAwareWindowManager
import com.android.internal.logging.InstanceId
import com.android.internal.logging.testing.UiEventLoggerFake
import com.android.systemui.SysuiTestCase
@@ -86,7 +84,6 @@ class ChipbarCoordinatorTest : SysuiTestCase() {
@Mock private lateinit var viewUtil: ViewUtil
@Mock private lateinit var vibratorHelper: VibratorHelper
@Mock private lateinit var swipeGestureHandler: SwipeChipbarAwayGestureHandler
- @Mock private lateinit var lazyViewCapture: Lazy<ViewCapture>
private lateinit var chipbarAnimator: TestChipbarAnimator
private lateinit var fakeWakeLockBuilder: WakeLockFake.Builder
private lateinit var fakeWakeLock: WakeLockFake
@@ -115,8 +112,7 @@ class ChipbarCoordinatorTest : SysuiTestCase() {
ChipbarCoordinator(
context,
logger,
- ViewCaptureAwareWindowManager(windowManager, lazyViewCapture,
- isViewCaptureEnabled = false),
+ windowManager,
fakeExecutor,
accessibilityManager,
configurationController,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/WindowManagerProviderImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/utils/WindowManagerProviderImplTest.kt
new file mode 100644
index 000000000000..7b52237f0a01
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/WindowManagerProviderImplTest.kt
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2025 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.utils
+
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
+import android.view.WindowManager
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.Flags
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.utils.windowmanager.WindowManagerProviderImpl
+import com.google.common.truth.Truth.assertThat
+import org.junit.runner.RunWith
+import org.junit.Test
+
+@RunWith(AndroidJUnit4::class)
+@SmallTest
+class WindowManagerProviderImplTest : SysuiTestCase() {
+
+ private val windowManagerProvider = WindowManagerProviderImpl()
+ private val windowManagerFromSystemService = mContext.getSystemService(WindowManager::class.java)
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_VIEW_CAPTURE_TRACING)
+ fun viewCaptureTracingEnabled_verifyWMInstanceDoesNotMatchContextOne() {
+ val windowManagerFromProvider = windowManagerProvider.getWindowManager(mContext)
+ assertThat(windowManagerFromProvider).isNotEqualTo(windowManagerFromSystemService)
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_ENABLE_VIEW_CAPTURE_TRACING)
+ fun viewCaptureTracingDisabled_verifyWMInstanceMatchesContextOne() {
+ mContext.addMockSystemService(WindowManager::class.java, windowManagerFromSystemService)
+
+ val windowManagerFromProvider = windowManagerProvider.getWindowManager(mContext)
+ assertThat(windowManagerFromProvider).isEqualTo(windowManagerFromSystemService)
+ }
+} \ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
index 0d7ce5353cd4..f89571f2db2d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
@@ -101,8 +101,6 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.test.filters.SmallTest;
-import com.android.app.viewcapture.ViewCapture;
-import com.android.app.viewcapture.ViewCaptureAwareWindowManager;
import com.android.internal.colorextraction.ColorExtractor;
import com.android.internal.logging.UiEventLogger;
import com.android.internal.protolog.ProtoLog;
@@ -358,8 +356,6 @@ public class BubblesTest extends SysuiTestCase {
@Mock
private Display mDefaultDisplay;
@Mock
- private Lazy<ViewCapture> mLazyViewCapture;
- @Mock
private SyncTransactionQueue mSyncQueue;
private final KosmosJavaAdapter mKosmos = new KosmosJavaAdapter(this);
@@ -429,8 +425,7 @@ public class BubblesTest extends SysuiTestCase {
mNotificationShadeWindowController = new NotificationShadeWindowControllerImpl(
mContext,
new FakeWindowRootViewComponent.Factory(mNotificationShadeWindowView),
- new ViewCaptureAwareWindowManager(mWindowManager, mLazyViewCapture,
- /* isViewCaptureEnabled= */ false),
+ mWindowManager,
mActivityManager,
mDozeParameters,
mStatusBarStateController,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java b/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java
index 15cb95a99967..2facc1c01ae1 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java
@@ -15,6 +15,7 @@
*/
package com.android.systemui;
+import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
@@ -49,11 +50,13 @@ import com.android.systemui.log.LogWtfHandlerRule;
import org.junit.After;
import org.junit.AfterClass;
+import org.junit.Assert;
import org.junit.Before;
import org.junit.ClassRule;
import org.junit.Rule;
import org.mockito.Mockito;
+import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.lang.annotation.Retention;
@@ -190,6 +193,7 @@ public abstract class SysuiTestCase {
@Before
public void SysuiSetup() throws Exception {
+ assertTempFilesAreCreatable();
ProtoLog.REQUIRE_PROTOLOGTOOL = false;
mSysuiDependency = new SysuiTestDependency(mContext, shouldFailOnLeakedReceiver());
mDependency = mSysuiDependency.install();
@@ -211,6 +215,28 @@ public abstract class SysuiTestCase {
}
}
+ private static Boolean sCanCreateTempFiles = null;
+
+ private static void assertTempFilesAreCreatable() {
+ // TODO(b/391948934): hopefully remove this hack
+ if (sCanCreateTempFiles == null) {
+ try {
+ File tempFile = File.createTempFile("confirm_temp_file_createable", "txt");
+ sCanCreateTempFiles = true;
+ assertTrue(tempFile.delete());
+ } catch (IOException e) {
+ sCanCreateTempFiles = false;
+ throw new RuntimeException(e);
+ }
+ }
+ if (!sCanCreateTempFiles) {
+ Assert.fail(
+ "Cannot create temp files, so mockito will probably fail (b/391948934). Temp"
+ + " folder should be: "
+ + System.getProperty("java.io.tmpdir"));
+ }
+ }
+
protected boolean shouldFailOnLeakedReceiver() {
return false;
}
@@ -257,6 +283,9 @@ public abstract class SysuiTestCase {
}
public FakeBroadcastDispatcher getFakeBroadcastDispatcher() {
+ if (mSysuiDependency == null) {
+ return null;
+ }
return mSysuiDependency.getFakeBroadcastDispatcher();
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCaseExt.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCaseExt.kt
index d3dccb021ff8..ebf89e9e3889 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCaseExt.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCaseExt.kt
@@ -18,8 +18,47 @@ package com.android.systemui
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testCase
+import com.android.systemui.kosmos.useStandardTestDispatcher
+import com.android.systemui.kosmos.useUnconfinedTestDispatcher
-fun SysuiTestCase.testKosmos(): Kosmos = Kosmos().apply { testCase = this@testKosmos }
+/**
+ * This definition, which uses standard dispatcher, is eventually going away.
+ *
+ * If you are calling this method, and want the new default behavior, call `testKosmosNew`, and you
+ * will be migrated to the new behavior (unconfined dispatcher). If you want to maintain the old
+ * behavior, directly call testKosmosNew().useStandardTestDispatcher().
+ *
+ * The migration will proceed in multiple steps:
+ * 1. All calls to testKosmos will be converted to testKosmosLegacy, maybe over several CLs.
+ * 2. When there are zero references to testKosmos, it will be briefly deleted
+ * 3. A new testKosmos will be introduced that uses unconfined test dispatcher
+ * 4. All callers to testKosmosNew that have been introduced since step 1 will be migrated to this
+ * new definition of testKosmos
+ * 5. testKosmosNew will be deleted
+ * 6. Over time, test authors will be encouraged to migrate away from testKosmosLegacy
+ *
+ * For details, see go/thetiger
+ */
+// TODO(b/342622417)
+fun SysuiTestCase.testKosmos(): Kosmos = testKosmosLegacy()
+
+/**
+ * Create a new Kosmos instance using the unconfined test dispatcher. See migration notes on
+ * [testKosmos]
+ */
+fun SysuiTestCase.testKosmosNew(): Kosmos =
+ Kosmos().apply { testCase = this@testKosmosNew }.useUnconfinedTestDispatcher()
+
+/**
+ * This should not be called directly. Instead, you can use:
+ * - testKosmosNew().useStandardTestDispatcher() to explicitly choose the standard dispatcher
+ * - testKosmosNew() to explicitly choose the unconfined dispatcher (which is the new sysui default)
+ *
+ * For details, see go/thetiger
+ */
+@Deprecated("Do not call this directly. Use testKosmos() with dispatcher functions if needed.")
+fun SysuiTestCase.testKosmosLegacy(): Kosmos =
+ Kosmos().useStandardTestDispatcher().apply { testCase = this@testKosmosLegacy }
/** Run [f] on the main thread and return its result once completed. */
fun <T : Any> SysuiTestCase.runOnMainThreadAndWaitForIdleSync(f: () -> T): T {
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/posturing/data/repository/FakePosturingRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/posturing/data/repository/FakePosturingRepository.kt
index 8a597a61ee78..47b1bf52d8e2 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/posturing/data/repository/FakePosturingRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/posturing/data/repository/FakePosturingRepository.kt
@@ -16,18 +16,18 @@
package com.android.systemui.communal.posturing.data.repository
-import com.android.systemui.communal.posturing.shared.model.PosturedState
-import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.StateFlow
-import kotlinx.coroutines.flow.asStateFlow
+import com.android.systemui.communal.posturing.data.model.PositionState
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableSharedFlow
+import kotlinx.coroutines.flow.asSharedFlow
class FakePosturingRepository : PosturingRepository {
- private val _postured = MutableStateFlow<PosturedState>(PosturedState.Unknown)
+ private val _postured = MutableSharedFlow<PositionState>()
- override val posturedState: StateFlow<PosturedState> = _postured.asStateFlow()
+ override val positionState: Flow<PositionState> = _postured.asSharedFlow()
- fun setPosturedState(state: PosturedState) {
- _postured.value = state
+ suspend fun emitPositionState(state: PositionState) {
+ _postured.emit(state)
}
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/posturing/domain/interactor/PosturingInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/posturing/domain/interactor/PosturingInteractorKosmos.kt
index 53c9c6440c69..792346ebce59 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/posturing/domain/interactor/PosturingInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/posturing/domain/interactor/PosturingInteractorKosmos.kt
@@ -18,6 +18,27 @@ package com.android.systemui.communal.posturing.domain.interactor
import com.android.systemui.communal.posturing.data.repository.posturingRepository
import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.advanceTimeBy
+import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.kosmos.runCurrent
+import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.log.logcatLogBuffer
+import com.android.systemui.util.sensors.asyncSensorManager
+import com.android.systemui.util.time.systemClock
val Kosmos.posturingInteractor by
- Kosmos.Fixture<PosturingInteractor> { PosturingInteractor(repository = posturingRepository) }
+ Kosmos.Fixture<PosturingInteractor> {
+ PosturingInteractor(
+ repository = posturingRepository,
+ asyncSensorManager = asyncSensorManager,
+ applicationScope = applicationCoroutineScope,
+ bgDispatcher = testDispatcher,
+ logBuffer = logcatLogBuffer("PosturingInteractor"),
+ clock = systemClock,
+ )
+ }
+
+fun Kosmos.advanceTimeBySlidingWindowAndRun() {
+ advanceTimeBy(PosturingInteractor.SLIDING_WINDOW_DURATION)
+ runCurrent()
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/DisplayScopeRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/DisplayScopeRepositoryKosmos.kt
index ff4ba61b6965..208eabc44073 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/DisplayScopeRepositoryKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/DisplayScopeRepositoryKosmos.kt
@@ -16,11 +16,13 @@
package com.android.systemui.display.data.repository
+import com.android.app.displaylib.PerDisplayRepository
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testDispatcher
+import kotlinx.coroutines.CoroutineScope
val Kosmos.fakeDisplayScopeRepository by
Kosmos.Fixture { FakeDisplayScopeRepository(testDispatcher) }
-var Kosmos.displayScopeRepository: DisplayScopeRepository by
+var Kosmos.displayScopeRepository: PerDisplayRepository<CoroutineScope> by
Kosmos.Fixture { fakeDisplayScopeRepository }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/FakeDisplayRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/FakeDisplayRepository.kt
index 338e4bec7aa2..122e6a507cbf 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/FakeDisplayRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/FakeDisplayRepository.kt
@@ -16,6 +16,7 @@
package com.android.systemui.display.data.repository
import android.view.Display
+import com.android.app.displaylib.DisplayRepository.PendingDisplay
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.util.mockito.mock
import dagger.Binds
@@ -26,7 +27,6 @@ import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
-import com.android.app.displaylib.DisplayRepository.PendingDisplay
import org.mockito.Mockito.`when` as whenever
/** Creates a mock display. */
@@ -50,8 +50,7 @@ fun createPendingDisplay(id: Int = 0): PendingDisplay =
class FakeDisplayRepository @Inject constructor() : DisplayRepository {
private val flow = MutableStateFlow<Set<Display>>(emptySet())
private val displayIdFlow = MutableStateFlow<Set<Int>>(emptySet())
- private val pendingDisplayFlow =
- MutableSharedFlow<PendingDisplay?>(replay = 1)
+ private val pendingDisplayFlow = MutableSharedFlow<PendingDisplay?>(replay = 1)
private val displayAdditionEventFlow = MutableSharedFlow<Display?>(replay = 0)
private val displayRemovalEventFlow = MutableSharedFlow<Int>(replay = 0)
private val displayIdsWithSystemDecorationsFlow = MutableStateFlow<Set<Int>>(emptySet())
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/FakeDisplayScopeRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/FakeDisplayScopeRepository.kt
index 3c2592471694..84c9abfba803 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/FakeDisplayScopeRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/FakeDisplayScopeRepository.kt
@@ -16,15 +16,18 @@
package com.android.systemui.display.data.repository
+import com.android.app.displaylib.PerDisplayRepository
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
-class FakeDisplayScopeRepository(private val dispatcher: CoroutineDispatcher) :
- DisplayScopeRepository {
+class FakeDisplayScopeRepository(
+ private val dispatcher: CoroutineDispatcher,
+ override val debugName: String = "FakeDisplayScopeRepository",
+) : PerDisplayRepository<CoroutineScope> {
private val perDisplayScopes = mutableMapOf<Int, CoroutineScope>()
- override fun scopeForDisplay(displayId: Int): CoroutineScope {
+ override fun get(displayId: Int): CoroutineScope {
return perDisplayScopes.computeIfAbsent(displayId) { CoroutineScope(dispatcher) }
}
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/PerDisplayStoreKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/PerDisplayStoreKosmos.kt
index 5ab3b3de49f4..161e06295852 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/PerDisplayStoreKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/PerDisplayStoreKosmos.kt
@@ -16,6 +16,10 @@
package com.android.systemui.display.data.repository
+import com.android.app.displaylib.DisplayInstanceLifecycleManager
+import com.android.app.displaylib.FakeDisplayInstanceLifecycleManager
+import com.android.app.displaylib.PerDisplayInstanceProviderWithTeardown
+import com.android.app.displaylib.PerDisplayInstanceRepositoryImpl
import com.android.systemui.dump.dumpManager
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.applicationCoroutineScope
@@ -67,13 +71,25 @@ val Kosmos.fakePerDisplayInstanceProviderWithTeardown by
Kosmos.Fixture { FakePerDisplayInstanceProviderWithTeardown() }
val Kosmos.perDisplayDumpHelper by Kosmos.Fixture { PerDisplayRepoDumpHelper(dumpManager) }
+val Kosmos.fakeDisplayInstanceLifecycleManager by
+ Kosmos.Fixture { FakeDisplayInstanceLifecycleManager() }
+
val Kosmos.fakePerDisplayInstanceRepository by
Kosmos.Fixture {
- PerDisplayInstanceRepositoryImpl(
- debugName = "fakePerDisplayInstanceRepository",
- instanceProvider = fakePerDisplayInstanceProviderWithTeardown,
- testScope.backgroundScope,
- displayRepository,
- perDisplayDumpHelper,
- )
+ { lifecycleManager: DisplayInstanceLifecycleManager? ->
+ PerDisplayInstanceRepositoryImpl(
+ debugName = "fakePerDisplayInstanceRepository",
+ instanceProvider = fakePerDisplayInstanceProviderWithTeardown,
+ lifecycleManager,
+ testScope.backgroundScope,
+ displayRepository,
+ perDisplayDumpHelper,
+ )
+ }
}
+
+fun Kosmos.createPerDisplayInstanceRepository(
+ overrideLifecycleManager: DisplayInstanceLifecycleManager? = null
+): PerDisplayInstanceRepositoryImpl<TestPerDisplayInstance> {
+ return fakePerDisplayInstanceRepository(overrideLifecycleManager)
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyboard/shortcut/KeyboardShortcutHelperKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyboard/shortcut/KeyboardShortcutHelperKosmos.kt
index 739f6c2af2b4..318e5c716ca7 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyboard/shortcut/KeyboardShortcutHelperKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyboard/shortcut/KeyboardShortcutHelperKosmos.kt
@@ -29,6 +29,7 @@ import com.android.systemui.keyboard.shortcut.data.repository.DefaultShortcutCat
import com.android.systemui.keyboard.shortcut.data.repository.InputGestureDataAdapter
import com.android.systemui.keyboard.shortcut.data.repository.InputGestureMaps
import com.android.systemui.keyboard.shortcut.data.repository.ShortcutCategoriesUtils
+import com.android.systemui.keyboard.shortcut.data.repository.ShortcutHelperCustomizationModeRepository
import com.android.systemui.keyboard.shortcut.data.repository.ShortcutHelperInputDeviceRepository
import com.android.systemui.keyboard.shortcut.data.repository.ShortcutHelperStateRepository
import com.android.systemui.keyboard.shortcut.data.repository.ShortcutHelperTestHelper
@@ -41,6 +42,7 @@ import com.android.systemui.keyboard.shortcut.data.source.MultitaskingShortcutsS
import com.android.systemui.keyboard.shortcut.data.source.SystemShortcutsSource
import com.android.systemui.keyboard.shortcut.domain.interactor.ShortcutCustomizationInteractor
import com.android.systemui.keyboard.shortcut.domain.interactor.ShortcutHelperCategoriesInteractor
+import com.android.systemui.keyboard.shortcut.domain.interactor.ShortcutHelperCustomizationModeInteractor
import com.android.systemui.keyboard.shortcut.domain.interactor.ShortcutHelperStateInteractor
import com.android.systemui.keyboard.shortcut.shared.model.ShortcutHelperExclusions
import com.android.systemui.keyboard.shortcut.ui.ShortcutCustomizationDialogStarter
@@ -196,6 +198,14 @@ val Kosmos.shortcutHelperCategoriesInteractor by
}
}
+val Kosmos.shortcutHelperCustomizationModeRepository by
+ Kosmos.Fixture { ShortcutHelperCustomizationModeRepository(shortcutHelperStateRepository) }
+
+val Kosmos.shortcutHelperCustomizationModeInteractor by
+ Kosmos.Fixture {
+ ShortcutHelperCustomizationModeInteractor(shortcutHelperCustomizationModeRepository)
+ }
+
val Kosmos.shortcutHelperViewModel by
Kosmos.Fixture {
ShortcutHelperViewModel(
@@ -206,6 +216,7 @@ val Kosmos.shortcutHelperViewModel by
testDispatcher,
shortcutHelperStateInteractor,
shortcutHelperCategoriesInteractor,
+ shortcutHelperCustomizationModeInteractor,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractorKosmos.kt
index 511bede7349b..41dddce77a30 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractorKosmos.kt
@@ -33,6 +33,7 @@ var Kosmos.fromLockscreenTransitionInteractor by
transitionInteractor = keyguardTransitionInteractor,
internalTransitionInteractor = internalKeyguardTransitionInteractor,
scope = applicationCoroutineScope,
+ applicationScope = applicationCoroutineScope,
bgDispatcher = testDispatcher,
mainDispatcher = testDispatcher,
keyguardInteractor = keyguardInteractor,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/GeneralKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/GeneralKosmos.kt
index 044332981bf8..ae28022e096e 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/GeneralKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/GeneralKosmos.kt
@@ -25,6 +25,7 @@ import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.coroutines.collectValues
import com.android.systemui.kosmos.Kosmos.Fixture
import kotlin.coroutines.CoroutineContext
+import kotlin.time.Duration
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.StateFlow
@@ -32,6 +33,7 @@ import kotlinx.coroutines.launch
import kotlinx.coroutines.test.StandardTestDispatcher
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.UnconfinedTestDispatcher
+import kotlinx.coroutines.test.advanceTimeBy
import kotlinx.coroutines.test.runCurrent
import org.mockito.kotlin.verify
@@ -72,6 +74,8 @@ fun Kosmos.runTest(testBody: suspend Kosmos.() -> Unit) = let { kosmos ->
fun Kosmos.runCurrent() = testScope.runCurrent()
+fun Kosmos.advanceTimeBy(duration: Duration) = testScope.advanceTimeBy(duration)
+
fun <T> Kosmos.collectLastValue(flow: Flow<T>) = testScope.collectLastValue(flow)
fun <T> Kosmos.collectValues(flow: Flow<T>): FlowValue<List<T>> = testScope.collectValues(flow)
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt
index 623989ec5809..c80d7386f67a 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt
@@ -81,6 +81,7 @@ import com.android.systemui.statusbar.disableflags.domain.interactor.disableFlag
import com.android.systemui.statusbar.notification.collection.provider.visualStabilityProvider
import com.android.systemui.statusbar.notification.domain.interactor.activeNotificationsInteractor
import com.android.systemui.statusbar.notification.domain.interactor.seenNotificationsInteractor
+import com.android.systemui.statusbar.notification.row.entryAdapterFactory
import com.android.systemui.statusbar.notification.stack.domain.interactor.headsUpNotificationInteractor
import com.android.systemui.statusbar.notification.stack.domain.interactor.sharedNotificationContainerInteractor
import com.android.systemui.statusbar.phone.fakeAutoHideControllerStore
@@ -206,4 +207,5 @@ class KosmosJavaAdapter() {
val displayTracker by lazy { kosmos.displayTracker }
val fakeShadeDisplaysRepository by lazy { kosmos.fakeShadeDisplaysRepository }
val sysUIStateDispatcher by lazy { kosmos.sysUIStateDispatcher }
+ val entryAdapterFactory by lazy { kosmos.entryAdapterFactory }
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/ui/viewmodel/MediaControlViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/ui/viewmodel/MediaControlViewModelKosmos.kt
index 2f3d3c3e0489..ba1c598a79d7 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/ui/viewmodel/MediaControlViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/ui/viewmodel/MediaControlViewModelKosmos.kt
@@ -17,6 +17,7 @@
package com.android.systemui.media.controls.ui.viewmodel
import android.content.applicationContext
+import com.android.internal.logging.InstanceId
import com.android.systemui.concurrency.fakeExecutor
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testDispatcher
@@ -31,5 +32,9 @@ val Kosmos.mediaControlViewModel by
backgroundExecutor = fakeExecutor,
interactor = mediaControlInteractor,
logger = mediaUiEventLogger,
+ instanceId = InstanceId.fakeInstanceId(-1),
+ onAdded = {},
+ onRemoved = {},
+ onUpdated = {},
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/EditTilesListInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/EditTilesListInteractorKosmos.kt
index bd54fd471807..2cd270e97e61 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/EditTilesListInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/EditTilesListInteractorKosmos.kt
@@ -19,7 +19,7 @@ package com.android.systemui.qs.panels.domain.interactor
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.qs.panels.data.repository.iconAndNameCustomRepository
import com.android.systemui.qs.panels.data.repository.stockTilesRepository
-import com.android.systemui.qs.tiles.viewmodel.qSTileConfigProvider
+import com.android.systemui.qs.tiles.base.shared.model.qSTileConfigProvider
val Kosmos.editTilesListInteractor by
Kosmos.Fixture {
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/FakeTileAvailabilityInteractor.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/FakeTileAvailabilityInteractor.kt
index cc7eb6b4b3dc..7887762270c7 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/FakeTileAvailabilityInteractor.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/FakeTileAvailabilityInteractor.kt
@@ -17,12 +17,11 @@
package com.android.systemui.qs.panels.domain.interactor
import android.os.UserHandle
-import com.android.systemui.qs.tiles.base.interactor.QSTileAvailabilityInteractor
+import com.android.systemui.qs.tiles.base.domain.interactor.QSTileAvailabilityInteractor
import kotlinx.coroutines.flow.Flow
-class FakeTileAvailabilityInteractor(
- private val availabilityFlows: Map<Int, Flow<Boolean>>
-) : QSTileAvailabilityInteractor {
+class FakeTileAvailabilityInteractor(private val availabilityFlows: Map<Int, Flow<Boolean>>) :
+ QSTileAvailabilityInteractor {
override fun availability(user: UserHandle): Flow<Boolean> {
return availabilityFlows.getValue(user.identifier)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/NewTilesAvailabilityInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/NewTilesAvailabilityInteractorKosmos.kt
index 40e6c75ee34a..6a65706e1f11 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/NewTilesAvailabilityInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/NewTilesAvailabilityInteractorKosmos.kt
@@ -17,16 +17,13 @@
package com.android.systemui.qs.panels.domain.interactor
import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.qs.tiles.base.interactor.QSTileAvailabilityInteractor
+import com.android.systemui.qs.tiles.base.domain.interactor.QSTileAvailabilityInteractor
import com.android.systemui.user.data.repository.userRepository
-var Kosmos.tileAvailabilityInteractorsMap by Kosmos.Fixture {
- emptyMap<String, QSTileAvailabilityInteractor>()
-}
+var Kosmos.tileAvailabilityInteractorsMap by
+ Kosmos.Fixture { emptyMap<String, QSTileAvailabilityInteractor>() }
-val Kosmos.newTilesAvailabilityInteractor by Kosmos.Fixture {
- NewTilesAvailabilityInteractor(
- tileAvailabilityInteractorsMap,
- userRepository,
- )
-}
+val Kosmos.newTilesAvailabilityInteractor by
+ Kosmos.Fixture {
+ NewTilesAvailabilityInteractor(tileAvailabilityInteractorsMap, userRepository)
+ }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/DetailsViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/DetailsViewModelKosmos.kt
index dc22905ba320..56fd270dffb7 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/DetailsViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/DetailsViewModelKosmos.kt
@@ -18,5 +18,11 @@ package com.android.systemui.qs.panels.ui.viewmodel
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.qs.pipeline.domain.interactor.currentTilesInteractor
+import com.android.systemui.shade.domain.interactor.shadeModeInteractor
-val Kosmos.detailsViewModel by Kosmos.Fixture { DetailsViewModel(currentTilesInteractor) }
+val Kosmos.detailsViewModel by Kosmos.Fixture {
+ DetailsViewModel(
+ currentTilesInteractor,
+ shadeModeInteractor
+ )
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractorKosmos.kt
index d97a5b2bede2..4823607c2993 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractorKosmos.kt
@@ -29,7 +29,7 @@ import com.android.systemui.qs.pipeline.data.repository.tileSpecRepository
import com.android.systemui.qs.pipeline.shared.logging.qsLogger
import com.android.systemui.qs.pipeline.shared.pipelineFlagsRepository
import com.android.systemui.qs.qsTileFactory
-import com.android.systemui.qs.tiles.di.newQSTileFactory
+import com.android.systemui.qs.tiles.base.ui.model.newQSTileFactory
import com.android.systemui.settings.userTracker
import com.android.systemui.user.data.repository.userRepository
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/actions/FakeQSTileIntentUserInputHandler.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/domain/actions/FakeQSTileIntentUserInputHandler.kt
index f50443ec4e86..1319787cc030 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/actions/FakeQSTileIntentUserInputHandler.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/domain/actions/FakeQSTileIntentUserInputHandler.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.qs.tiles.base.actions
+package com.android.systemui.qs.tiles.base.domain.actions
import android.app.PendingIntent
import android.content.Intent
@@ -34,7 +34,7 @@ class FakeQSTileIntentUserInputHandler : QSTileIntentUserInputHandler {
override fun handle(
expandable: Expandable?,
intent: Intent,
- handleDismissShadeShowOverLockScreenWhenLocked: Boolean
+ handleDismissShadeShowOverLockScreenWhenLocked: Boolean,
) {
mutableInputs.add(Input.Intent(expandable, intent))
}
@@ -42,7 +42,7 @@ class FakeQSTileIntentUserInputHandler : QSTileIntentUserInputHandler {
override fun handle(
expandable: Expandable?,
pendingIntent: PendingIntent,
- requestLaunchingDefaultActivity: Boolean
+ requestLaunchingDefaultActivity: Boolean,
) {
mutableInputs.add(
Input.PendingIntent(expandable, pendingIntent, requestLaunchingDefaultActivity)
@@ -51,10 +51,11 @@ class FakeQSTileIntentUserInputHandler : QSTileIntentUserInputHandler {
sealed interface Input {
data class Intent(val expandable: Expandable?, val intent: android.content.Intent) : Input
+
data class PendingIntent(
val expandable: Expandable?,
val pendingIntent: android.app.PendingIntent,
- val requestLaunchingDefaultActivity: Boolean
+ val requestLaunchingDefaultActivity: Boolean,
) : Input
}
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserInputHandlerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/domain/actions/QSTileIntentUserInputHandlerKosmos.kt
index ccfb6092a2e3..b884b8a8306b 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserInputHandlerKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/domain/actions/QSTileIntentUserInputHandlerKosmos.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.qs.tiles.base.actions
+package com.android.systemui.qs.tiles.base.domain.actions
import com.android.systemui.kosmos.Kosmos
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserInputHandlerSubject.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/domain/actions/QSTileIntentUserInputHandlerSubject.kt
index c1e689c0165c..3b7810ea22f0 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/actions/QSTileIntentUserInputHandlerSubject.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/domain/actions/QSTileIntentUserInputHandlerSubject.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.qs.tiles.base.actions
+package com.android.systemui.qs.tiles.base.domain.actions
import com.google.common.truth.FailureMetadata
import com.google.common.truth.Subject
@@ -24,11 +24,11 @@ import com.google.common.truth.Truth
class QSTileIntentUserInputHandlerSubject
private constructor(
failureMetadata: FailureMetadata,
- private val subject: FakeQSTileIntentUserInputHandler
+ private val subject: FakeQSTileIntentUserInputHandler,
) : Subject(failureMetadata, subject) {
fun handledOneIntentInput(
- intentAssertions: (FakeQSTileIntentUserInputHandler.Input.Intent) -> Unit = {},
+ intentAssertions: (FakeQSTileIntentUserInputHandler.Input.Intent) -> Unit = {}
) {
// check that there are no other inputs
check("handledInputs").that(subject.handledInputs).hasSize(1)
@@ -39,7 +39,7 @@ private constructor(
}
fun handledOnePendingIntentInput(
- intentAssertions: (FakeQSTileIntentUserInputHandler.Input.PendingIntent) -> Unit = {},
+ intentAssertions: (FakeQSTileIntentUserInputHandler.Input.PendingIntent) -> Unit = {}
) {
// check that there are no other inputs
check("handledInputs").that(subject.handledInputs).hasSize(1)
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/interactor/DisabledByPolicyInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/domain/interactor/DisabledByPolicyInteractorKosmos.kt
index 9ad49f052c9e..2ffc47972484 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/interactor/DisabledByPolicyInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/domain/interactor/DisabledByPolicyInteractorKosmos.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.qs.tiles.base.interactor
+package com.android.systemui.qs.tiles.base.domain.interactor
import com.android.systemui.kosmos.Kosmos
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/interactor/FakeDisabledByPolicyInteractor.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/domain/interactor/FakeDisabledByPolicyInteractor.kt
index fb6ba20e4c51..5bdeeab33f37 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/interactor/FakeDisabledByPolicyInteractor.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/domain/interactor/FakeDisabledByPolicyInteractor.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.qs.tiles.base.interactor
+package com.android.systemui.qs.tiles.base.domain.interactor
import android.os.UserHandle
import com.android.settingslib.RestrictedLockUtils
@@ -23,7 +23,7 @@ class FakeDisabledByPolicyInteractor : DisabledByPolicyInteractor {
override suspend fun isDisabled(
user: UserHandle,
- userRestriction: String?
+ userRestriction: String?,
): DisabledByPolicyInteractor.PolicyResult =
if (userRestriction == DISABLED_RESTRICTION || userRestriction == DISABLED_RESTRICTION_2) {
DisabledByPolicyInteractor.PolicyResult.TileDisabled(
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/interactor/FakeQSTileDataInteractor.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/domain/interactor/FakeQSTileDataInteractor.kt
index 3fcf8a93dc87..6bd8152665ac 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/interactor/FakeQSTileDataInteractor.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/domain/interactor/FakeQSTileDataInteractor.kt
@@ -14,9 +14,10 @@
* limitations under the License.
*/
-package com.android.systemui.qs.tiles.base.interactor
+package com.android.systemui.qs.tiles.base.domain.interactor
import android.os.UserHandle
+import com.android.systemui.qs.tiles.base.domain.model.DataUpdateTrigger
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.flatMapLatest
@@ -26,6 +27,7 @@ class FakeQSTileDataInteractor<T> : QSTileDataInteractor<T> {
private val dataFlow: MutableSharedFlow<T> = MutableSharedFlow(replay = 1)
val dataSubscriptionCount
get() = dataFlow.subscriptionCount
+
private val availabilityFlow: MutableSharedFlow<Boolean> = MutableSharedFlow(replay = 1)
val availabilitySubscriptionCount
get() = availabilityFlow.subscriptionCount
@@ -42,6 +44,7 @@ class FakeQSTileDataInteractor<T> : QSTileDataInteractor<T> {
suspend fun emitData(data: T): Unit = dataFlow.emit(data)
fun tryEmitAvailability(isAvailable: Boolean): Boolean = availabilityFlow.tryEmit(isAvailable)
+
suspend fun emitAvailability(isAvailable: Boolean) = availabilityFlow.emit(isAvailable)
override fun tileData(user: UserHandle, triggers: Flow<DataUpdateTrigger>): Flow<T> {
@@ -58,5 +61,6 @@ class FakeQSTileDataInteractor<T> : QSTileDataInteractor<T> {
}
data class DataRequest(val user: UserHandle)
+
data class AvailabilityRequest(val user: UserHandle)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/interactor/FakeQSTileUserActionInteractor.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/domain/interactor/FakeQSTileUserActionInteractor.kt
index c0584903db2d..8fd87a30e2fe 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/interactor/FakeQSTileUserActionInteractor.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/domain/interactor/FakeQSTileUserActionInteractor.kt
@@ -14,10 +14,9 @@
* limitations under the License.
*/
-package com.android.systemui.qs.tiles.base.interactor
+package com.android.systemui.qs.tiles.base.domain.interactor
-import com.android.systemui.plugins.qs.TileDetailsViewModel
-import com.android.systemui.qs.FakeTileDetailsViewModel
+import com.android.systemui.qs.tiles.base.domain.model.QSTileInput
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/interactor/QSTileInputTestKtx.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/domain/model/QSTileInputTestKtx.kt
index 3943d1d7ec01..5c2b4a641b0e 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/interactor/QSTileInputTestKtx.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/domain/model/QSTileInputTestKtx.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2025 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.
@@ -14,11 +14,11 @@
* limitations under the License.
*/
-package com.android.systemui.qs.tiles.base.interactor
+package com.android.systemui.qs.tiles.base.domain.model
import android.os.UserHandle
import com.android.systemui.animation.Expandable
-import com.android.systemui.qs.tiles.viewmodel.QSTileUserAction
+import com.android.systemui.qs.tiles.base.shared.model.QSTileUserAction
object QSTileInputTestKtx {
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/viewmodel/FakeQSTileConfigProvider.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/shared/model/FakeQSTileConfigProvider.kt
index d231d63a3906..97e65831b1d0 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/viewmodel/FakeQSTileConfigProvider.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/shared/model/FakeQSTileConfigProvider.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2025 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.qs.tiles.viewmodel
+package com.android.systemui.qs.tiles.base.shared.model
import com.android.systemui.qs.pipeline.shared.TileSpec
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfigProviderKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/shared/model/QSTileConfigProviderKosmos.kt
index 1d579797bcb3..d1b410c4f5f3 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfigProviderKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/shared/model/QSTileConfigProviderKosmos.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2024 The Android Open Source Project
+ * Copyright (C) 2025 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.qs.tiles.viewmodel
+package com.android.systemui.qs.tiles.base.shared.model
import com.android.systemui.kosmos.Kosmos
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfigTestBuilder.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/shared/model/QSTileConfigTestBuilder.kt
index 73d9b3233375..8938ebc2317e 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/viewmodel/QSTileConfigTestBuilder.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/shared/model/QSTileConfigTestBuilder.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2025 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.qs.tiles.viewmodel
+package com.android.systemui.qs.tiles.base.shared.model
import com.android.internal.logging.InstanceId
import com.android.systemui.qs.pipeline.shared.TileSpec
@@ -33,14 +33,6 @@ object QSTileConfigTestBuilder {
var policy: QSTilePolicy = QSTilePolicy.NoRestrictions
var category: TileCategory = TileCategory.UNKNOWN
- fun build() =
- QSTileConfig(
- tileSpec,
- uiConfig,
- instanceId,
- category,
- metricsSpec,
- policy,
- )
+ fun build() = QSTileConfig(tileSpec, uiConfig, instanceId, category, metricsSpec, policy)
}
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/analytics/QSTileAnalyticsKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/ui/analytics/QSTileAnalyticsKosmos.kt
index 146c1ad6ab70..753824b2a1c8 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/analytics/QSTileAnalyticsKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/ui/analytics/QSTileAnalyticsKosmos.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.qs.tiles.base.analytics
+package com.android.systemui.qs.tiles.base.ui.analytics
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.util.mockito.mock
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/di/NewQSTileFactoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/ui/model/NewQSTileFactoryKosmos.kt
index c223be44a70c..3c2973077174 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/di/NewQSTileFactoryKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/ui/model/NewQSTileFactoryKosmos.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2024 The Android Open Source Project
+ * Copyright (C) 2025 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.qs.tiles.di
+package com.android.systemui.qs.tiles.base.ui.model
import android.os.UserHandle
import com.android.systemui.kosmos.Kosmos
@@ -22,14 +22,14 @@ import com.android.systemui.qs.instanceIdSequenceFake
import com.android.systemui.qs.pipeline.domain.interactor.currentTilesInteractor
import com.android.systemui.qs.pipeline.shared.TileSpec
import com.android.systemui.qs.shared.model.TileCategory
-import com.android.systemui.qs.tiles.base.viewmodel.QSTileViewModelFactory
-import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
-import com.android.systemui.qs.tiles.viewmodel.QSTileState
-import com.android.systemui.qs.tiles.viewmodel.QSTileUIConfig
-import com.android.systemui.qs.tiles.viewmodel.QSTileUserAction
-import com.android.systemui.qs.tiles.viewmodel.QSTileViewModel
-import com.android.systemui.qs.tiles.viewmodel.qSTileConfigProvider
-import com.android.systemui.qs.tiles.viewmodel.qsTileViewModelAdaperFactory
+import com.android.systemui.qs.tiles.base.shared.model.QSTileConfig
+import com.android.systemui.qs.tiles.base.shared.model.QSTileState
+import com.android.systemui.qs.tiles.base.shared.model.QSTileUIConfig
+import com.android.systemui.qs.tiles.base.shared.model.QSTileUserAction
+import com.android.systemui.qs.tiles.base.shared.model.qSTileConfigProvider
+import com.android.systemui.qs.tiles.base.ui.viewmodel.QSTileViewModel
+import com.android.systemui.qs.tiles.base.ui.viewmodel.QSTileViewModelFactory
+import com.android.systemui.qs.tiles.base.ui.viewmodel.qsTileViewModelAdaperFactory
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/custom/QSTileStateSubject.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/ui/model/QSTileStateSubject.kt
index 657a95a3261e..54e85f1c2718 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/custom/QSTileStateSubject.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/ui/model/QSTileStateSubject.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2025 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.
@@ -14,15 +14,11 @@
* limitations under the License.
*/
-package com.android.systemui.qs.tiles.impl.custom
+package com.android.systemui.qs.tiles.base.ui.model
-import com.android.systemui.qs.tiles.impl.custom.QSTileStateSubject.Companion.assertThat
-import com.android.systemui.qs.tiles.impl.custom.QSTileStateSubject.Companion.states
-import com.android.systemui.qs.tiles.impl.custom.TileSubject.Companion.assertThat
-import com.android.systemui.qs.tiles.viewmodel.QSTileState
+import com.android.systemui.qs.tiles.base.shared.model.QSTileState
import com.google.common.truth.FailureMetadata
import com.google.common.truth.Subject
-import com.google.common.truth.Subject.Factory
import com.google.common.truth.Truth
/**
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelAdapterKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/ui/viewmodel/QSTileViewModelAdapterKosmos.kt
index de9f629ef787..cbadf8eb149d 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/viewmodel/QSTileViewModelAdapterKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/base/ui/viewmodel/QSTileViewModelAdapterKosmos.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2024 The Android Open Source Project
+ * Copyright (C) 2025 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.qs.tiles.viewmodel
+package com.android.systemui.qs.tiles.base.ui.viewmodel
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.applicationCoroutineScope
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/custom/CustomTileKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/custom/CustomTileKosmos.kt
index 42437d5a5b81..77f561e3e9a6 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/custom/CustomTileKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/custom/CustomTileKosmos.kt
@@ -22,11 +22,12 @@ import com.android.systemui.kosmos.testDispatcher
import com.android.systemui.kosmos.testScope
import com.android.systemui.plugins.activityStarter
import com.android.systemui.qs.external.FakeCustomTileStatePersister
-import com.android.systemui.qs.external.tileServices
import com.android.systemui.qs.external.tileServicesFacade
import com.android.systemui.qs.pipeline.shared.TileSpec
-import com.android.systemui.qs.tiles.base.actions.FakeQSTileIntentUserInputHandler
-import com.android.systemui.qs.tiles.base.logging.QSTileLogger
+import com.android.systemui.qs.tiles.base.domain.actions.FakeQSTileIntentUserInputHandler
+import com.android.systemui.qs.tiles.base.shared.logging.QSTileLogger
+import com.android.systemui.qs.tiles.base.shared.model.QSTileConfig
+import com.android.systemui.qs.tiles.base.shared.model.QSTileConfigTestBuilder
import com.android.systemui.qs.tiles.impl.custom.data.repository.FakeCustomTileDefaultsRepository
import com.android.systemui.qs.tiles.impl.custom.data.repository.FakeCustomTilePackageUpdatesRepository
import com.android.systemui.qs.tiles.impl.custom.data.repository.FakeCustomTileRepository
@@ -34,8 +35,6 @@ import com.android.systemui.qs.tiles.impl.custom.data.repository.FakePackageMana
import com.android.systemui.qs.tiles.impl.custom.domain.interactor.CustomTileInteractor
import com.android.systemui.qs.tiles.impl.custom.domain.interactor.CustomTileServiceInteractor
import com.android.systemui.qs.tiles.impl.custom.domain.interactor.CustomTileUserActionInteractor
-import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
-import com.android.systemui.qs.tiles.viewmodel.QSTileConfigTestBuilder
import com.android.systemui.user.data.repository.userRepository
import com.android.systemui.util.mockito.mock
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/custom/data/repository/FakeCustomTileDefaultsRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/custom/data/repository/FakeCustomTileDefaultsRepository.kt
index ccba07273f1e..7b9d601c59ff 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/custom/data/repository/FakeCustomTileDefaultsRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/custom/data/repository/FakeCustomTileDefaultsRepository.kt
@@ -18,7 +18,7 @@ package com.android.systemui.qs.tiles.impl.custom.data.repository
import android.content.ComponentName
import android.os.UserHandle
-import com.android.systemui.qs.tiles.impl.custom.data.entity.CustomTileDefaults
+import com.android.systemui.qs.tiles.impl.custom.data.model.CustomTileDefaults
import kotlinx.coroutines.channels.BufferOverflow
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableSharedFlow
@@ -31,7 +31,7 @@ class FakeCustomTileDefaultsRepository : CustomTileDefaultsRepository {
private val defaultsFlow =
MutableSharedFlow<DefaultsRequest>(
replay = 1,
- onBufferOverflow = BufferOverflow.DROP_OLDEST
+ onBufferOverflow = BufferOverflow.DROP_OLDEST,
)
private val mutableDefaultsRequests: MutableList<DefaultsRequest> = mutableListOf()
@@ -51,7 +51,7 @@ class FakeCustomTileDefaultsRepository : CustomTileDefaultsRepository {
override fun requestNewDefaults(
user: UserHandle,
componentName: ComponentName,
- force: Boolean
+ force: Boolean,
) {
val request = DefaultsRequest(user, componentName, force)
mutableDefaultsRequests.add(request)
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/custom/data/repository/FakeCustomTileRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/custom/data/repository/FakeCustomTileRepository.kt
index c110da057a4e..c9ed71c3ae21 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/custom/data/repository/FakeCustomTileRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/custom/data/repository/FakeCustomTileRepository.kt
@@ -20,7 +20,7 @@ import android.os.UserHandle
import android.service.quicksettings.Tile
import com.android.systemui.qs.external.FakeCustomTileStatePersister
import com.android.systemui.qs.pipeline.shared.TileSpec
-import com.android.systemui.qs.tiles.impl.custom.data.entity.CustomTileDefaults
+import com.android.systemui.qs.tiles.impl.custom.data.model.CustomTileDefaults
import kotlin.coroutines.CoroutineContext
import kotlinx.coroutines.flow.Flow
@@ -54,11 +54,8 @@ class FakeCustomTileRepository(
override suspend fun isTileToggleable(): Boolean = realDelegate.isTileToggleable()
- override suspend fun updateWithTile(
- user: UserHandle,
- newTile: Tile,
- isPersistable: Boolean,
- ) = realDelegate.updateWithTile(user, newTile, isPersistable)
+ override suspend fun updateWithTile(user: UserHandle, newTile: Tile, isPersistable: Boolean) =
+ realDelegate.updateWithTile(user, newTile, isPersistable)
override suspend fun updateWithDefaults(
user: UserHandle,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileUserActionInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileUserActionInteractorKosmos.kt
index 3f07d05a4786..f94da17ad923 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileUserActionInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileUserActionInteractorKosmos.kt
@@ -18,7 +18,7 @@ package com.android.systemui.qs.tiles.impl.modes.domain.interactor
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.mainCoroutineContext
-import com.android.systemui.qs.tiles.base.actions.qsTileIntentUserInputHandler
+import com.android.systemui.qs.tiles.base.domain.actions.qsTileIntentUserInputHandler
import com.android.systemui.statusbar.policy.domain.interactor.zenModeInteractor
import com.android.systemui.statusbar.policy.ui.dialog.modesDialogDelegate
import com.android.systemui.statusbar.policy.ui.dialog.modesDialogEventLogger
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/qr/QRCodeScannerTileKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/qr/QRCodeScannerTileKosmos.kt
index 537be4fc2527..5cd11e065553 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/qr/QRCodeScannerTileKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/qr/QRCodeScannerTileKosmos.kt
@@ -23,17 +23,17 @@ import com.android.systemui.kosmos.applicationCoroutineScope
import com.android.systemui.kosmos.backgroundCoroutineContext
import com.android.systemui.kosmos.testDispatcher
import com.android.systemui.kosmos.testScope
-import com.android.systemui.qrcodescanner.dagger.QRCodeScannerModule
import com.android.systemui.qrcodescanner.qrCodeScannerController
import com.android.systemui.qs.qsEventLogger
-import com.android.systemui.qs.tiles.base.actions.qsTileIntentUserInputHandler
-import com.android.systemui.qs.tiles.base.analytics.qsTileAnalytics
-import com.android.systemui.qs.tiles.base.interactor.fakeDisabledByPolicyInteractor
-import com.android.systemui.qs.tiles.base.viewmodel.QSTileViewModelImpl
+import com.android.systemui.qs.tiles.base.domain.actions.qsTileIntentUserInputHandler
+import com.android.systemui.qs.tiles.base.domain.interactor.fakeDisabledByPolicyInteractor
+import com.android.systemui.qs.tiles.base.ui.analytics.qsTileAnalytics
+import com.android.systemui.qs.tiles.base.ui.viewmodel.QSTileViewModelImpl
import com.android.systemui.qs.tiles.impl.custom.qsTileLogger
import com.android.systemui.qs.tiles.impl.qr.domain.interactor.QRCodeScannerTileDataInteractor
import com.android.systemui.qs.tiles.impl.qr.domain.interactor.QRCodeScannerTileUserActionInteractor
-import com.android.systemui.qs.tiles.impl.qr.ui.QRCodeScannerTileMapper
+import com.android.systemui.qs.tiles.impl.qr.ui.mapper.QRCodeScannerTileMapper
+import com.android.systemui.qs.tiles.impl.qr.ui.model.QRCodeScannerModule
import com.android.systemui.user.data.repository.fakeUserRepository
import com.android.systemui.util.time.systemClock
@@ -45,7 +45,7 @@ val Kosmos.qrCodeScannerTileDataInteractor by
QRCodeScannerTileDataInteractor(
backgroundCoroutineContext,
applicationCoroutineScope,
- qrCodeScannerController
+ qrCodeScannerController,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/uimodenight/UiModeNightTileModelHelper.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/uimodenight/UiModeNightTileModelHelper.kt
index 1fe18e3f4d8e..0045960c19ce 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/uimodenight/UiModeNightTileModelHelper.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/uimodenight/UiModeNightTileModelHelper.kt
@@ -17,7 +17,7 @@
package com.android.systemui.qs.tiles.impl.uimodenight
import android.content.res.Configuration
-import com.android.systemui.qs.tiles.impl.uimodenight.domain.model.UiModeNightTileModel
+import com.android.systemui.qs.tiles.impl.uimodenight.domain.interactor.model.UiModeNightTileModel
import java.time.LocalTime
object UiModeNightTileModelHelper {
@@ -34,7 +34,7 @@ object UiModeNightTileModelHelper {
nighModeCustomType: Int = DEFAULT_NIGHT_MODE_CUSTOM_TYPE,
is24HourFormat: Boolean = false,
customNightModeEnd: LocalTime = defaultCustomNightEnd,
- customNightModeStart: LocalTime = defaultCustomNightStart
+ customNightModeStart: LocalTime = defaultCustomNightStart,
): UiModeNightTileModel {
return UiModeNightTileModel(
uiMode,
@@ -44,7 +44,7 @@ object UiModeNightTileModelHelper {
nighModeCustomType,
is24HourFormat,
customNightModeEnd,
- customNightModeStart
+ customNightModeStart,
)
}
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/startable/SceneContainerStartableKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/startable/SceneContainerStartableKosmos.kt
index 7a9b052481cb..349e670a9af3 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/startable/SceneContainerStartableKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/startable/SceneContainerStartableKosmos.kt
@@ -46,7 +46,6 @@ import com.android.systemui.scene.domain.interactor.sceneContainerOcclusionInter
import com.android.systemui.scene.domain.interactor.sceneInteractor
import com.android.systemui.scene.session.shared.shadeSessionStorage
import com.android.systemui.scene.shared.logger.sceneLogger
-import com.android.systemui.settings.displayTracker
import com.android.systemui.shade.domain.interactor.shadeInteractor
import com.android.systemui.shade.domain.interactor.shadeModeInteractor
import com.android.systemui.statusbar.notification.stack.domain.interactor.headsUpNotificationInteractor
@@ -65,7 +64,6 @@ val Kosmos.sceneContainerStartable by Fixture {
bouncerInteractor = bouncerInteractor,
keyguardInteractor = keyguardInteractor,
sysUiState = sysUiState,
- displayId = displayTracker.defaultDisplayId,
sceneLogger = sceneLogger,
falsingCollector = falsingCollector,
falsingManager = falsingManager,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModelKosmos.kt
index 34e5bfde43c9..9a9f3354a7ba 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModelKosmos.kt
@@ -14,10 +14,14 @@
* limitations under the License.
*/
+@file:OptIn(ExperimentalKairosApi::class)
+
package com.android.systemui.shade.ui.viewmodel
import android.content.applicationContext
import com.android.systemui.battery.batteryMeterViewControllerFactory
+import com.android.systemui.kairos.ExperimentalKairosApi
+import com.android.systemui.kairos.kairos
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.plugins.activityStarter
import com.android.systemui.scene.domain.interactor.sceneInteractor
@@ -46,6 +50,8 @@ val Kosmos.shadeHeaderViewModel: ShadeHeaderViewModel by
tintedIconManagerFactory = tintedIconManagerFactory,
batteryMeterViewControllerFactory = batteryMeterViewControllerFactory,
statusBarIconController = mock<StatusBarIconController>(),
+ kairosNetwork = kairos,
+ mobileIconsViewModelKairos = mock(),
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModelKosmos.kt
index ab61a3ef4c3f..553e876bbb26 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModelKosmos.kt
@@ -22,6 +22,7 @@ import com.android.systemui.kosmos.applicationCoroutineScope
import com.android.systemui.plugins.activityStarter
import com.android.systemui.statusbar.chips.call.domain.interactor.callChipInteractor
import com.android.systemui.statusbar.chips.statusBarChipsLogger
+import com.android.systemui.statusbar.chips.uievents.statusBarChipsUiEventLogger
import com.android.systemui.util.time.fakeSystemClock
val Kosmos.callChipViewModel: CallChipViewModel by
@@ -33,5 +34,6 @@ val Kosmos.callChipViewModel: CallChipViewModel by
systemClock = fakeSystemClock,
activityStarter = activityStarter,
logger = statusBarChipsLogger,
+ uiEventLogger = statusBarChipsUiEventLogger,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/viewmodel/CastToOtherDeviceChipViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/viewmodel/CastToOtherDeviceChipViewModelKosmos.kt
index c7dfd5cc93b9..7e8dfa7e585c 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/viewmodel/CastToOtherDeviceChipViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/viewmodel/CastToOtherDeviceChipViewModelKosmos.kt
@@ -17,7 +17,6 @@
package com.android.systemui.statusbar.chips.casttootherdevice.ui.viewmodel
import android.content.applicationContext
-import com.android.systemui.animation.dialogTransitionAnimator
import com.android.systemui.animation.mockDialogTransitionAnimator
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.applicationCoroutineScope
@@ -25,6 +24,7 @@ import com.android.systemui.statusbar.chips.casttootherdevice.domain.interactor.
import com.android.systemui.statusbar.chips.mediaprojection.domain.interactor.mediaProjectionChipInteractor
import com.android.systemui.statusbar.chips.mediaprojection.ui.view.endMediaProjectionDialogHelper
import com.android.systemui.statusbar.chips.statusBarChipsLogger
+import com.android.systemui.statusbar.chips.uievents.statusBarChipsUiEventLogger
import com.android.systemui.util.time.fakeSystemClock
val Kosmos.castToOtherDeviceChipViewModel: CastToOtherDeviceChipViewModel by
@@ -38,5 +38,6 @@ val Kosmos.castToOtherDeviceChipViewModel: CastToOtherDeviceChipViewModel by
endMediaProjectionDialogHelper = endMediaProjectionDialogHelper,
dialogTransitionAnimator = mockDialogTransitionAnimator,
logger = statusBarChipsLogger,
+ uiEventLogger = statusBarChipsUiEventLogger,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/screenrecord/ui/viewmodel/ScreenRecordChipViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/screenrecord/ui/viewmodel/ScreenRecordChipViewModelKosmos.kt
index c2a6f7d91eb0..0cd83d348f9a 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/screenrecord/ui/viewmodel/ScreenRecordChipViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/screenrecord/ui/viewmodel/ScreenRecordChipViewModelKosmos.kt
@@ -24,6 +24,7 @@ import com.android.systemui.statusbar.chips.mediaprojection.ui.view.endMediaProj
import com.android.systemui.statusbar.chips.screenrecord.domain.interactor.screenRecordChipInteractor
import com.android.systemui.statusbar.chips.sharetoapp.ui.viewmodel.shareToAppChipViewModel
import com.android.systemui.statusbar.chips.statusBarChipsLogger
+import com.android.systemui.statusbar.chips.uievents.statusBarChipsUiEventLogger
import com.android.systemui.util.time.fakeSystemClock
val Kosmos.screenRecordChipViewModel: ScreenRecordChipViewModel by
@@ -37,5 +38,6 @@ val Kosmos.screenRecordChipViewModel: ScreenRecordChipViewModel by
dialogTransitionAnimator = mockDialogTransitionAnimator,
systemClock = fakeSystemClock,
logger = statusBarChipsLogger,
+ uiEventLogger = statusBarChipsUiEventLogger,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/sharetoapp/ui/viewmodel/ShareToAppChipViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/sharetoapp/ui/viewmodel/ShareToAppChipViewModelKosmos.kt
index 0770009f9998..c8d2b5d4c8f7 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/sharetoapp/ui/viewmodel/ShareToAppChipViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/sharetoapp/ui/viewmodel/ShareToAppChipViewModelKosmos.kt
@@ -23,6 +23,7 @@ import com.android.systemui.kosmos.applicationCoroutineScope
import com.android.systemui.statusbar.chips.mediaprojection.domain.interactor.mediaProjectionChipInteractor
import com.android.systemui.statusbar.chips.mediaprojection.ui.view.endMediaProjectionDialogHelper
import com.android.systemui.statusbar.chips.statusBarChipsLogger
+import com.android.systemui.statusbar.chips.uievents.statusBarChipsUiEventLogger
import com.android.systemui.util.time.fakeSystemClock
val Kosmos.shareToAppChipViewModel: ShareToAppChipViewModel by
@@ -35,5 +36,6 @@ val Kosmos.shareToAppChipViewModel: ShareToAppChipViewModel by
endMediaProjectionDialogHelper = endMediaProjectionDialogHelper,
dialogTransitionAnimator = mockDialogTransitionAnimator,
logger = statusBarChipsLogger,
+ uiEventLogger = statusBarChipsUiEventLogger,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/uievents/StatusBarChipsUiEventLoggerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/uievents/StatusBarChipsUiEventLoggerKosmos.kt
new file mode 100644
index 000000000000..a3ba29b66b5c
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/uievents/StatusBarChipsUiEventLoggerKosmos.kt
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2025 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.chips.uievents
+
+import com.android.internal.logging.uiEventLoggerFake
+import com.android.systemui.kosmos.Kosmos
+
+val Kosmos.statusBarChipsUiEventLogger: StatusBarChipsUiEventLogger by
+ Kosmos.Fixture { StatusBarChipsUiEventLogger(uiEventLoggerFake) }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/PrivacyDotWindowControllerStoreKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/PrivacyDotWindowControllerStoreKosmos.kt
index aae32cfaafa6..f83fcb32aafe 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/PrivacyDotWindowControllerStoreKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/PrivacyDotWindowControllerStoreKosmos.kt
@@ -16,8 +16,6 @@
package com.android.systemui.statusbar.data.repository
-import android.view.WindowManager
-import com.android.app.viewcapture.ViewCaptureAwareWindowManager
import com.android.systemui.display.data.repository.displayRepository
import com.android.systemui.display.data.repository.displayWindowPropertiesRepository
import com.android.systemui.kosmos.Kosmos
@@ -35,14 +33,6 @@ val Kosmos.privacyDotWindowControllerStoreImpl by
windowControllerFactory = { _, _, _, _ -> mock() },
displayWindowPropertiesRepository = displayWindowPropertiesRepository,
privacyDotViewControllerStore = privacyDotViewControllerStore,
- viewCaptureAwareWindowManagerFactory =
- object : ViewCaptureAwareWindowManager.Factory {
- override fun create(
- windowManager: WindowManager
- ): ViewCaptureAwareWindowManager {
- return mock()
- }
- },
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/events/PrivacyDotWindowControllerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/events/PrivacyDotWindowControllerKosmos.kt
index c73838708a7a..da4027e46783 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/events/PrivacyDotWindowControllerKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/events/PrivacyDotWindowControllerKosmos.kt
@@ -17,8 +17,8 @@
package com.android.systemui.statusbar.events
import android.content.testableContext
+import android.view.fakeWindowManager
import android.view.layoutInflater
-import com.android.app.viewcapture.realCaptureAwareWindowManager
import com.android.systemui.concurrency.fakeExecutor
import com.android.systemui.decor.privacyDotDecorProviderFactory
import com.android.systemui.kosmos.Kosmos
@@ -28,7 +28,7 @@ var Kosmos.privacyDotWindowController by
PrivacyDotWindowController(
testableContext.displayId,
privacyDotViewController,
- realCaptureAwareWindowManager,
+ fakeWindowManager,
layoutInflater,
fakeExecutor,
privacyDotDecorProviderFactory,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/NotificationEntryBuilderKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/collection/NotificationEntryBuilderKosmos.kt
index 59f5ecd2563f..00c6c9445fe7 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/NotificationEntryBuilderKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/collection/NotificationEntryBuilderKosmos.kt
@@ -14,18 +14,25 @@
* limitations under the License.
*/
-package com.android.systemui.statusbar.notification
+package com.android.systemui.statusbar.notification.collection
import android.app.Notification
+import android.app.NotificationChannel
+import android.app.NotificationManager.IMPORTANCE_DEFAULT
import android.app.PendingIntent
import android.app.Person
import android.content.Intent
import android.content.applicationContext
+import android.graphics.Bitmap
import android.graphics.drawable.Icon
+import com.android.systemui.activity.EmptyTestActivity
import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.statusbar.notification.collection.NotificationEntry
-import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder
+import com.android.systemui.res.R
import com.android.systemui.statusbar.notification.icon.IconPack
+import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier.Companion.PeopleNotificationType
+import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier.Companion.TYPE_FULL_PERSON
+import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier.Companion.TYPE_IMPORTANT_PERSON
+import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier.Companion.TYPE_NON_PERSON
import com.android.systemui.statusbar.notification.promoted.setPromotedContent
import org.mockito.kotlin.mock
@@ -40,6 +47,11 @@ fun Kosmos.setIconPackWithMockIconViews(entry: NotificationEntry) {
)
}
+fun Kosmos.buildPromotedOngoingEntry(
+ block: NotificationEntryBuilder.() -> Unit = {}
+): NotificationEntry =
+ buildNotificationEntry(tag = "ron", promoted = true, style = null, block = block)
+
fun Kosmos.buildOngoingCallEntry(
promoted: Boolean = false,
block: NotificationEntryBuilder.() -> Unit = {},
@@ -51,11 +63,6 @@ fun Kosmos.buildOngoingCallEntry(
block = block,
)
-fun Kosmos.buildPromotedOngoingEntry(
- block: NotificationEntryBuilder.() -> Unit = {}
-): NotificationEntry =
- buildNotificationEntry(tag = "ron", promoted = true, style = null, block = block)
-
fun Kosmos.buildNotificationEntry(
tag: String? = null,
promoted: Boolean = false,
@@ -88,3 +95,49 @@ private fun Kosmos.makeOngoingCallStyle(): Notification.CallStyle {
val person = Person.Builder().setName("person").build()
return Notification.CallStyle.forOngoingCall(person, pendingIntent)
}
+
+private fun Kosmos.makeMessagingStyleNotification(): Notification.Builder {
+ val personIcon = Icon.createWithBitmap(Bitmap.createBitmap(10, 10, Bitmap.Config.ARGB_8888))
+ val person = Person.Builder().setIcon(personIcon).setName("Person").build()
+ val message = Notification.MessagingStyle.Message("Message!", 4323, person)
+ val bubbleIntent =
+ PendingIntent.getActivity(
+ applicationContext,
+ 0,
+ Intent(applicationContext, EmptyTestActivity::class.java),
+ PendingIntent.FLAG_MUTABLE,
+ )
+
+ return Notification.Builder(applicationContext, "channelId")
+ .setSmallIcon(R.drawable.ic_person)
+ .setContentTitle("Title")
+ .setContentText("Text")
+ .setStyle(Notification.MessagingStyle(person).addMessage(message))
+ .setBubbleMetadata(
+ Notification.BubbleMetadata.Builder(
+ bubbleIntent,
+ Icon.createWithResource(applicationContext, R.drawable.android),
+ )
+ .setDeleteIntent(mock<PendingIntent>())
+ .setDesiredHeight(314)
+ .setAutoExpandBubble(false)
+ .build()
+ )
+}
+
+fun Kosmos.makeEntryOfPeopleType(@PeopleNotificationType type: Int): NotificationEntryBuilder {
+ val channel = NotificationChannel("messages", "messages", IMPORTANCE_DEFAULT)
+ channel.isImportantConversation = (type == TYPE_IMPORTANT_PERSON)
+ channel.setConversationId("parent", "convo")
+
+ val entry =
+ NotificationEntryBuilder().apply {
+ updateRanking {
+ it.setIsConversation(type != TYPE_NON_PERSON)
+ it.setShortcutInfo(if (type >= TYPE_FULL_PERSON) mock() else null)
+ it.setChannel(channel)
+ }
+ setNotification(makeMessagingStyleNotification().build())
+ }
+ return entry
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/collection/NotificationEntryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/collection/NotificationEntryKosmos.kt
new file mode 100644
index 000000000000..e127a70dc07d
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/collection/NotificationEntryKosmos.kt
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2025 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.notification.collection
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier.Companion.TYPE_FULL_PERSON
+
+val Kosmos.msgStyleBubbleableFullPerson by
+ Kosmos.Fixture { makeEntryOfPeopleType(TYPE_FULL_PERSON).build() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorKosmos.kt
index 1a5c61a04c9f..aba4942b44e4 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorKosmos.kt
@@ -32,6 +32,7 @@ import com.android.systemui.statusbar.notification.stack.data.repository.headsUp
import com.android.systemui.statusbar.notification.visibilityLocationProvider
import com.android.systemui.statusbar.policy.keyguardStateController
import com.android.systemui.util.kotlin.JavaAdapter
+import org.mockito.kotlin.mock
var Kosmos.visualStabilityCoordinator: VisualStabilityCoordinator by
Kosmos.Fixture {
@@ -54,3 +55,5 @@ var Kosmos.visualStabilityCoordinator: VisualStabilityCoordinator by
visualStabilityCoordinatorLogger,
)
}
+
+var Kosmos.mockVisualStabilityCoordinator: VisualStabilityCoordinator by Kosmos.Fixture { mock() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/collection/provider/HighPriorityProviderKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/collection/provider/HighPriorityProviderKosmos.kt
new file mode 100644
index 000000000000..aa30828d4375
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/collection/provider/HighPriorityProviderKosmos.kt
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2025 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.notification.collection.provider
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.util.mockito.mock
+
+var Kosmos.mockHighPriorityProvider: HighPriorityProvider by Kosmos.Fixture { mock() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/data/model/ActiveNotificationModelBuilder.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/data/model/ActiveNotificationModelBuilder.kt
index 63085e178e7d..db27e7f81e64 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/data/model/ActiveNotificationModelBuilder.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/data/model/ActiveNotificationModelBuilder.kt
@@ -18,8 +18,9 @@ package com.android.systemui.statusbar.notification.data.model
import android.app.PendingIntent
import android.graphics.drawable.Icon
+import com.android.internal.logging.InstanceId
import com.android.systemui.statusbar.StatusBarIconView
-import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel
+import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModels
import com.android.systemui.statusbar.notification.shared.ActiveNotificationModel
import com.android.systemui.statusbar.notification.shared.CallType
import com.android.systemui.statusbar.notification.stack.BUCKET_UNKNOWN
@@ -29,6 +30,8 @@ fun activeNotificationModel(
key: String,
groupKey: String? = null,
whenTime: Long = 0L,
+ isForegroundService: Boolean = false,
+ isOngoingEvent: Boolean = false,
isAmbient: Boolean = false,
isRowDismissed: Boolean = false,
isSilent: Boolean = false,
@@ -40,19 +43,21 @@ fun activeNotificationModel(
statusBarIcon: Icon? = null,
statusBarChipIcon: StatusBarIconView? = null,
uid: Int = 0,
- instanceId: Int? = null,
+ instanceId: InstanceId? = null,
isGroupSummary: Boolean = false,
packageName: String = "pkg",
appName: String = "appName",
contentIntent: PendingIntent? = null,
bucket: Int = BUCKET_UNKNOWN,
callType: CallType = CallType.None,
- promotedContent: PromotedNotificationContentModel? = null,
+ promotedContent: PromotedNotificationContentModels? = null,
) =
ActiveNotificationModel(
key = key,
groupKey = groupKey,
whenTime = whenTime,
+ isForegroundService = isForegroundService,
+ isOngoingEvent = isOngoingEvent,
isAmbient = isAmbient,
isRowDismissed = isRowDismissed,
isSilent = isSilent,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterViewModelKosmos.kt
index 99323dbd7cce..ebe20af51c19 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterViewModelKosmos.kt
@@ -22,6 +22,7 @@ import com.android.systemui.shade.domain.interactor.shadeInteractor
import com.android.systemui.shared.notifications.domain.interactor.notificationSettingsInteractor
import com.android.systemui.statusbar.notification.domain.interactor.activeNotificationsInteractor
import com.android.systemui.statusbar.notification.domain.interactor.seenNotificationsInteractor
+import com.android.systemui.window.domain.interactor.windowRootViewBlurInteractor
val Kosmos.footerViewModel by Fixture {
FooterViewModel(
@@ -29,6 +30,7 @@ val Kosmos.footerViewModel by Fixture {
notificationSettingsInteractor = notificationSettingsInteractor,
seenNotificationsInteractor = seenNotificationsInteractor,
shadeInteractor = shadeInteractor,
+ windowRootViewBlurInteractor = windowRootViewBlurInteractor,
)
}
val Kosmos.footerViewModelFactory: FooterViewModel.Factory by Fixture {
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/promoted/FakePromotedNotificationContentExtractor.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/promoted/FakePromotedNotificationContentExtractor.kt
index 8fdf5dbf2aeb..aaa86aaaedc6 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/promoted/FakePromotedNotificationContentExtractor.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/promoted/FakePromotedNotificationContentExtractor.kt
@@ -17,21 +17,23 @@
package com.android.systemui.statusbar.notification.promoted
import android.app.Notification
+import com.android.systemui.statusbar.NotificationLockscreenUserManager.RedactionType
import com.android.systemui.statusbar.notification.collection.NotificationEntry
-import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel
+import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModels
import com.android.systemui.statusbar.notification.row.shared.ImageModelProvider
import org.junit.Assert
class FakePromotedNotificationContentExtractor : PromotedNotificationContentExtractor {
@JvmField
- val contentForEntry = mutableMapOf<NotificationEntry, PromotedNotificationContentModel?>()
+ val contentForEntry = mutableMapOf<NotificationEntry, PromotedNotificationContentModels?>()
@JvmField val extractCalls = mutableListOf<Pair<NotificationEntry, Notification.Builder>>()
override fun extractContent(
entry: NotificationEntry,
recoveredBuilder: Notification.Builder,
+ @RedactionType redactionType: Int,
imageModelProvider: ImageModelProvider,
- ): PromotedNotificationContentModel? {
+ ): PromotedNotificationContentModels? {
extractCalls.add(entry to recoveredBuilder)
if (contentForEntry.isEmpty()) {
@@ -44,7 +46,7 @@ class FakePromotedNotificationContentExtractor : PromotedNotificationContentExtr
}
}
- fun resetForEntry(entry: NotificationEntry, content: PromotedNotificationContentModel?) {
+ fun resetForEntry(entry: NotificationEntry, content: PromotedNotificationContentModels?) {
contentForEntry.clear()
contentForEntry[entry] = content
extractCalls.clear()
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractorKosmos.kt
index 2b3158da38f9..c4542c4e709b 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractorKosmos.kt
@@ -19,6 +19,7 @@ package com.android.systemui.statusbar.notification.promoted
import android.app.Notification
import android.content.applicationContext
import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.statusbar.NotificationLockscreenUserManager.REDACTION_TYPE_NONE
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.row.RowImageInflater
import com.android.systemui.statusbar.notification.row.shared.skeletonImageTransform
@@ -39,9 +40,10 @@ fun Kosmos.setPromotedContent(entry: NotificationEntry) {
promotedNotificationContentExtractor.extractContent(
entry,
Notification.Builder.recoverBuilder(applicationContext, entry.sbn.notification),
+ REDACTION_TYPE_NONE,
RowImageInflater.newInstance(previousIndex = null, reinflating = false)
.useForContentModel(),
)
- entry.promotedNotificationContentModel =
+ entry.promotedNotificationContentModels =
requireNotNull(extractedContent) { "extractContent returned null" }
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/promoted/domain/interactor/PromotedNotificationsInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/promoted/domain/interactor/PromotedNotificationsInteractorKosmos.kt
index 093ec10e2642..8b2c68aa04cd 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/promoted/domain/interactor/PromotedNotificationsInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/promoted/domain/interactor/PromotedNotificationsInteractorKosmos.kt
@@ -19,13 +19,17 @@ package com.android.systemui.statusbar.notification.promoted.domain.interactor
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testDispatcher
import com.android.systemui.statusbar.chips.call.domain.interactor.callChipInteractor
+import com.android.systemui.statusbar.chips.mediaprojection.domain.interactor.mediaProjectionChipInteractor
import com.android.systemui.statusbar.chips.notification.domain.interactor.statusBarNotificationChipsInteractor
+import com.android.systemui.statusbar.chips.screenrecord.domain.interactor.screenRecordChipInteractor
import com.android.systemui.statusbar.notification.domain.interactor.activeNotificationsInteractor
val Kosmos.promotedNotificationsInteractor by
Kosmos.Fixture {
PromotedNotificationsInteractor(
activeNotificationsInteractor = activeNotificationsInteractor,
+ screenRecordChipInteractor = screenRecordChipInteractor,
+ mediaProjectionChipInteractor = mediaProjectionChipInteractor,
callChipInteractor = callChipInteractor,
notifChipsInteractor = statusBarNotificationChipsInteractor,
backgroundDispatcher = testDispatcher,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/promoted/shared/model/PromotedNotificationContentBuilder.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/promoted/shared/model/PromotedNotificationContentBuilder.kt
new file mode 100644
index 000000000000..6916d560a7ad
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/promoted/shared/model/PromotedNotificationContentBuilder.kt
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2025 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.notification.promoted.shared.model
+
+class PromotedNotificationContentBuilder(val key: String) {
+ private val sharedBuilder = PromotedNotificationContentModel.Builder(key)
+
+ fun applyToShared(
+ block: PromotedNotificationContentModel.Builder.() -> Unit
+ ): PromotedNotificationContentBuilder {
+ sharedBuilder.apply(block)
+ return this
+ }
+
+ fun build(): PromotedNotificationContentModels {
+ val sharedModel = sharedBuilder.build()
+ return PromotedNotificationContentModels(sharedModel, sharedModel)
+ }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/EntryAdapterFactoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/EntryAdapterFactoryKosmos.kt
index 067e420b89c3..4b699702d54f 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/EntryAdapterFactoryKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/EntryAdapterFactoryKosmos.kt
@@ -19,7 +19,9 @@ package com.android.systemui.statusbar.notification.row
import com.android.internal.logging.metricsLogger
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.statusbar.notification.collection.EntryAdapterFactoryImpl
-import com.android.systemui.statusbar.notification.collection.coordinator.visualStabilityCoordinator
+import com.android.systemui.statusbar.notification.collection.coordinator.mockVisualStabilityCoordinator
+import com.android.systemui.statusbar.notification.collection.provider.mockHighPriorityProvider
+import com.android.systemui.statusbar.notification.headsup.mockHeadsUpManager
import com.android.systemui.statusbar.notification.mockNotificationActivityStarter
import com.android.systemui.statusbar.notification.people.peopleNotificationIdentifier
import com.android.systemui.statusbar.notification.row.icon.notificationIconStyleProvider
@@ -31,7 +33,9 @@ val Kosmos.entryAdapterFactory by
metricsLogger,
peopleNotificationIdentifier,
notificationIconStyleProvider,
- visualStabilityCoordinator,
+ mockVisualStabilityCoordinator,
mockNotificationActionClickManager,
+ mockHighPriorityProvider,
+ mockHeadsUpManager,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowBuilder.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowBuilder.kt
index 6a674ca29ca4..b6acf477b660 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowBuilder.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowBuilder.kt
@@ -60,6 +60,7 @@ import com.android.systemui.statusbar.notification.collection.NotificationEntryB
import com.android.systemui.statusbar.notification.collection.coordinator.VisualStabilityCoordinator
import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener
+import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider
import com.android.systemui.statusbar.notification.collection.provider.NotificationDismissibilityProvider
import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManager
import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManagerImpl
@@ -376,6 +377,8 @@ class ExpandableNotificationRowBuilder(
Mockito.mock(NotificationIconStyleProvider::class.java),
Mockito.mock(VisualStabilityCoordinator::class.java),
Mockito.mock(NotificationActionClickManager::class.java),
+ Mockito.mock(HighPriorityProvider::class.java),
+ Mockito.mock(HeadsUpManager::class.java),
)
.create(entry)
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/icon/AppIconProviderKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/icon/AppIconProviderKosmos.kt
index 0fd0f1469818..277fc62242b1 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/icon/AppIconProviderKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/icon/AppIconProviderKosmos.kt
@@ -19,6 +19,9 @@ package com.android.systemui.statusbar.notification.row.icon
import android.content.applicationContext
import com.android.systemui.dump.dumpManager
import com.android.systemui.kosmos.Kosmos
+import org.mockito.kotlin.mock
+
+val Kosmos.mockAppIconProvider by Kosmos.Fixture { mock<AppIconProvider>() }
val Kosmos.appIconProvider by
Kosmos.Fixture { AppIconProviderImpl(applicationContext, dumpManager) }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/icon/NotificationIconStyleProviderKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/icon/NotificationIconStyleProviderKosmos.kt
index b4fb7dd9d760..86ff722f99be 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/icon/NotificationIconStyleProviderKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/icon/NotificationIconStyleProviderKosmos.kt
@@ -19,6 +19,10 @@ package com.android.systemui.statusbar.notification.row.icon
import android.os.userManager
import com.android.systemui.dump.dumpManager
import com.android.systemui.kosmos.Kosmos
+import org.mockito.kotlin.mock
+
+val Kosmos.mockNotificationIconStyleProvider by
+ Kosmos.Fixture { mock<NotificationIconStyleProvider>() }
val Kosmos.notificationIconStyleProvider by
Kosmos.Fixture { NotificationIconStyleProviderImpl(userManager, dumpManager) }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/ongoingcall/shared/model/OngoingCallTestHelper.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/ongoingcall/shared/model/OngoingCallTestHelper.kt
index 3e96fd7c729f..bc0bb753b1d9 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/ongoingcall/shared/model/OngoingCallTestHelper.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/ongoingcall/shared/model/OngoingCallTestHelper.kt
@@ -17,6 +17,7 @@
package com.android.systemui.statusbar.phone.ongoingcall.shared.model
import android.app.PendingIntent
+import com.android.internal.logging.InstanceId
import com.android.systemui.activity.data.repository.activityManagerRepository
import com.android.systemui.activity.data.repository.fake
import com.android.systemui.kosmos.Kosmos
@@ -26,7 +27,7 @@ import com.android.systemui.statusbar.notification.data.model.activeNotification
import com.android.systemui.statusbar.notification.data.repository.activeNotificationListRepository
import com.android.systemui.statusbar.notification.data.repository.addNotif
import com.android.systemui.statusbar.notification.data.repository.removeNotif
-import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel
+import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModels
import com.android.systemui.statusbar.notification.shared.CallType
import com.android.systemui.statusbar.phone.ongoingcall.StatusBarChipsModernization
import com.android.systemui.statusbar.phone.ongoingcall.data.repository.ongoingCallRepository
@@ -39,8 +40,9 @@ fun inCallModel(
intent: PendingIntent? = null,
notificationKey: String = "test",
appName: String = "",
- promotedContent: PromotedNotificationContentModel? = null,
+ promotedContent: PromotedNotificationContentModels? = null,
isAppVisible: Boolean = false,
+ instanceId: InstanceId? = null,
) =
OngoingCallModel.InCall(
startTimeMs,
@@ -50,6 +52,7 @@ fun inCallModel(
appName,
promotedContent,
isAppVisible,
+ instanceId,
)
object OngoingCallTestHelper {
@@ -77,11 +80,12 @@ object OngoingCallTestHelper {
key: String = "notif",
startTimeMs: Long = 1000L,
statusBarChipIconView: StatusBarIconView? = createStatusBarIconViewOrNull(),
- promotedContent: PromotedNotificationContentModel? = null,
+ promotedContent: PromotedNotificationContentModels? = null,
contentIntent: PendingIntent? = null,
uid: Int = DEFAULT_UID,
appName: String = "Fake name",
isAppVisible: Boolean = false,
+ instanceId: InstanceId? = null,
) {
if (StatusBarChipsModernization.isEnabled) {
activityManagerRepository.fake.startingIsAppVisibleValue = isAppVisible
@@ -95,6 +99,7 @@ object OngoingCallTestHelper {
promotedContent = promotedContent,
uid = uid,
appName = appName,
+ instanceId = instanceId,
)
)
} else {
@@ -107,6 +112,7 @@ object OngoingCallTestHelper {
appName = appName,
promotedContent = promotedContent,
isAppVisible = isAppVisible,
+ instanceId = instanceId,
)
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/ui/TintedIconManagerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/ui/TintedIconManagerKosmos.kt
index 8e13b624f5fa..fd63ce28a29d 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/ui/TintedIconManagerKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/ui/TintedIconManagerKosmos.kt
@@ -16,16 +16,24 @@
package com.android.systemui.statusbar.phone.ui
+import com.android.systemui.kairos.ExperimentalKairosApi
+import com.android.systemui.kairos.kairos
import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.applicationCoroutineScope
import com.android.systemui.statusbar.connectivity.ui.mobileContextProvider
import com.android.systemui.statusbar.pipeline.mobile.ui.mobileUiAdapter
+import com.android.systemui.statusbar.pipeline.mobile.ui.mobileUiAdapterKairos
import com.android.systemui.statusbar.pipeline.wifi.ui.wifiUiAdapter
+@OptIn(ExperimentalKairosApi::class)
val Kosmos.tintedIconManagerFactory by
-Kosmos.Fixture {
- TintedIconManager.Factory(
- wifiUiAdapter,
- mobileUiAdapter,
- mobileContextProvider,
- )
-} \ No newline at end of file
+ Kosmos.Fixture {
+ TintedIconManager.Factory(
+ wifiUiAdapter,
+ mobileUiAdapter,
+ mobileContextProvider,
+ { mobileUiAdapterKairos },
+ kairos,
+ applicationCoroutineScope,
+ )
+ }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/ui/MobileUiAdapterKairosKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/ui/MobileUiAdapterKairosKosmos.kt
new file mode 100644
index 000000000000..3a3f18ad2ede
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/ui/MobileUiAdapterKairosKosmos.kt
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2025 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.pipeline.mobile.ui
+
+import com.android.systemui.dump.dumpManager
+import com.android.systemui.kairos.ActivatedKairosFixture
+import com.android.systemui.kairos.ExperimentalKairosApi
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.statusbar.phone.ui.statusBarIconController
+import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.mobileIconsViewModelKairos
+
+@ExperimentalKairosApi
+val Kosmos.mobileUiAdapterKairos by ActivatedKairosFixture {
+ MobileUiAdapterKairos(
+ statusBarIconController,
+ mobileIconsViewModelKairos,
+ mobileViewLogger,
+ dumpManager,
+ )
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModelKairosKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModelKairosKosmos.kt
new file mode 100644
index 000000000000..83b8283b1892
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModelKairosKosmos.kt
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2025 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.pipeline.mobile.ui.viewmodel
+
+import com.android.systemui.flags.featureFlagsClassic
+import com.android.systemui.kairos.ActivatedKairosFixture
+import com.android.systemui.kairos.ExperimentalKairosApi
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.statusbar.pipeline.airplane.domain.interactor.airplaneModeInteractor
+import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.mobileIconsInteractorKairos
+import com.android.systemui.statusbar.pipeline.mobile.ui.mobileViewLogger
+import com.android.systemui.util.mockito.mock
+
+@ExperimentalKairosApi
+val Kosmos.mobileIconsViewModelKairos by ActivatedKairosFixture {
+ MobileIconsViewModelKairos(
+ logger = mobileViewLogger,
+ verboseLogger = mock(),
+ interactor = mobileIconsInteractorKairos,
+ airplaneModeInteractor = airplaneModeInteractor,
+ constants = mock(),
+ flags = featureFlagsClassic,
+ )
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/StackedMobileIconViewModelKairosKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/StackedMobileIconViewModelKairosKosmos.kt
index 3ee33802e9d5..ad42a89dc237 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/StackedMobileIconViewModelKairosKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/StackedMobileIconViewModelKairosKosmos.kt
@@ -16,7 +16,11 @@
package com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel
+import com.android.systemui.kairos.ActivatedKairosFixture
+import com.android.systemui.kairos.ExperimentalKairosApi
import com.android.systemui.kosmos.Kosmos
-val Kosmos.stackedMobileIconViewModelKairos by
- Kosmos.Fixture { StackedMobileIconViewModelKairos(mobileIconsViewModel) }
+@ExperimentalKairosApi
+val Kosmos.stackedMobileIconViewModelKairos by ActivatedKairosFixture {
+ StackedMobileIconViewModelKairos(mobileIconsViewModelKairos)
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/StackedMobileIconViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/StackedMobileIconViewModelKosmos.kt
index 880ba5eee5d2..0a8e0a7d48b6 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/StackedMobileIconViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/StackedMobileIconViewModelKosmos.kt
@@ -18,5 +18,8 @@ package com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel
import com.android.systemui.kosmos.Kosmos
-val Kosmos.stackedMobileIconViewModel: StackedMobileIconViewModel by
- Kosmos.Fixture { StackedMobileIconViewModel(mobileIconsViewModel) }
+var Kosmos.stackedMobileIconViewModel: StackedMobileIconViewModel by
+ Kosmos.Fixture { stackedMobileIconViewModelImpl }
+
+val Kosmos.stackedMobileIconViewModelImpl by
+ Kosmos.Fixture { StackedMobileIconViewModelImpl(mobileIconsViewModel) }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModelKosmos.kt
index 5c4deaadffd5..0c088cc00dec 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModelKosmos.kt
@@ -29,6 +29,7 @@ import com.android.systemui.shade.domain.interactor.shadeDisplaysInteractor
import com.android.systemui.shade.domain.interactor.shadeInteractor
import com.android.systemui.statusbar.chips.sharetoapp.ui.viewmodel.shareToAppChipViewModel
import com.android.systemui.statusbar.chips.ui.viewmodel.ongoingActivityChipsViewModel
+import com.android.systemui.statusbar.chips.uievents.statusBarChipsUiEventLogger
import com.android.systemui.statusbar.events.domain.interactor.systemStatusEventAnimationInteractor
import com.android.systemui.statusbar.featurepods.popups.ui.viewmodel.statusBarPopupChipsViewModelFactory
import com.android.systemui.statusbar.layout.ui.viewmodel.multiDisplayStatusBarContentInsetsViewModelStore
@@ -69,6 +70,7 @@ var Kosmos.homeStatusBarViewModelFactory: (Int) -> HomeStatusBarViewModel by
backgroundScope,
testDispatcher,
{ shadeDisplaysInteractor },
+ uiEventLogger = statusBarChipsUiEventLogger,
)
}
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/FakeStatusBarWindowControllerFactory.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/FakeStatusBarWindowControllerFactory.kt
index 3a19547f0713..f20fb3bf1779 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/FakeStatusBarWindowControllerFactory.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/FakeStatusBarWindowControllerFactory.kt
@@ -17,14 +17,14 @@
package com.android.systemui.statusbar.window
import android.content.Context
-import com.android.app.viewcapture.ViewCaptureAwareWindowManager
+import android.view.WindowManager
import com.android.systemui.statusbar.data.repository.StatusBarConfigurationController
import com.android.systemui.statusbar.layout.StatusBarContentInsetsProvider
class FakeStatusBarWindowControllerFactory : StatusBarWindowController.Factory {
override fun create(
context: Context,
- viewCaptureAwareWindowManager: ViewCaptureAwareWindowManager,
+ windowManager: WindowManager,
statusBarConfigurationController: StatusBarConfigurationController,
contentInsetsProvider: StatusBarContentInsetsProvider,
) = FakeStatusBarWindowController()
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/StatusBarWindowControllerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/StatusBarWindowControllerKosmos.kt
index f595aef41e2d..b0214769362f 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/StatusBarWindowControllerKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/StatusBarWindowControllerKosmos.kt
@@ -17,8 +17,8 @@
package com.android.systemui.statusbar.window
import android.content.testableContext
+import android.view.fakeWindowManager
import android.view.windowManagerService
-import com.android.app.viewcapture.realCaptureAwareWindowManager
import com.android.systemui.concurrency.fakeExecutor
import com.android.systemui.fragments.fragmentService
import com.android.systemui.kosmos.Kosmos
@@ -33,7 +33,7 @@ val Kosmos.statusBarWindowControllerImpl by
StatusBarWindowControllerImpl(
testableContext,
statusBarWindowViewInflater,
- realCaptureAwareWindowManager,
+ fakeWindowManager,
statusBarConfigurationController,
windowManagerService,
statusBarContentInsetsProvider,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/StatusBarWindowControllerStoreKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/StatusBarWindowControllerStoreKosmos.kt
index 4941ceb7991d..0f9310376b2a 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/StatusBarWindowControllerStoreKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/StatusBarWindowControllerStoreKosmos.kt
@@ -16,9 +16,6 @@
package com.android.systemui.statusbar.window
-import android.view.WindowManager
-import com.android.app.viewcapture.ViewCaptureAwareWindowManager
-import com.android.app.viewcapture.realCaptureAwareWindowManager
import com.android.systemui.display.data.repository.displayRepository
import com.android.systemui.display.data.repository.displayWindowPropertiesRepository
import com.android.systemui.kosmos.Kosmos
@@ -33,14 +30,6 @@ val Kosmos.multiDisplayStatusBarWindowControllerStore by
backgroundApplicationScope = applicationCoroutineScope,
controllerFactory = { _, _, _, _ -> mock() },
displayWindowPropertiesRepository = displayWindowPropertiesRepository,
- viewCaptureAwareWindowManagerFactory =
- object : ViewCaptureAwareWindowManager.Factory {
- override fun create(
- windowManager: WindowManager
- ): ViewCaptureAwareWindowManager {
- return realCaptureAwareWindowManager
- }
- },
statusBarConfigurationControllerStore = statusBarConfigurationControllerStore,
statusBarContentInsetsProviderStore = statusBarContentInsetsProviderStore,
displayRepository = displayRepository,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/user/domain/interactor/UserSwitcherInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/user/domain/interactor/UserSwitcherInteractorKosmos.kt
index 1504df4ef6d0..6767300a22bc 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/user/domain/interactor/UserSwitcherInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/user/domain/interactor/UserSwitcherInteractorKosmos.kt
@@ -55,5 +55,6 @@ val Kosmos.userSwitcherInteractor by
uiEventLogger = uiEventLogger,
userRestrictionChecker = userRestrictionChecker,
processWrapper = processWrapper,
+ userLogoutInteractor = userLogoutInteractor,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeSettings.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeSettings.kt
index f31697e82a45..a6332bf9e3d7 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeSettings.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeSettings.kt
@@ -289,6 +289,37 @@ class FakeSettings : SecureSettings, SystemSettings, UserSettingsProxy {
return putString(name, value)
}
+ override fun getInt(name: String): Int {
+ return getIntForUser(name, userId)
+ }
+
+ override fun getInt(name: String, default: Int): Int {
+ return getIntForUser(name, default, userId)
+ }
+
+ override fun getIntForUser(name: String, userHandle: Int): Int {
+ return getIntForUser(name, 0, userHandle)
+ }
+
+ override fun getIntForUser(name: String, default: Int, userHandle: Int): Int {
+ return values[SettingsKey(userHandle, getUriFor(name).toString())]?.toInt() ?: default
+ }
+
+ override fun putIntForUser(name: String, value: Int, userHandle: Int): Boolean {
+ val key = SettingsKey(userHandle, getUriFor(name).toString())
+ values[key] = value.toString()
+ val uri = getUriFor(name)
+ contentObservers[key]?.onEach { it.dispatchChange(false, listOf(uri), 0, userHandle) }
+ contentObserversAllUsers[uri.toString()]?.onEach {
+ it.dispatchChange(false, listOf(uri), 0, userHandle)
+ }
+ return true
+ }
+
+ override fun putInt(name: String, value: Int): Boolean {
+ return putIntForUser(name, value, userId)
+ }
+
/** Runs current jobs on dispatcher after calling the method. */
private fun <T> advanceDispatcher(f: () -> T): T {
val result = f()
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/utils/windowmanager/FakeWindowManagerProvider.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/utils/windowmanager/FakeWindowManagerProvider.kt
new file mode 100644
index 000000000000..5c8eae3183c7
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/utils/windowmanager/FakeWindowManagerProvider.kt
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2025 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.utils.windowmanager
+
+import android.content.Context
+import android.view.WindowManager
+
+/** Fake implementation of [WindowManagerProvider], to be used in tests only. */
+class FakeWindowManagerProvider(private val windowManager: WindowManager) : WindowManagerProvider {
+
+ override fun getWindowManager(context: Context): WindowManager {
+ return windowManager
+ }
+} \ No newline at end of file
diff --git a/packages/SystemUI/tests/utils/src/com/android/app/viewcapture/ViewCaptureAwareWindowManagerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogCsdWarningInteractorKosmos.kt
index 021c7bbb44cd..fc7fc0f4e5b4 100644
--- a/packages/SystemUI/tests/utils/src/com/android/app/viewcapture/ViewCaptureAwareWindowManagerKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogCsdWarningInteractorKosmos.kt
@@ -14,23 +14,13 @@
* limitations under the License.
*/
-package com.android.app.viewcapture
+package com.android.systemui.volume.dialog.domain.interactor
-import android.view.fakeWindowManager
import com.android.systemui.kosmos.Kosmos
-import org.mockito.kotlin.mock
+import com.android.systemui.volume.dialog.shared.model.CsdWarningConfigModel
-val Kosmos.mockViewCaptureAwareWindowManager by
- Kosmos.Fixture { mock<ViewCaptureAwareWindowManager>() }
+val Kosmos.volumeDialogCsdWarningInteractor: VolumeDialogCsdWarningInteractor by
+ Kosmos.Fixture { VolumeDialogCsdWarningInteractor(volumeDialogStateInteractor) }
-val Kosmos.realCaptureAwareWindowManager by
- Kosmos.Fixture {
- ViewCaptureAwareWindowManager(
- fakeWindowManager,
- lazyViewCapture = lazy { mock<ViewCapture>() },
- isViewCaptureEnabled = false,
- )
- }
-
-var Kosmos.viewCaptureAwareWindowManager: ViewCaptureAwareWindowManager by
- Kosmos.Fixture { mockViewCaptureAwareWindowManager }
+val Kosmos.csdWarningConfigModel: CsdWarningConfigModel by
+ Kosmos.Fixture { CsdWarningConfigModel(emptyList()) }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderIconProviderKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderIconProviderKosmos.kt
index 09f9f1c6362e..44d7a22b6258 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderIconProviderKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderIconProviderKosmos.kt
@@ -18,6 +18,7 @@ package com.android.systemui.volume.dialog.sliders.ui.viewmodel
import android.content.applicationContext
import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.backgroundCoroutineContext
import com.android.systemui.statusbar.policy.domain.interactor.zenModeInteractor
import com.android.systemui.volume.domain.interactor.audioVolumeInteractor
@@ -25,6 +26,7 @@ val Kosmos.volumeDialogSliderIconProvider by
Kosmos.Fixture {
VolumeDialogSliderIconProvider(
context = applicationContext,
+ uiBackgroundContext = backgroundCoroutineContext,
audioVolumeInteractor = audioVolumeInteractor,
zenModeInteractor = zenModeInteractor,
)
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/ui/viewmodel/VolumeDialogPluginViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/ui/viewmodel/VolumeDialogPluginViewModelKosmos.kt
new file mode 100644
index 000000000000..5a58599eaade
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/ui/viewmodel/VolumeDialogPluginViewModelKosmos.kt
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2025 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.volume.dialog.ui.viewmodel
+
+import com.android.internal.logging.uiEventLogger
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.backgroundScope
+import com.android.systemui.volume.dialog.domain.interactor.csdWarningConfigModel
+import com.android.systemui.volume.dialog.domain.interactor.volumeDialogCsdWarningInteractor
+import com.android.systemui.volume.dialog.domain.interactor.volumeDialogSafetyWarningInteractor
+import com.android.systemui.volume.dialog.domain.interactor.volumeDialogVisibilityInteractor
+import com.android.systemui.volume.dialog.shared.volumeDialogLogger
+import com.android.systemui.volume.dialog.volumeDialog
+
+val Kosmos.volumeDialogPluginViewModel: VolumeDialogPluginViewModel by
+ Kosmos.Fixture {
+ VolumeDialogPluginViewModel(
+ backgroundScope,
+ volumeDialogVisibilityInteractor,
+ volumeDialogSafetyWarningInteractor,
+ volumeDialogCsdWarningInteractor,
+ { volumeDialog },
+ volumeDialogLogger,
+ csdWarningConfigModel,
+ uiEventLogger,
+ )
+ }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioSharingStreamSliderViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioSharingStreamSliderViewModelKosmos.kt
index 8c8d0240f572..6e43d79295ff 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioSharingStreamSliderViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioSharingStreamSliderViewModelKosmos.kt
@@ -16,9 +16,11 @@
package com.android.systemui.volume.panel.component.volume.slider.ui.viewmodel
+import android.content.applicationContext
import com.android.internal.logging.uiEventLogger
import com.android.systemui.haptics.slider.sliderHapticsViewModelFactory
import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.backgroundCoroutineContext
import com.android.systemui.volume.domain.interactor.audioSharingInteractor
import com.android.systemui.volume.shared.volumePanelLogger
import kotlinx.coroutines.CoroutineScope
@@ -28,7 +30,9 @@ val Kosmos.audioSharingStreamSliderViewModelFactory by
object : AudioSharingStreamSliderViewModel.Factory {
override fun create(coroutineScope: CoroutineScope): AudioSharingStreamSliderViewModel {
return AudioSharingStreamSliderViewModel(
+ applicationContext,
coroutineScope,
+ backgroundCoroutineContext,
audioSharingInteractor,
uiEventLogger,
sliderHapticsViewModelFactory,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioStreamSliderViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioStreamSliderViewModelKosmos.kt
index 88c716e0ab10..47016e535ea4 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioStreamSliderViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioStreamSliderViewModelKosmos.kt
@@ -20,6 +20,7 @@ import android.content.applicationContext
import com.android.internal.logging.uiEventLogger
import com.android.systemui.haptics.slider.sliderHapticsViewModelFactory
import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.backgroundCoroutineContext
import com.android.systemui.statusbar.policy.domain.interactor.zenModeInteractor
import com.android.systemui.volume.domain.interactor.audioSharingInteractor
import com.android.systemui.volume.domain.interactor.audioVolumeInteractor
@@ -37,6 +38,7 @@ val Kosmos.audioStreamSliderViewModelFactory by
return AudioStreamSliderViewModel(
audioStream,
coroutineScope,
+ backgroundCoroutineContext,
applicationContext,
audioVolumeInteractor,
zenModeInteractor,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/CastVolumeSliderViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/CastVolumeSliderViewModelKosmos.kt
index 6875619d45fc..ed51e054e50c 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/CastVolumeSliderViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/CastVolumeSliderViewModelKosmos.kt
@@ -19,6 +19,7 @@ package com.android.systemui.volume.panel.component.volume.slider.ui.viewmodel
import android.content.applicationContext
import com.android.systemui.haptics.slider.sliderHapticsViewModelFactory
import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.backgroundCoroutineContext
import com.android.systemui.volume.mediaDeviceSessionInteractor
import com.android.systemui.volume.panel.component.mediaoutput.shared.model.MediaDeviceSession
import com.android.systemui.volume.shared.volumePanelLogger
@@ -34,6 +35,7 @@ val Kosmos.castVolumeSliderViewModelFactory by
return CastVolumeSliderViewModel(
session,
coroutineScope,
+ backgroundCoroutineContext,
applicationContext,
mediaDeviceSessionInteractor,
sliderHapticsViewModelFactory,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/window/data/repository/WindowRootViewBlurRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/window/data/repository/WindowRootViewBlurRepositoryKosmos.kt
index b619e2d70724..2b518182922d 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/window/data/repository/WindowRootViewBlurRepositoryKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/window/data/repository/WindowRootViewBlurRepositoryKosmos.kt
@@ -26,7 +26,7 @@ val Kosmos.windowRootViewBlurRepository: WindowRootViewBlurRepository by
Kosmos.Fixture { fakeWindowRootViewBlurRepository }
class FakeWindowRootViewBlurRepository : WindowRootViewBlurRepository {
- override val blurRadius: MutableStateFlow<Int> = MutableStateFlow(0)
+ override val blurRequestedByShade: MutableStateFlow<Int> = MutableStateFlow(0)
override val isBlurOpaque: MutableStateFlow<Boolean> = MutableStateFlow(false)
override val isBlurSupported: MutableStateFlow<Boolean> = MutableStateFlow(false)
override var blurAppliedListener: BlurAppliedListener? = null
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/window/ui/viewmodel/WindowRootViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/window/ui/viewmodel/WindowRootViewModelKosmos.kt
index 5a02bfbacd35..e56f32f3534f 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/window/ui/viewmodel/WindowRootViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/window/ui/viewmodel/WindowRootViewModelKosmos.kt
@@ -16,9 +16,11 @@
package com.android.systemui.window.ui.viewmodel
+import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
import com.android.systemui.keyguard.ui.transitions.FakeBouncerTransition
import com.android.systemui.keyguard.ui.transitions.FakeGlanceableHubTransition
import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.shade.domain.interactor.shadeInteractor
import com.android.systemui.window.domain.interactor.windowRootViewBlurInteractor
import org.mockito.internal.util.collections.Sets
@@ -38,5 +40,7 @@ val Kosmos.windowRootViewModel by
fakeBouncerTransitions,
fakeGlanceableHubTransitions,
windowRootViewBlurInteractor,
+ keyguardInteractor,
+ shadeInteractor,
)
}
diff --git a/packages/SystemUI/utils/Android.bp b/packages/SystemUI/utils/Android.bp
index 1efb11b436ff..8b63c07b270f 100644
--- a/packages/SystemUI/utils/Android.bp
+++ b/packages/SystemUI/utils/Android.bp
@@ -26,6 +26,8 @@ java_library {
"src/**/*.kt",
],
static_libs: [
+ "//frameworks/libs/systemui:view_capture",
+ "com_android_systemui_flags_lib",
"kotlin-stdlib",
"kotlinx_coroutines",
],
diff --git a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/Graph.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/Graph.kt
index b956e44e0618..90a92712bf81 100644
--- a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/Graph.kt
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/Graph.kt
@@ -281,7 +281,6 @@ internal class DepthTracker {
},
)
}
- downstreamSet.clear()
}
}
reset()
diff --git a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/MuxDeferred.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/MuxDeferred.kt
index c11eb122597d..81f37022a868 100644
--- a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/MuxDeferred.kt
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/MuxDeferred.kt
@@ -145,7 +145,14 @@ internal class MuxDeferredNode<W, K, V>(
val conn = branchNode.upstream
severed.add(conn)
conn.removeDownstream(downstream = branchNode.schedulable)
- depthTracker.removeDirectUpstream(conn.depthTracker.snapshotDirectDepth)
+ if (conn.depthTracker.snapshotIsDirect) {
+ depthTracker.removeDirectUpstream(conn.depthTracker.snapshotDirectDepth)
+ } else {
+ depthTracker.removeIndirectUpstream(conn.depthTracker.snapshotIndirectDepth)
+ depthTracker.updateIndirectRoots(
+ removals = conn.depthTracker.snapshotIndirectRoots
+ )
+ }
}
}
@@ -156,7 +163,14 @@ internal class MuxDeferredNode<W, K, V>(
val conn = branchNode.upstream
severed.add(conn)
conn.removeDownstream(downstream = branchNode.schedulable)
- depthTracker.removeDirectUpstream(conn.depthTracker.snapshotDirectDepth)
+ if (conn.depthTracker.snapshotIsDirect) {
+ depthTracker.removeDirectUpstream(conn.depthTracker.snapshotDirectDepth)
+ } else {
+ depthTracker.removeIndirectUpstream(conn.depthTracker.snapshotIndirectDepth)
+ depthTracker.updateIndirectRoots(
+ removals = conn.depthTracker.snapshotIndirectRoots
+ )
+ }
}
// add new
@@ -343,13 +357,8 @@ private class MuxDeferredActivator<W, K, V>(
val (patchesConn, needsEval) =
getPatches(evalScope).activate(evalScope, downstream = muxNode.schedulable)
?: run {
- // Turns out we can't connect to patches, so update our depth and
- // propagate
- if (muxNode.depthTracker.setIsIndirectRoot(false)) {
- // TODO: schedules might not be necessary now that we're not
- // parallel?
- muxNode.depthTracker.schedule(evalScope.scheduler, muxNode)
- }
+ // Turns out we can't connect to patches, so update our depth
+ muxNode.depthTracker.setIsIndirectRoot(false)
return
}
muxNode.patches = patchesConn
diff --git a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/MuxPrompt.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/MuxPrompt.kt
index cb2c6e51df66..faef6a21d4f3 100644
--- a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/MuxPrompt.kt
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/MuxPrompt.kt
@@ -109,7 +109,14 @@ internal class MuxPromptNode<W, K, V>(
val conn: NodeConnection<V> = branchNode.upstream
severed.add(conn)
conn.removeDownstream(downstream = branchNode.schedulable)
- depthTracker.removeDirectUpstream(conn.depthTracker.snapshotDirectDepth)
+ if (conn.depthTracker.snapshotIsDirect) {
+ depthTracker.removeDirectUpstream(conn.depthTracker.snapshotDirectDepth)
+ } else {
+ depthTracker.removeIndirectUpstream(conn.depthTracker.snapshotIndirectDepth)
+ depthTracker.updateIndirectRoots(
+ removals = conn.depthTracker.snapshotIndirectRoots
+ )
+ }
}
}
@@ -123,7 +130,14 @@ internal class MuxPromptNode<W, K, V>(
val conn: NodeConnection<V> = oldBranch.upstream
severed.add(conn)
conn.removeDownstream(downstream = oldBranch.schedulable)
- depthTracker.removeDirectUpstream(conn.depthTracker.snapshotDirectDepth)
+ if (conn.depthTracker.snapshotIsDirect) {
+ depthTracker.removeDirectUpstream(conn.depthTracker.snapshotDirectDepth)
+ } else {
+ depthTracker.removeIndirectUpstream(conn.depthTracker.snapshotIndirectDepth)
+ depthTracker.updateIndirectRoots(
+ removals = conn.depthTracker.snapshotIndirectRoots
+ )
+ }
}
// add new
diff --git a/packages/SystemUI/utils/src/com/android/systemui/utils/windowmanager/WindowManagerProvider.kt b/packages/SystemUI/utils/src/com/android/systemui/utils/windowmanager/WindowManagerProvider.kt
new file mode 100644
index 000000000000..4e6eacbc8808
--- /dev/null
+++ b/packages/SystemUI/utils/src/com/android/systemui/utils/windowmanager/WindowManagerProvider.kt
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2025 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.utils.windowmanager
+
+import android.content.Context
+import android.view.WindowManager
+
+/**
+ * Provider for [WindowManager] in SystemUI.
+ *
+ * Use this class over [WindowManagerUtils] in cases where
+ * a [WindowManager] is needed for a context created inside the class. [WindowManagerUtils] should
+ * only be used in a class where the [WindowManager] is needed for a custom context inside the
+ * class, and the class is not part of the dagger graph. Example usage:
+ * ```kotlin
+ * class Sample {
+ * private final WindowManager mWindowManager;
+ *
+ * @Inject
+ * public Sample(WindowManagerProvider windowManagerProvider) {
+ * Context context = getCustomContext();
+ * mWindowManager = windowManagerProvider.getWindowManager(context);
+ * }
+ * // use mWindowManager
+ * }
+ *
+ * class SampleTest {
+ *
+ * @Mock
+ * WindowManager mWindowManager;
+ *
+ * FakeWindowManagerProvider fakeProvider = new FakeWindowManagerProvider(mWindowManager);
+ *
+ * // define the behaviour of mWindowManager to get required WindowManager instance in tests.
+ * }
+ * ```
+ */
+interface WindowManagerProvider {
+
+ /** Method to return the required [WindowManager]. */
+ fun getWindowManager(context: Context): WindowManager
+}
diff --git a/packages/SystemUI/utils/src/com/android/systemui/utils/windowmanager/WindowManagerProviderImpl.kt b/packages/SystemUI/utils/src/com/android/systemui/utils/windowmanager/WindowManagerProviderImpl.kt
new file mode 100644
index 000000000000..5e965ed47403
--- /dev/null
+++ b/packages/SystemUI/utils/src/com/android/systemui/utils/windowmanager/WindowManagerProviderImpl.kt
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2025 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.utils.windowmanager
+
+import android.content.Context
+import android.view.WindowManager
+
+/** Implementation of [WindowManagerProvider]. */
+class WindowManagerProviderImpl : WindowManagerProvider {
+
+ override fun getWindowManager(context: Context): WindowManager {
+ return WindowManagerUtils.getWindowManager(context)
+ }
+}
diff --git a/packages/SystemUI/utils/src/com/android/systemui/utils/windowmanager/WindowManagerUtils.kt b/packages/SystemUI/utils/src/com/android/systemui/utils/windowmanager/WindowManagerUtils.kt
new file mode 100644
index 000000000000..643e93422294
--- /dev/null
+++ b/packages/SystemUI/utils/src/com/android/systemui/utils/windowmanager/WindowManagerUtils.kt
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2025 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.utils.windowmanager
+
+import android.content.Context
+import android.view.WindowManager
+import com.android.app.viewcapture.ViewCaptureAwareWindowManagerFactory
+import com.android.systemui.Flags.enableViewCaptureTracing
+
+/**
+ * Provides [WindowManager] in SystemUI. Use [WindowManagerProvider] unless [WindowManager] instance
+ * needs to be created in a class that is not part of the dagger dependency graph.
+ */
+object WindowManagerUtils {
+
+ /** Method to return the required [WindowManager]. */
+ @JvmStatic
+ fun getWindowManager(context: Context): WindowManager {
+ return if (!enableViewCaptureTracing()) {
+ context.getSystemService(WindowManager::class.java)
+ } else {
+ /**
+ * We use this token to supply windowContextToken to [WindowManager] for
+ * [WindowContext].
+ */
+ val windowContextToken = context.windowContextToken
+
+ ViewCaptureAwareWindowManagerFactory.getInstance(
+ context,
+ parent = null,
+ windowContextToken,
+ )
+ }
+ }
+}
diff --git a/packages/WallpaperBackup/Android.bp b/packages/WallpaperBackup/Android.bp
index b8e0d427f3d8..b0c0c3a5d455 100644
--- a/packages/WallpaperBackup/Android.bp
+++ b/packages/WallpaperBackup/Android.bp
@@ -48,7 +48,9 @@ android_test {
static_libs: [
"androidx.test.core",
"androidx.test.rules",
+ "flag-junit",
"mockito-target-minus-junit4",
+ "platform-test-annotations",
"truth",
],
resource_dirs: ["test/res"],
diff --git a/packages/WallpaperBackup/src/com/android/wallpaperbackup/WallpaperBackupAgent.java b/packages/WallpaperBackup/src/com/android/wallpaperbackup/WallpaperBackupAgent.java
index 44ea9a2848ac..a80a64d6daba 100644
--- a/packages/WallpaperBackup/src/com/android/wallpaperbackup/WallpaperBackupAgent.java
+++ b/packages/WallpaperBackup/src/com/android/wallpaperbackup/WallpaperBackupAgent.java
@@ -16,6 +16,7 @@
package com.android.wallpaperbackup;
+import static android.app.Flags.liveWallpaperContentHandling;
import static android.app.WallpaperManager.FLAG_LOCK;
import static android.app.WallpaperManager.FLAG_SYSTEM;
import static android.app.WallpaperManager.ORIENTATION_UNKNOWN;
@@ -27,6 +28,7 @@ import static com.android.wallpaperbackup.WallpaperEventLogger.ERROR_NO_WALLPAPE
import static com.android.wallpaperbackup.WallpaperEventLogger.ERROR_QUOTA_EXCEEDED;
import static com.android.window.flags.Flags.multiCrop;
+import android.annotation.Nullable;
import android.app.AppGlobals;
import android.app.WallpaperManager;
import android.app.backup.BackupAgent;
@@ -35,6 +37,7 @@ import android.app.backup.BackupDataOutput;
import android.app.backup.BackupManager;
import android.app.backup.BackupRestoreEventLogger.BackupRestoreError;
import android.app.backup.FullBackupDataOutput;
+import android.app.wallpaper.WallpaperDescription;
import android.content.ComponentName;
import android.content.Context;
import android.content.SharedPreferences;
@@ -493,11 +496,13 @@ public class WallpaperBackupAgent extends BackupAgent {
// First parse the live component name so that we know for logging if we care about
// logging errors with the image restore.
- ComponentName wpService = parseWallpaperComponent(infoStage, "wp");
- mSystemHasLiveComponent = wpService != null;
+ Pair<ComponentName, WallpaperDescription> wpService = parseWallpaperComponent(infoStage,
+ "wp");
+ mSystemHasLiveComponent = wpService.first != null;
- ComponentName kwpService = parseWallpaperComponent(infoStage, "kwp");
- mLockHasLiveComponent = kwpService != null;
+ Pair<ComponentName, WallpaperDescription> kwpService = parseWallpaperComponent(
+ infoStage, "kwp");
+ mLockHasLiveComponent = kwpService.first != null;
boolean separateLockWallpaper = mLockHasLiveComponent || lockImageStage.exists();
// if there's no separate lock wallpaper, apply the system wallpaper to both screens.
@@ -586,25 +591,39 @@ public class WallpaperBackupAgent extends BackupAgent {
}
@VisibleForTesting
- void updateWallpaperComponent(ComponentName wpService, int which)
+ void updateWallpaperComponent(Pair<ComponentName, WallpaperDescription> wpService, int which)
throws IOException {
- if (servicePackageExists(wpService)) {
- Slog.i(TAG, "Using wallpaper service " + wpService);
- mWallpaperManager.setWallpaperComponentWithFlags(wpService, which);
- if ((which & FLAG_LOCK) != 0) {
- mEventLogger.onLockLiveWallpaperRestored(wpService);
- }
- if ((which & FLAG_SYSTEM) != 0) {
- mEventLogger.onSystemLiveWallpaperRestored(wpService);
+ WallpaperDescription description = wpService.second;
+ boolean hasDescription = (liveWallpaperContentHandling() && description != null);
+ ComponentName component = hasDescription ? description.getComponent() : wpService.first;
+ if (servicePackageExists(component)) {
+ if (hasDescription) {
+ Slog.i(TAG, "Using wallpaper description " + description);
+ mWallpaperManager.setWallpaperComponentWithDescription(description, which);
+ if ((which & FLAG_LOCK) != 0) {
+ mEventLogger.onLockLiveWallpaperRestoredWithDescription(description);
+ }
+ if ((which & FLAG_SYSTEM) != 0) {
+ mEventLogger.onSystemLiveWallpaperRestoredWithDescription(description);
+ }
+ } else {
+ Slog.i(TAG, "Using wallpaper service " + component);
+ mWallpaperManager.setWallpaperComponentWithFlags(component, which);
+ if ((which & FLAG_LOCK) != 0) {
+ mEventLogger.onLockLiveWallpaperRestored(component);
+ }
+ if ((which & FLAG_SYSTEM) != 0) {
+ mEventLogger.onSystemLiveWallpaperRestored(component);
+ }
}
} else {
// If we've restored a live wallpaper, but the component doesn't exist,
// we should log it as an error so we can easily identify the problem
// in reports from users
- if (wpService != null) {
+ if (component != null) {
// TODO(b/268471749): Handle delayed case
- applyComponentAtInstall(wpService, which);
- Slog.w(TAG, "Wallpaper service " + wpService + " isn't available. "
+ applyComponentAtInstall(component, description, which);
+ Slog.w(TAG, "Wallpaper service " + component + " isn't available. "
+ " Will try to apply later");
}
}
@@ -697,7 +716,6 @@ public class WallpaperBackupAgent extends BackupAgent {
* (thereby preserving the center point). Then finally, adding any leftover image real-estate
* (i.e. space left over on the horizontal axis) to add parallax effect. Parallax is only added
* if was present in the old device's settings.
- *
*/
private Rect findNewCropfromOldCrop(Rect oldCrop, Point oldDisplaySize, boolean oldRtl,
Point newDisplaySize, Point bitmapSize, boolean newRtl) {
@@ -976,10 +994,12 @@ public class WallpaperBackupAgent extends BackupAgent {
return cropHints;
}
- private ComponentName parseWallpaperComponent(File wallpaperInfo, String sectionTag) {
+ private Pair<ComponentName, WallpaperDescription> parseWallpaperComponent(File wallpaperInfo,
+ String sectionTag) {
ComponentName name = null;
+ WallpaperDescription description = null;
try (FileInputStream stream = new FileInputStream(wallpaperInfo)) {
- final XmlPullParser parser = Xml.resolvePullParser(stream);
+ final TypedXmlPullParser parser = Xml.resolvePullParser(stream);
int type;
do {
@@ -991,6 +1011,7 @@ public class WallpaperBackupAgent extends BackupAgent {
name = (parsedName != null)
? ComponentName.unflattenFromString(parsedName)
: null;
+ description = parseWallpaperDescription(parser, name);
break;
}
}
@@ -998,9 +1019,30 @@ public class WallpaperBackupAgent extends BackupAgent {
} catch (Exception e) {
// Whoops; can't process the info file at all. Report failure.
Slog.w(TAG, "Failed to parse restored component: " + e.getMessage());
- return null;
+ return new Pair<>(null, null);
}
- return name;
+ return new Pair<>(name, description);
+ }
+
+ // Copied from com.android.server.wallpaper.WallpaperDataParser
+ private WallpaperDescription parseWallpaperDescription(TypedXmlPullParser parser,
+ ComponentName component) throws XmlPullParserException, IOException {
+
+ WallpaperDescription description = null;
+ int type = parser.next();
+ if (type == XmlPullParser.START_TAG && "description".equals(parser.getName())) {
+ // Always read the description if it's there - there may be one from a previous save
+ // with content handling enabled even if it's enabled now
+ description = WallpaperDescription.restoreFromXml(parser);
+ if (liveWallpaperContentHandling()) {
+ // null component means that wallpaper was last saved without content handling, so
+ // populate description from saved component
+ if (description.getComponent() == null) {
+ description = description.toBuilder().setComponent(component).build();
+ }
+ }
+ }
+ return description;
}
private int getAttributeInt(XmlPullParser parser, String name, int defValue) {
@@ -1037,14 +1079,16 @@ public class WallpaperBackupAgent extends BackupAgent {
// Intentionally blank
}
- private void applyComponentAtInstall(ComponentName componentName, int which) {
- PackageMonitor packageMonitor = getWallpaperPackageMonitor(
- componentName, which);
+ private void applyComponentAtInstall(ComponentName componentName,
+ @Nullable WallpaperDescription description, int which) {
+ PackageMonitor packageMonitor = getWallpaperPackageMonitor(componentName, description,
+ which);
packageMonitor.register(getBaseContext(), null, UserHandle.ALL, true);
}
@VisibleForTesting
- PackageMonitor getWallpaperPackageMonitor(ComponentName componentName, int which) {
+ PackageMonitor getWallpaperPackageMonitor(ComponentName componentName,
+ @Nullable WallpaperDescription description, int which) {
return new PackageMonitor() {
@Override
public void onPackageAdded(String packageName, int uid) {
@@ -1068,7 +1112,33 @@ public class WallpaperBackupAgent extends BackupAgent {
return;
}
- if (componentName.getPackageName().equals(packageName)) {
+ boolean useDescription = (liveWallpaperContentHandling() && description != null
+ && description.getComponent() != null);
+ if (useDescription && description.getComponent().getPackageName().equals(
+ packageName)) {
+ Slog.d(TAG, "Applying description " + description);
+ boolean success = mWallpaperManager.setWallpaperComponentWithDescription(
+ description, which);
+ WallpaperEventLogger logger = new WallpaperEventLogger(
+ mBackupManager.getDelayedRestoreLogger());
+ if (success) {
+ if ((which & FLAG_SYSTEM) != 0) {
+ logger.onSystemLiveWallpaperRestoredWithDescription(description);
+ }
+ if ((which & FLAG_LOCK) != 0) {
+ logger.onLockLiveWallpaperRestoredWithDescription(description);
+ }
+ } else {
+ if ((which & FLAG_SYSTEM) != 0) {
+ logger.onSystemLiveWallpaperRestoreFailed(
+ WallpaperEventLogger.ERROR_SET_DESCRIPTION_EXCEPTION);
+ }
+ if ((which & FLAG_LOCK) != 0) {
+ logger.onLockLiveWallpaperRestoreFailed(
+ WallpaperEventLogger.ERROR_SET_DESCRIPTION_EXCEPTION);
+ }
+ }
+ } else if (componentName.getPackageName().equals(packageName)) {
Slog.d(TAG, "Applying component " + componentName);
boolean success = mWallpaperManager.setWallpaperComponentWithFlags(
componentName, which);
@@ -1191,4 +1261,4 @@ public class WallpaperBackupAgent extends BackupAgent {
void setBackupManagerForTesting(BackupManager backupManager) {
mBackupManager = backupManager;
}
-} \ No newline at end of file
+}
diff --git a/packages/WallpaperBackup/src/com/android/wallpaperbackup/WallpaperEventLogger.java b/packages/WallpaperBackup/src/com/android/wallpaperbackup/WallpaperEventLogger.java
index b25f95ab1936..69469e427266 100644
--- a/packages/WallpaperBackup/src/com/android/wallpaperbackup/WallpaperEventLogger.java
+++ b/packages/WallpaperBackup/src/com/android/wallpaperbackup/WallpaperEventLogger.java
@@ -16,12 +16,14 @@
package com.android.wallpaperbackup;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.WallpaperInfo;
import android.app.backup.BackupManager;
import android.app.backup.BackupRestoreEventLogger;
import android.app.backup.BackupRestoreEventLogger.BackupRestoreDataType;
import android.app.backup.BackupRestoreEventLogger.BackupRestoreError;
+import android.app.wallpaper.WallpaperDescription;
import android.content.ComponentName;
import com.android.internal.annotations.VisibleForTesting;
@@ -53,6 +55,16 @@ public class WallpaperEventLogger {
@VisibleForTesting
static final String WALLPAPER_LIVE_LOCK = "wlp_live_lock";
+ /* Live component used as system (or home) screen wallpaper */
+ @BackupRestoreDataType
+ @VisibleForTesting
+ static final String WALLPAPER_DESCRIPTION_SYSTEM = "wlp_description_system";
+
+ /* Live component used as lock screen wallpaper */
+ @BackupRestoreDataType
+ @VisibleForTesting
+ static final String WALLPAPER_DESCRIPTION_LOCK = "wlp_description_lock";
+
@BackupRestoreError
static final String ERROR_INELIGIBLE = "ineligible";
@BackupRestoreError
@@ -64,6 +76,8 @@ public class WallpaperEventLogger {
@BackupRestoreError
static final String ERROR_SET_COMPONENT_EXCEPTION = "exception_in_set_component";
@BackupRestoreError
+ static final String ERROR_SET_DESCRIPTION_EXCEPTION = "exception_in_set_description";
+ @BackupRestoreError
static final String ERROR_LIVE_PACKAGE_NOT_INSTALLED = "live_pkg_not_installed_in_restore";
private final BackupRestoreEventLogger mLogger;
@@ -115,11 +129,11 @@ public class WallpaperEventLogger {
}
void onSystemImageWallpaperRestored() {
- logRestoreSuccessInternal(WALLPAPER_IMG_SYSTEM, /* liveComponentWallpaperInfo */ null);
+ logRestoreSuccessInternal(WALLPAPER_IMG_SYSTEM, (ComponentName) null);
}
void onLockImageWallpaperRestored() {
- logRestoreSuccessInternal(WALLPAPER_IMG_LOCK, /* liveComponentWallpaperInfo */ null);
+ logRestoreSuccessInternal(WALLPAPER_IMG_LOCK, (ComponentName) null);
}
void onSystemLiveWallpaperRestored(ComponentName wpService) {
@@ -146,6 +160,13 @@ public class WallpaperEventLogger {
logRestoreFailureInternal(WALLPAPER_LIVE_LOCK, error);
}
+ void onSystemLiveWallpaperRestoredWithDescription(@NonNull WallpaperDescription description) {
+ logRestoreSuccessInternal(WALLPAPER_DESCRIPTION_SYSTEM, description);
+ }
+
+ void onLockLiveWallpaperRestoredWithDescription(@NonNull WallpaperDescription description) {
+ logRestoreSuccessInternal(WALLPAPER_DESCRIPTION_LOCK, description);
+ }
/**
@@ -168,15 +189,17 @@ public class WallpaperEventLogger {
*/
void onRestoreException(Exception exception) {
String error = exception.getClass().getName();
- if (!mProcessedDataTypes.contains(WALLPAPER_IMG_SYSTEM) && !mProcessedDataTypes.contains(
- WALLPAPER_LIVE_SYSTEM)) {
+ if (!(mProcessedDataTypes.contains(WALLPAPER_IMG_SYSTEM) || mProcessedDataTypes.contains(
+ WALLPAPER_LIVE_SYSTEM) || mProcessedDataTypes.contains(
+ WALLPAPER_DESCRIPTION_SYSTEM))) {
mLogger.logItemsRestoreFailed(WALLPAPER_IMG_SYSTEM, /* count */ 1, error);
}
- if (!mProcessedDataTypes.contains(WALLPAPER_IMG_LOCK) && !mProcessedDataTypes.contains(
- WALLPAPER_LIVE_LOCK)) {
+ if (!(mProcessedDataTypes.contains(WALLPAPER_IMG_LOCK) || mProcessedDataTypes.contains(
+ WALLPAPER_LIVE_LOCK) || mProcessedDataTypes.contains(WALLPAPER_DESCRIPTION_LOCK))) {
mLogger.logItemsRestoreFailed(WALLPAPER_IMG_LOCK, /* count */ 1, error);
}
}
+
private void logBackupSuccessInternal(@BackupRestoreDataType String which,
@Nullable WallpaperInfo liveComponentWallpaperInfo) {
mLogger.logItemsBackedUp(which, /* count */ 1);
@@ -197,6 +220,13 @@ public class WallpaperEventLogger {
mProcessedDataTypes.add(which);
}
+ private void logRestoreSuccessInternal(@BackupRestoreDataType String which,
+ @NonNull WallpaperDescription description) {
+ mLogger.logItemsRestored(which, /* count */ 1);
+ logRestoredLiveWallpaperDescription(which, description);
+ mProcessedDataTypes.add(which);
+ }
+
private void logRestoreFailureInternal(@BackupRestoreDataType String which,
@BackupRestoreError String error) {
mLogger.logItemsRestoreFailed(which, /* count */ 1, error);
@@ -216,4 +246,11 @@ public class WallpaperEventLogger {
mLogger.logRestoreMetadata(wallpaperType, wpService.getClassName());
}
}
+
+ private void logRestoredLiveWallpaperDescription(@BackupRestoreDataType String wallpaperType,
+ WallpaperDescription description) {
+ if (description != null) {
+ mLogger.logRestoreMetadata(wallpaperType, description.toString());
+ }
+ }
}
diff --git a/packages/WallpaperBackup/test/src/com/android/wallpaperbackup/WallpaperBackupAgentTest.java b/packages/WallpaperBackup/test/src/com/android/wallpaperbackup/WallpaperBackupAgentTest.java
index f5fb644502ab..c9f1b5857d10 100644
--- a/packages/WallpaperBackup/test/src/com/android/wallpaperbackup/WallpaperBackupAgentTest.java
+++ b/packages/WallpaperBackup/test/src/com/android/wallpaperbackup/WallpaperBackupAgentTest.java
@@ -16,6 +16,7 @@
package com.android.wallpaperbackup;
+import static android.app.Flags.FLAG_LIVE_WALLPAPER_CONTENT_HANDLING;
import static android.app.WallpaperManager.FLAG_LOCK;
import static android.app.WallpaperManager.FLAG_SYSTEM;
import static android.os.ParcelFileDescriptor.MODE_READ_ONLY;
@@ -27,6 +28,8 @@ import static com.android.wallpaperbackup.WallpaperEventLogger.ERROR_INELIGIBLE;
import static com.android.wallpaperbackup.WallpaperEventLogger.ERROR_NO_METADATA;
import static com.android.wallpaperbackup.WallpaperEventLogger.ERROR_NO_WALLPAPER;
import static com.android.wallpaperbackup.WallpaperEventLogger.ERROR_QUOTA_EXCEEDED;
+import static com.android.wallpaperbackup.WallpaperEventLogger.WALLPAPER_DESCRIPTION_LOCK;
+import static com.android.wallpaperbackup.WallpaperEventLogger.WALLPAPER_DESCRIPTION_SYSTEM;
import static com.android.wallpaperbackup.WallpaperEventLogger.WALLPAPER_IMG_LOCK;
import static com.android.wallpaperbackup.WallpaperEventLogger.WALLPAPER_IMG_SYSTEM;
import static com.android.wallpaperbackup.WallpaperEventLogger.WALLPAPER_LIVE_LOCK;
@@ -54,6 +57,7 @@ import android.app.backup.BackupManager;
import android.app.backup.BackupRestoreEventLogger;
import android.app.backup.BackupRestoreEventLogger.DataTypeResult;
import android.app.backup.FullBackupDataOutput;
+import android.app.wallpaper.WallpaperDescription;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -63,7 +67,10 @@ import android.graphics.Rect;
import android.os.FileUtils;
import android.os.ParcelFileDescriptor;
import android.os.UserHandle;
+import android.platform.test.annotations.EnableFlags;
+import android.platform.test.flag.junit.SetFlagsRule;
import android.service.wallpaper.WallpaperService;
+import android.util.Pair;
import android.util.SparseArray;
import android.util.Xml;
@@ -79,6 +86,7 @@ import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
+import org.junit.rules.RuleChain;
import org.junit.rules.TemporaryFolder;
import org.junit.runner.RunWith;
import org.mockito.ArgumentMatcher;
@@ -113,12 +121,15 @@ public class WallpaperBackupAgentTest {
@Mock
private BackupManager mBackupManager;
- @Rule
- public TemporaryFolder mTemporaryFolder = new TemporaryFolder();
-
private ContextWithServiceOverrides mContext;
private IsolatedWallpaperBackupAgent mWallpaperBackupAgent;
private ComponentName mWallpaperComponent;
+ private WallpaperDescription mWallpaperDescription;
+
+ private final TemporaryFolder mTemporaryFolder = new TemporaryFolder();
+
+ @Rule
+ public RuleChain mRuleChain = RuleChain.outerRule(new SetFlagsRule()).around(mTemporaryFolder);
@Before
public void setUp() {
@@ -135,6 +146,8 @@ public class WallpaperBackupAgentTest {
BackupAnnotations.OperationType.BACKUP);
mWallpaperComponent = new ComponentName(TEST_WALLPAPER_PACKAGE, "");
+ mWallpaperDescription = new WallpaperDescription.Builder().setComponent(
+ mWallpaperComponent).setId("id").build();
}
@After
@@ -366,11 +379,128 @@ public class WallpaperBackupAgentTest {
}
@Test
- public void testUpdateWallpaperComponent_systemAndLock() throws IOException {
- mWallpaperBackupAgent.mIsDeviceInRestore = true;
- mWallpaperBackupAgent.updateWallpaperComponent(mWallpaperComponent,
+ public void testUpdateWallpaperComponent_immediate_systemAndLock() throws IOException {
+ mWallpaperBackupAgent.mPackageExists = true;
+
+ mWallpaperBackupAgent.updateWallpaperComponent(new Pair<>(mWallpaperComponent, null),
/* which */ FLAG_LOCK | FLAG_SYSTEM);
+ assertThat(mWallpaperBackupAgent.mGetPackageMonitorCallCount).isEqualTo(0);
+ verify(mWallpaperManager, times(1))
+ .setWallpaperComponentWithFlags(mWallpaperComponent, FLAG_LOCK | FLAG_SYSTEM);
+ verify(mWallpaperManager, never())
+ .setWallpaperComponentWithFlags(mWallpaperComponent, FLAG_SYSTEM);
+ verify(mWallpaperManager, never())
+ .setWallpaperComponentWithFlags(mWallpaperComponent, FLAG_LOCK);
+ verify(mWallpaperManager, never()).clear(anyInt());
+ }
+
+ @Test
+ public void testUpdateWallpaperComponent_immediate_systemOnly()
+ throws IOException {
+ mWallpaperBackupAgent.mPackageExists = true;
+
+ mWallpaperBackupAgent.updateWallpaperComponent(new Pair<>(mWallpaperComponent, null),
+ /* which */ FLAG_SYSTEM);
+
+ assertThat(mWallpaperBackupAgent.mGetPackageMonitorCallCount).isEqualTo(0);
+ verify(mWallpaperManager, times(1))
+ .setWallpaperComponentWithFlags(mWallpaperComponent, FLAG_SYSTEM);
+ verify(mWallpaperManager, never())
+ .setWallpaperComponentWithFlags(mWallpaperComponent, FLAG_LOCK);
+ verify(mWallpaperManager, never())
+ .setWallpaperComponentWithFlags(mWallpaperComponent, FLAG_LOCK | FLAG_SYSTEM);
+ verify(mWallpaperManager, never()).clear(anyInt());
+ }
+
+ @Test
+ @EnableFlags(FLAG_LIVE_WALLPAPER_CONTENT_HANDLING)
+ public void testUpdateWallpaperDescription_immediate_systemAndLock()
+ throws IOException {
+ mWallpaperBackupAgent.mPackageExists = true;
+
+ mWallpaperBackupAgent.updateWallpaperComponent(
+ new Pair<>(mWallpaperComponent, mWallpaperDescription), /* which */
+ FLAG_LOCK | FLAG_SYSTEM);
+
+ verify(mWallpaperManager, times(1))
+ .setWallpaperComponentWithDescription(mWallpaperDescription,
+ FLAG_LOCK | FLAG_SYSTEM);
+ verify(mWallpaperManager, never())
+ .setWallpaperComponentWithDescription(mWallpaperDescription, FLAG_SYSTEM);
+ verify(mWallpaperManager, never())
+ .setWallpaperComponentWithDescription(mWallpaperDescription, FLAG_LOCK);
+ verify(mWallpaperManager, never()).clear(anyInt());
+ }
+
+ @Test
+ @EnableFlags(FLAG_LIVE_WALLPAPER_CONTENT_HANDLING)
+ public void testUpdateWallpaperDescription_immediate_systemOnly() throws IOException {
+ mWallpaperBackupAgent.mPackageExists = true;
+
+ mWallpaperBackupAgent.updateWallpaperComponent(
+ new Pair<>(mWallpaperComponent, mWallpaperDescription), /* which */ FLAG_SYSTEM);
+
+ verify(mWallpaperManager, times(1))
+ .setWallpaperComponentWithDescription(mWallpaperDescription, FLAG_SYSTEM);
+ verify(mWallpaperManager, never())
+ .setWallpaperComponentWithDescription(mWallpaperDescription, FLAG_LOCK);
+ verify(mWallpaperManager, never())
+ .setWallpaperComponentWithDescription(mWallpaperDescription,
+ FLAG_LOCK | FLAG_SYSTEM);
+ verify(mWallpaperManager, never()).clear(anyInt());
+ }
+
+ @Test
+ @EnableFlags(FLAG_LIVE_WALLPAPER_CONTENT_HANDLING)
+ public void testUpdateWallpaperDescription_delayed_systemAndLock()
+ throws IOException {
+ mWallpaperBackupAgent.mIsDeviceInRestore = true;
+ mWallpaperBackupAgent.updateWallpaperComponent(
+ new Pair<>(mWallpaperComponent, mWallpaperDescription), /* which */
+ FLAG_LOCK | FLAG_SYSTEM);
+
+ // Imitate wallpaper component installation.
+ mWallpaperBackupAgent.mWallpaperPackageMonitor.onPackageAdded(TEST_WALLPAPER_PACKAGE,
+ /* uid */0);
+ verify(mWallpaperManager, times(1))
+ .setWallpaperComponentWithDescription(mWallpaperDescription,
+ FLAG_LOCK | FLAG_SYSTEM);
+ verify(mWallpaperManager, never())
+ .setWallpaperComponentWithDescription(mWallpaperDescription, FLAG_SYSTEM);
+ verify(mWallpaperManager, never())
+ .setWallpaperComponentWithDescription(mWallpaperDescription, FLAG_LOCK);
+ verify(mWallpaperManager, never()).clear(anyInt());
+ }
+
+ @Test
+ @EnableFlags(FLAG_LIVE_WALLPAPER_CONTENT_HANDLING)
+ public void testUpdateWallpaperDescription_delayed_systemOnly() throws IOException {
+ mWallpaperBackupAgent.mIsDeviceInRestore = true;
+
+ mWallpaperBackupAgent.updateWallpaperComponent(
+ new Pair<>(mWallpaperComponent, mWallpaperDescription), /* which */ FLAG_SYSTEM);
+
+ // Imitate wallpaper component installation.
+ mWallpaperBackupAgent.mWallpaperPackageMonitor.onPackageAdded(TEST_WALLPAPER_PACKAGE,
+ /* uid */0);
+
+ verify(mWallpaperManager, times(1))
+ .setWallpaperComponentWithDescription(mWallpaperDescription, FLAG_SYSTEM);
+ verify(mWallpaperManager, never())
+ .setWallpaperComponentWithDescription(mWallpaperDescription, FLAG_LOCK);
+ verify(mWallpaperManager, never())
+ .setWallpaperComponentWithDescription(mWallpaperDescription,
+ FLAG_LOCK | FLAG_SYSTEM);
+ verify(mWallpaperManager, never()).clear(anyInt());
+ }
+
+ @Test
+ public void testUpdateWallpaperComponent_delayed_systemAndLock() throws IOException {
+ mWallpaperBackupAgent.mIsDeviceInRestore = true;
+
+ mWallpaperBackupAgent.updateWallpaperComponent(new Pair<>(mWallpaperComponent, null),
+ /* which */ FLAG_LOCK | FLAG_SYSTEM);
// Imitate wallpaper component installation.
mWallpaperBackupAgent.mWallpaperPackageMonitor.onPackageAdded(TEST_WALLPAPER_PACKAGE,
/* uid */0);
@@ -384,13 +514,12 @@ public class WallpaperBackupAgentTest {
}
@Test
- public void testUpdateWallpaperComponent_systemOnly()
+ public void testUpdateWallpaperComponent_delayed_systemOnly()
throws IOException {
mWallpaperBackupAgent.mIsDeviceInRestore = true;
- mWallpaperBackupAgent.updateWallpaperComponent(mWallpaperComponent,
+ mWallpaperBackupAgent.updateWallpaperComponent(new Pair<>(mWallpaperComponent, null),
/* which */ FLAG_SYSTEM);
-
// Imitate wallpaper component installation.
mWallpaperBackupAgent.mWallpaperPackageMonitor.onPackageAdded(TEST_WALLPAPER_PACKAGE,
/* uid */0);
@@ -405,11 +534,11 @@ public class WallpaperBackupAgentTest {
}
@Test
- public void testUpdateWallpaperComponent_deviceNotInRestore_doesNotApply()
+ public void testUpdateWallpaperComponent_delayed_deviceNotInRestore_doesNotApply()
throws IOException {
mWallpaperBackupAgent.mIsDeviceInRestore = false;
- mWallpaperBackupAgent.updateWallpaperComponent(mWallpaperComponent,
+ mWallpaperBackupAgent.updateWallpaperComponent(new Pair<>(mWallpaperComponent, null),
/* which */ FLAG_LOCK | FLAG_SYSTEM);
// Imitate wallpaper component installation.
@@ -421,11 +550,11 @@ public class WallpaperBackupAgentTest {
}
@Test
- public void testUpdateWallpaperComponent_differentPackageInstalled_doesNotApply()
+ public void testUpdateWallpaperComponent_delayed_differentPackageInstalled_doesNotApply()
throws IOException {
mWallpaperBackupAgent.mIsDeviceInRestore = false;
- mWallpaperBackupAgent.updateWallpaperComponent(mWallpaperComponent,
+ mWallpaperBackupAgent.updateWallpaperComponent(new Pair<>(mWallpaperComponent, null),
/* which */ FLAG_LOCK | FLAG_SYSTEM);
// Imitate "wrong" wallpaper component installation.
@@ -745,9 +874,8 @@ public class WallpaperBackupAgentTest {
}
@Test
- public void testUpdateWallpaperComponent_delayedRestore_logsSuccess() throws Exception {
+ public void testUpdateWallpaperComponent_delayed_succeeds_logsSuccess() throws Exception {
mWallpaperBackupAgent.mIsDeviceInRestore = true;
- when(mWallpaperManager.setWallpaperComponent(any())).thenReturn(true);
when(mWallpaperManager.setWallpaperComponentWithFlags(any(), eq(FLAG_LOCK | FLAG_SYSTEM)))
.thenReturn(true);
BackupRestoreEventLogger logger = new BackupRestoreEventLogger(
@@ -755,7 +883,7 @@ public class WallpaperBackupAgentTest {
when(mBackupManager.getDelayedRestoreLogger()).thenReturn(logger);
mWallpaperBackupAgent.setBackupManagerForTesting(mBackupManager);
- mWallpaperBackupAgent.updateWallpaperComponent(mWallpaperComponent,
+ mWallpaperBackupAgent.updateWallpaperComponent(new Pair<>(mWallpaperComponent, null),
/* which */ FLAG_LOCK | FLAG_SYSTEM);
// Imitate wallpaper component installation.
mWallpaperBackupAgent.mWallpaperPackageMonitor.onPackageAdded(TEST_WALLPAPER_PACKAGE,
@@ -771,15 +899,41 @@ public class WallpaperBackupAgentTest {
@Test
- public void testUpdateWallpaperComponent_delayedRestoreFails_logsFailure() throws Exception {
+ @EnableFlags(FLAG_LIVE_WALLPAPER_CONTENT_HANDLING)
+ public void testUpdateWallpaperDescription_delayed_succeeds_logsSuccess() throws Exception {
+ mWallpaperBackupAgent.mIsDeviceInRestore = true;
+ when(mWallpaperManager.setWallpaperComponentWithDescription(any(),
+ eq(FLAG_LOCK | FLAG_SYSTEM))).thenReturn(true);
+ BackupRestoreEventLogger logger = new BackupRestoreEventLogger(
+ BackupAnnotations.OperationType.RESTORE);
+ when(mBackupManager.getDelayedRestoreLogger()).thenReturn(logger);
+ mWallpaperBackupAgent.setBackupManagerForTesting(mBackupManager);
+
+ mWallpaperBackupAgent.updateWallpaperComponent(new Pair<>(null, mWallpaperDescription),
+ /* which */ FLAG_LOCK | FLAG_SYSTEM);
+ // Imitate wallpaper component installation.
+ mWallpaperBackupAgent.mWallpaperPackageMonitor.onPackageAdded(TEST_WALLPAPER_PACKAGE,
+ /* uid */0);
+
+ DataTypeResult system = getLoggingResult(WALLPAPER_DESCRIPTION_SYSTEM,
+ logger.getLoggingResults());
+ DataTypeResult lock = getLoggingResult(WALLPAPER_DESCRIPTION_LOCK,
+ logger.getLoggingResults());
+ assertThat(system).isNotNull();
+ assertThat(system.getSuccessCount()).isEqualTo(1);
+ assertThat(lock).isNotNull();
+ assertThat(lock.getSuccessCount()).isEqualTo(1);
+ }
+
+ @Test
+ public void testUpdateWallpaperComponent_delayed_fails_logsFailure() throws Exception {
mWallpaperBackupAgent.mIsDeviceInRestore = true;
- when(mWallpaperManager.setWallpaperComponent(any())).thenReturn(false);
BackupRestoreEventLogger logger = new BackupRestoreEventLogger(
BackupAnnotations.OperationType.RESTORE);
when(mBackupManager.getDelayedRestoreLogger()).thenReturn(logger);
mWallpaperBackupAgent.setBackupManagerForTesting(mBackupManager);
- mWallpaperBackupAgent.updateWallpaperComponent(mWallpaperComponent,
+ mWallpaperBackupAgent.updateWallpaperComponent(new Pair<>(mWallpaperComponent, null),
/* which */ FLAG_LOCK | FLAG_SYSTEM);
// Imitate wallpaper component installation.
mWallpaperBackupAgent.mWallpaperPackageMonitor.onPackageAdded(TEST_WALLPAPER_PACKAGE,
@@ -793,7 +947,29 @@ public class WallpaperBackupAgentTest {
}
@Test
- public void testUpdateWallpaperComponent_delayedRestore_packageNotInstalled_logsFailure()
+ @EnableFlags(FLAG_LIVE_WALLPAPER_CONTENT_HANDLING)
+ public void testUpdateWallpaperDescription_delayed_fails_logsFailure() throws Exception {
+ mWallpaperBackupAgent.mIsDeviceInRestore = true;
+ BackupRestoreEventLogger logger = new BackupRestoreEventLogger(
+ BackupAnnotations.OperationType.RESTORE);
+ when(mBackupManager.getDelayedRestoreLogger()).thenReturn(logger);
+ mWallpaperBackupAgent.setBackupManagerForTesting(mBackupManager);
+
+ mWallpaperBackupAgent.updateWallpaperComponent(new Pair<>(null, mWallpaperDescription),
+ /* which */ FLAG_LOCK | FLAG_SYSTEM);
+ // Imitate wallpaper component installation.
+ mWallpaperBackupAgent.mWallpaperPackageMonitor.onPackageAdded(TEST_WALLPAPER_PACKAGE,
+ /* uid */0);
+
+ DataTypeResult system = getLoggingResult(WALLPAPER_LIVE_SYSTEM, logger.getLoggingResults());
+ assertThat(system).isNotNull();
+ assertThat(system.getFailCount()).isEqualTo(1);
+ assertThat(system.getErrors()).containsKey(
+ WallpaperEventLogger.ERROR_SET_DESCRIPTION_EXCEPTION);
+ }
+
+ @Test
+ public void testUpdateWallpaperComponent_delayed_packageNotInstalled_logsFailure()
throws Exception {
mWallpaperBackupAgent.mIsDeviceInRestore = false;
BackupRestoreEventLogger logger = new BackupRestoreEventLogger(
@@ -801,7 +977,7 @@ public class WallpaperBackupAgentTest {
when(mBackupManager.getDelayedRestoreLogger()).thenReturn(logger);
mWallpaperBackupAgent.setBackupManagerForTesting(mBackupManager);
- mWallpaperBackupAgent.updateWallpaperComponent(mWallpaperComponent,
+ mWallpaperBackupAgent.updateWallpaperComponent(new Pair<>(mWallpaperComponent, null),
/* which */ FLAG_LOCK | FLAG_SYSTEM);
// Imitate wallpaper component installation.
@@ -990,6 +1166,8 @@ public class WallpaperBackupAgentTest {
List<File> mBackedUpFiles = new ArrayList<>();
PackageMonitor mWallpaperPackageMonitor;
boolean mIsDeviceInRestore = false;
+ boolean mPackageExists = false;
+ int mGetPackageMonitorCallCount = 0;
@Override
protected void backupFile(File file, FullBackupDataOutput data) {
@@ -998,7 +1176,7 @@ public class WallpaperBackupAgentTest {
@Override
boolean servicePackageExists(ComponentName comp) {
- return false;
+ return mPackageExists;
}
@Override
@@ -1007,8 +1185,11 @@ public class WallpaperBackupAgentTest {
}
@Override
- PackageMonitor getWallpaperPackageMonitor(ComponentName componentName, int which) {
- mWallpaperPackageMonitor = super.getWallpaperPackageMonitor(componentName, which);
+ PackageMonitor getWallpaperPackageMonitor(ComponentName componentName,
+ WallpaperDescription description, int which) {
+ mGetPackageMonitorCallCount++;
+ mWallpaperPackageMonitor = super.getWallpaperPackageMonitor(componentName, description,
+ which);
return mWallpaperPackageMonitor;
}
diff --git a/packages/WallpaperBackup/test/src/com/android/wallpaperbackup/WallpaperEventLoggerTest.java b/packages/WallpaperBackup/test/src/com/android/wallpaperbackup/WallpaperEventLoggerTest.java
index 383bf2f68217..09aa23e713a5 100644
--- a/packages/WallpaperBackup/test/src/com/android/wallpaperbackup/WallpaperEventLoggerTest.java
+++ b/packages/WallpaperBackup/test/src/com/android/wallpaperbackup/WallpaperEventLoggerTest.java
@@ -16,6 +16,10 @@
package com.android.wallpaperbackup;
+import static android.app.Flags.FLAG_LIVE_WALLPAPER_CONTENT_HANDLING;
+
+import static com.android.wallpaperbackup.WallpaperEventLogger.WALLPAPER_DESCRIPTION_LOCK;
+import static com.android.wallpaperbackup.WallpaperEventLogger.WALLPAPER_DESCRIPTION_SYSTEM;
import static com.android.wallpaperbackup.WallpaperEventLogger.WALLPAPER_IMG_LOCK;
import static com.android.wallpaperbackup.WallpaperEventLogger.WALLPAPER_IMG_SYSTEM;
import static com.android.wallpaperbackup.WallpaperEventLogger.WALLPAPER_LIVE_LOCK;
@@ -31,16 +35,20 @@ import android.app.WallpaperInfo;
import android.app.backup.BackupAnnotations;
import android.app.backup.BackupManager;
import android.app.backup.BackupRestoreEventLogger;
+import android.app.wallpaper.WallpaperDescription;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
+import android.platform.test.annotations.EnableFlags;
+import android.platform.test.flag.junit.SetFlagsRule;
import android.service.wallpaper.WallpaperService;
import androidx.test.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
@@ -63,6 +71,10 @@ public class WallpaperEventLoggerTest {
private WallpaperEventLogger mWallpaperEventLogger;
private WallpaperInfo mWallpaperInfo;
+ private WallpaperDescription mWallpaperDescription;
+
+ @Rule
+ public SetFlagsRule mSetFlagsRule = new SetFlagsRule();
@Before
public void setUp() throws Exception {
@@ -73,6 +85,8 @@ public class WallpaperEventLoggerTest {
mWallpaperInfo = getWallpaperInfo();
mWallpaperEventLogger = new WallpaperEventLogger(mMockBackupManager, mMockBackupAgent);
+ mWallpaperDescription = new WallpaperDescription.Builder().setComponent(
+ mWallpaperInfo.getComponent()).build();
}
@Test
@@ -263,6 +277,19 @@ public class WallpaperEventLoggerTest {
}
@Test
+ @EnableFlags(FLAG_LIVE_WALLPAPER_CONTENT_HANDLING)
+ public void onSystemLiveWallpaperRestoredWithDescription_logsSuccess() {
+ setUpLoggerForRestore();
+
+ mWallpaperEventLogger.onSystemLiveWallpaperRestoredWithDescription(mWallpaperDescription);
+ BackupRestoreEventLogger.DataTypeResult result = getLogsForType(
+ WALLPAPER_DESCRIPTION_SYSTEM);
+
+ assertThat(result).isNotNull();
+ assertThat(result.getSuccessCount()).isEqualTo(1);
+ }
+
+ @Test
public void onLockLiveWallpaperRestored_logsSuccess() {
setUpLoggerForRestore();
@@ -274,6 +301,17 @@ public class WallpaperEventLoggerTest {
}
@Test
+ public void onLockLiveWallpaperRestoredWithDescription_logsSuccess() {
+ setUpLoggerForRestore();
+
+ mWallpaperEventLogger.onLockLiveWallpaperRestoredWithDescription(mWallpaperDescription);
+ BackupRestoreEventLogger.DataTypeResult result = getLogsForType(WALLPAPER_DESCRIPTION_LOCK);
+
+ assertThat(result).isNotNull();
+ assertThat(result.getSuccessCount()).isEqualTo(1);
+ }
+
+ @Test
public void onImgWallpaperRestored_nullInfo_doesNotLogMetadata() {
setUpLoggerForRestore();
@@ -286,7 +324,7 @@ public class WallpaperEventLoggerTest {
@Test
- public void onLiveWallpaperRestored_logsMetadata() {
+ public void onSystemLiveWallpaperRestored_logsMetadata() {
setUpLoggerForRestore();
mWallpaperEventLogger.onSystemLiveWallpaperRestored(mWallpaperInfo.getComponent());
@@ -296,6 +334,19 @@ public class WallpaperEventLoggerTest {
assertThat(result.getMetadataHash()).isNotNull();
}
+ @Test
+ @EnableFlags(FLAG_LIVE_WALLPAPER_CONTENT_HANDLING)
+ public void onSystemLiveWallpaperRestoredDescription_logsMetadata() {
+ setUpLoggerForRestore();
+
+ mWallpaperEventLogger.onSystemLiveWallpaperRestoredWithDescription(mWallpaperDescription);
+ BackupRestoreEventLogger.DataTypeResult result = getLogsForType(
+ WALLPAPER_DESCRIPTION_SYSTEM);
+
+ assertThat(result).isNotNull();
+ assertThat(result.getMetadataHash()).isNotNull();
+ }
+
@Test
public void onSystemImgWallpaperRestoreFailed_logsFail() {
@@ -373,7 +424,7 @@ public class WallpaperEventLoggerTest {
}
@Test
- public void onWallpaperRestoreException_liveTypeProcessed_doesNotLogForSameImgType() {
+ public void onSystemWallpaperRestoreException_liveTypeProcessed_doesNotLogForSameImgType() {
setUpLoggerForRestore();
mWallpaperEventLogger.onSystemLiveWallpaperRestored(mWallpaperInfo.getComponent());
@@ -383,6 +434,41 @@ public class WallpaperEventLoggerTest {
assertThat(result).isNull();
}
+ @Test
+ @EnableFlags(FLAG_LIVE_WALLPAPER_CONTENT_HANDLING)
+ public void onSystemWallpaperRestoreException_descriptionProcessed_doesNotLogForSameImgType() {
+ setUpLoggerForRestore();
+ mWallpaperEventLogger.onSystemLiveWallpaperRestoredWithDescription(mWallpaperDescription);
+
+ mWallpaperEventLogger.onRestoreException(new Exception());
+ BackupRestoreEventLogger.DataTypeResult result = getLogsForType(WALLPAPER_IMG_SYSTEM);
+
+ assertThat(result).isNull();
+ }
+
+ @Test
+ public void onLockWallpaperRestoreException_liveTypeProcessed_doesNotLogForSameImgType() {
+ setUpLoggerForRestore();
+ mWallpaperEventLogger.onLockLiveWallpaperRestored(mWallpaperInfo.getComponent());
+
+ mWallpaperEventLogger.onRestoreException(new Exception());
+ BackupRestoreEventLogger.DataTypeResult result = getLogsForType(WALLPAPER_IMG_LOCK);
+
+ assertThat(result).isNull();
+ }
+
+ @Test
+ @EnableFlags(FLAG_LIVE_WALLPAPER_CONTENT_HANDLING)
+ public void onLockWallpaperRestoreException_descriptionProcessed_doesNotLogForSameImgType() {
+ setUpLoggerForRestore();
+ mWallpaperEventLogger.onLockLiveWallpaperRestoredWithDescription(mWallpaperDescription);
+
+ mWallpaperEventLogger.onRestoreException(new Exception());
+ BackupRestoreEventLogger.DataTypeResult result = getLogsForType(WALLPAPER_IMG_LOCK);
+
+ assertThat(result).isNull();
+ }
+
private BackupRestoreEventLogger.DataTypeResult getLogsForType(String dataType) {
for (BackupRestoreEventLogger.DataTypeResult result : mEventLogger.getLoggingResults()) {
if ((result.getDataType()).equals(dataType)) {
diff --git a/ravenwood/scripts/ravenwood-stats-collector.sh b/ravenwood/scripts/ravenwood-stats-collector.sh
index b83216af95fe..c2bf8d82e272 100755
--- a/ravenwood/scripts/ravenwood-stats-collector.sh
+++ b/ravenwood/scripts/ravenwood-stats-collector.sh
@@ -29,21 +29,46 @@ mkdir -p $out_dir
mkdir -p $keep_all_dir
mkdir -p $dump_dir
-# Where the input files are.
-path=$ANDROID_BUILD_TOP/out/host/linux-x86/testcases/ravenwood-stats-checker/x86_64/
-timestamp="$(date --iso-8601=seconds)"
+stats_checker_module="ravenwood-stats-checker"
+minfo=$OUT/module-info.json
-m() {
- ${ANDROID_BUILD_TOP}/build/soong/soong_ui.bash --make-mode "$@"
-}
+timestamp="$(date --iso-8601=seconds)"
-# Building this will generate the files we need.
-m ravenwood-stats-checker
+# First, use jq to get the output files from the checker module. This will be something like this:
+#
+# ---
+# out/host/linux-x86/nativetest64/ravenwood-stats-checker/framework-configinfrastructure_apis.csv
+# out/host/linux-x86/nativetest64/ravenwood-stats-checker/framework-configinfrastructure_dump.txt
+# :
+# out/host/linux-x86/nativetest64/ravenwood-stats-checker/hoststubgen_services.core_stats.csv
+# out/host/linux-x86/nativetest64/ravenwood-stats-checker/ravenwood-stats-checker
+# ---
+# Then, use grep to find the script's path (the last line in the above examle)
+script_path="$(
+ jq -r ".\"$stats_checker_module\".installed | .[]" $minfo |
+ grep '/ravenwood-stats-checker$'
+)"
+
+if [[ "$script_path" == "" ]] ; then
+ echo "Error: $stats_checker_module script not found from $minfo"
+ exit 1
+fi
+
+# This is the directory where our input files are.
+script_dir="$ANDROID_BUILD_TOP/$(dirname "$script_path")"
+
+# Clear it before (re-)buildign the script, to make sure we won't have stale files.
+rm -fr "$script_dir"
+
+# Then build it, which will also collect the input files in the same dir.
+echo "Collecting the input files..."
+m "$stats_checker_module"
# Start...
-cd $path
+echo "Files directory is: $script_dir"
+cd "$script_dir"
dump() {
local jar=$1
@@ -55,6 +80,7 @@ dump() {
collect_stats() {
local out="$1"
+ local desc="$2"
{
# Copy the header, with the first column appended.
echo -n "Jar,Generated Date,"
@@ -66,11 +92,12 @@ collect_stats() {
dump "framework-statsd" framework-statsd_stats.csv
} > "$out"
- echo "Stats CVS created at $out"
+ echo "Stats CVS created at $out$desc"
}
collect_apis() {
local out="$1"
+ local desc="$2"
{
# Copy the header, with the first column appended.
echo -n "Jar,Generated Date,"
@@ -82,12 +109,12 @@ collect_apis() {
dump "framework-statsd" framework-statsd_apis.csv
} > "$out"
- echo "API CVS created at $out"
+ echo "API CVS created at $out$desc"
}
-collect_stats $stats
-collect_apis $apis
+collect_stats $stats " (import it as 'ravenwood_stats')"
+collect_apis $apis " (import it as 'ravenwood_supported_apis')"
cp *keep_all.txt $keep_all_dir
echo "Keep all files created at:"
diff --git a/ravenwood/tools/hoststubgen/Android.bp b/ravenwood/tools/hoststubgen/Android.bp
index 004834eed983..e605318f4a10 100644
--- a/ravenwood/tools/hoststubgen/Android.bp
+++ b/ravenwood/tools/hoststubgen/Android.bp
@@ -99,6 +99,7 @@ java_library_host {
"ow2-asm-commons",
"ow2-asm-tree",
"ow2-asm-util",
+ "apache-commons-compress",
],
}
diff --git a/ravenwood/tools/hoststubgen/README.md b/ravenwood/tools/hoststubgen/README.md
index 615e7671bea1..9f72611953c2 100644
--- a/ravenwood/tools/hoststubgen/README.md
+++ b/ravenwood/tools/hoststubgen/README.md
@@ -17,62 +17,3 @@ AndroidHeuristicsFilter has hardcoded heuristics to detect AIDL generated classe
- More Android specific build files and code are stored in `frameworks/base/Ravenwood.bp`
`frameworks/base/ravenwood`.
-
-## Directories and files
-
-- `src/`
-
- HostStubGen tool source code.
-
-- `annotations-src/` See `Android.bp`.
-- `helper-framework-buildtime-src/` See `Android.bp`.
-- `helper-framework-runtime-src/` See `Android.bp`.
-- `helper-runtime-src/` See `Android.bp`.
-
-- `test-tiny-framework/` See `README.md` in it.
-
-- `scripts`
- - `dump-jar.sh`
-
- A script to dump the content of `*.class` and `*.jar` files.
-
- - `run-all-tests.sh`
-
- Run all tests. Many tests may fail, but at least this should run til the end.
- (It should print `run-all-tests.sh finished` at the end)
-
-## Build and run
-
-### Building `HostStubGen` binary
-
-```
-m hoststubgen
-```
-
-### Run the tests
-
-- Run all relevant tests and test scripts. All of it is expected to pass, and it'll print
- "Ready to submit" at the end.
-
- However, because some of the script it executes depend on internal file paths to Soong's
- intermediate directory, some of it might fail when something changes in the build system.
-
- We need proper build system integration to fix them.
-```
-$ ./scripts/run-all-tests.sh
-```
-
-- See also `README.md` in `test-*` directories.
-
-## TODOs, etc
-
- - Make sure the parent's visibility is not smaller than the member's.
-
-- @HostSideTestNativeSubstitutionClass should automatically add class-keep to the substitute class.
- (or at least check it.)
-
- - The `HostStubGenTest-framework-test-host-test-lib` jar somehow contain all ASM classes? Figure out where the dependency is coming from.
-
-- At some point, we can move or delete all Android specific code to `frameworks/base/ravenwood`.
- - `helper-framework-*-src` should be moved to `frameworks/base/ravenwood`
- - `test-framework` should be deleted.
diff --git a/ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/HostStubGenStats.kt b/ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/HostStubGenStats.kt
index 9045db210495..ea8c25b6833c 100644
--- a/ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/HostStubGenStats.kt
+++ b/ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/HostStubGenStats.kt
@@ -15,13 +15,24 @@
*/
package com.android.hoststubgen
+import com.android.hoststubgen.asm.ClassNodes
import com.android.hoststubgen.asm.getOuterClassNameFromFullClassName
import com.android.hoststubgen.asm.getPackageNameFromFullClassName
import com.android.hoststubgen.filters.FilterPolicyWithReason
+import com.android.hoststubgen.filters.StatsLabel
import org.objectweb.asm.Opcodes
import java.io.PrintWriter
-open class HostStubGenStats {
+/**
+ * TODO This is for the legacy API coverage stats CSV that shows how many APIs are "supported"
+ * in each class with some heuristics. We created [ApiDumper] later, which dumpps all methods
+ * with the "supported" status. We should update the coverage dashboard to use the [ApiDumper]
+ * output and remove this class, once we port all the heuristics to [ApiDumper] as well.
+ * (For example, this class ignores non-public and/or abstract methods, but [ApiDumper] shows
+ * all of them in the same way. We should probably mark them as "Boring" or maybe "Ignore"
+ * for [ApiDumper])
+ */
+open class HostStubGenStats(val classes: ClassNodes) {
data class Stats(
var supported: Int = 0,
var total: Int = 0,
@@ -30,14 +41,6 @@ open class HostStubGenStats {
private val stats = mutableMapOf<String, Stats>()
- data class Api(
- val fullClassName: String,
- val methodName: String,
- val methodDesc: String,
- )
-
- private val apis = mutableListOf<Api>()
-
fun onVisitPolicyForMethod(
fullClassName: String,
methodName: String,
@@ -45,16 +48,16 @@ open class HostStubGenStats {
policy: FilterPolicyWithReason,
access: Int
) {
- if (policy.policy.isSupported) {
- apis.add(Api(fullClassName, methodName, descriptor))
- }
-
// Ignore methods that aren't public
if ((access and Opcodes.ACC_PUBLIC) == 0) return
// Ignore methods that are abstract
if ((access and Opcodes.ACC_ABSTRACT) != 0) return
+
// Ignore methods where policy isn't relevant
- if (policy.isIgnoredForStats) return
+ val statsLabel = policy.statsLabel
+ if (statsLabel == StatsLabel.Ignored) return
+
+ val cn = classes.findClass(fullClassName) ?: return
val packageName = getPackageNameFromFullClassName(fullClassName)
val className = getOuterClassNameFromFullClassName(fullClassName)
@@ -70,7 +73,7 @@ open class HostStubGenStats {
val packageStats = stats.getOrPut(packageName) { Stats() }
val classStats = packageStats.children.getOrPut(className) { Stats() }
- if (policy.policy.isSupported) {
+ if (statsLabel == StatsLabel.Supported) {
packageStats.supported += 1
classStats.supported += 1
}
diff --git a/ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/Utils.kt b/ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/Utils.kt
index b2af7827f8c5..a62f66dd18e5 100644
--- a/ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/Utils.kt
+++ b/ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/Utils.kt
@@ -16,6 +16,15 @@
package com.android.hoststubgen
import java.io.PrintWriter
+import java.util.zip.CRC32
+import org.apache.commons.compress.archivers.zip.ZipArchiveEntry
+import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream
+import org.apache.commons.compress.archivers.zip.ZipFile
+
+/**
+ * Whether to skip compression when adding processed entries back to a zip file.
+ */
+private const val SKIP_COMPRESSION = false
/**
* Name of this executable. Set it in the main method.
@@ -118,3 +127,29 @@ inline fun runMainWithBoilerplate(realMain: () -> Unit) {
System.exit(if (success) 0 else 1 )
}
+
+/**
+ * Copy a single ZIP entry to the output.
+ */
+fun copyZipEntry(
+ inZip: ZipFile,
+ entry: ZipArchiveEntry,
+ out: ZipArchiveOutputStream,
+) {
+ inZip.getRawInputStream(entry).use { out.addRawArchiveEntry(entry, it) }
+}
+
+/**
+ * Add a single ZIP entry with data.
+ */
+fun ZipArchiveOutputStream.addBytesEntry(name: String, data: ByteArray) {
+ val newEntry = ZipArchiveEntry(name)
+ if (SKIP_COMPRESSION) {
+ newEntry.method = 0
+ newEntry.size = data.size.toLong()
+ newEntry.crc = CRC32().apply { update(data) }.value
+ }
+ putArchiveEntry(newEntry)
+ write(data)
+ closeArchiveEntry()
+}
diff --git a/ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/asm/ClassNodes.kt b/ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/asm/ClassNodes.kt
index e2647eb13ed3..40d343ab9658 100644
--- a/ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/asm/ClassNodes.kt
+++ b/ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/asm/ClassNodes.kt
@@ -18,21 +18,20 @@ package com.android.hoststubgen.asm
import com.android.hoststubgen.ClassParseException
import com.android.hoststubgen.InvalidJarFileException
import com.android.hoststubgen.log
-import org.objectweb.asm.ClassReader
-import org.objectweb.asm.tree.AnnotationNode
-import org.objectweb.asm.tree.ClassNode
-import org.objectweb.asm.tree.FieldNode
-import org.objectweb.asm.tree.MethodNode
-import org.objectweb.asm.tree.TypeAnnotationNode
-import java.io.BufferedInputStream
import java.io.PrintWriter
import java.util.Arrays
import java.util.concurrent.Executors
import java.util.concurrent.TimeUnit
import java.util.concurrent.atomic.AtomicReference
import java.util.function.Consumer
-import java.util.zip.ZipEntry
-import java.util.zip.ZipFile
+import org.apache.commons.compress.archivers.zip.ZipArchiveEntry
+import org.apache.commons.compress.archivers.zip.ZipFile
+import org.objectweb.asm.ClassReader
+import org.objectweb.asm.tree.AnnotationNode
+import org.objectweb.asm.tree.ClassNode
+import org.objectweb.asm.tree.FieldNode
+import org.objectweb.asm.tree.MethodNode
+import org.objectweb.asm.tree.TypeAnnotationNode
/**
* Stores all classes loaded from a jar file, in a form of [ClassNode]
@@ -189,7 +188,8 @@ class ClassNodes {
* Load all the classes, without code.
*/
fun loadClassStructures(
- inJar: String,
+ inJar: ZipFile,
+ jarName: String,
timeCollector: Consumer<Double>? = null,
): ClassNodes {
val allClasses = ClassNodes()
@@ -201,20 +201,19 @@ class ClassNodes {
val exception = AtomicReference<Throwable>()
// Called on a BG thread. Read a single jar entry and add it to [allClasses].
- fun parseClass(inZip: ZipFile, entry: ZipEntry) {
+ fun parseClass(inZip: ZipFile, entry: ZipArchiveEntry) {
try {
- inZip.getInputStream(entry).use { ins ->
- val cr = ClassReader(BufferedInputStream(ins))
- val cn = ClassNode()
- cr.accept(
- cn, ClassReader.SKIP_CODE
- or ClassReader.SKIP_DEBUG
- or ClassReader.SKIP_FRAMES
- )
- synchronized(allClasses) {
- if (!allClasses.addClass(cn)) {
- log.w("Duplicate class found: ${cn.name}")
- }
+ val classBytes = inZip.getInputStream(entry).use { it.readAllBytes() }
+ val cr = ClassReader(classBytes)
+ val cn = ClassNode()
+ cr.accept(
+ cn, ClassReader.SKIP_CODE
+ or ClassReader.SKIP_DEBUG
+ or ClassReader.SKIP_FRAMES
+ )
+ synchronized(allClasses) {
+ if (!allClasses.addClass(cn)) {
+ log.w("Duplicate class found: ${cn.name}")
}
}
} catch (e: Throwable) {
@@ -224,35 +223,30 @@ class ClassNodes {
}
// Actually open the jar and read it on worker threads.
- val time = log.iTime("Reading class structure from $inJar") {
+ val time = log.iTime("Reading class structure from $jarName") {
log.withIndent {
- ZipFile(inJar).use { inZip ->
- val inEntries = inZip.entries()
-
- while (inEntries.hasMoreElements()) {
- val entry = inEntries.nextElement()
-
- if (entry.name.endsWith(".class")) {
- executor.submit {
- parseClass(inZip, entry)
- }
- } else if (entry.name.endsWith(".dex")) {
- // Seems like it's an ART jar file. We can't process it.
- // It's a fatal error.
- throw InvalidJarFileException(
- "$inJar is not a desktop jar file."
- + " It contains a *.dex file."
- )
- } else {
- // Unknown file type. Skip.
+ inJar.entries.asSequence().forEach { entry ->
+ if (entry.name.endsWith(".class")) {
+ executor.submit {
+ parseClass(inJar, entry)
}
+ } else if (entry.name.endsWith(".dex")) {
+ // Seems like it's an ART jar file. We can't process it.
+ // It's a fatal error.
+ throw InvalidJarFileException(
+ "$jarName is not a desktop jar file."
+ + " It contains a *.dex file."
+ )
+ } else {
+ // Unknown file type. Skip.
}
- // Wait for all the work to complete. (must do it before closing the zip)
- log.i("Waiting for all loaders to finish...")
- executor.shutdown()
- executor.awaitTermination(5, TimeUnit.MINUTES)
- log.i("All loaders to finished.")
}
+
+ // Wait for all the work to complete. (must do it before closing the zip)
+ log.i("Waiting for all loaders to finish...")
+ executor.shutdown()
+ executor.awaitTermination(5, TimeUnit.MINUTES)
+ log.i("All loaders to finished.")
}
// If any exception is detected, throw it.
@@ -261,13 +255,13 @@ class ClassNodes {
}
if (allClasses.size == 0) {
- log.w("$inJar contains no *.class files.")
+ log.w("$jarName contains no *.class files.")
} else {
- log.i("Loaded ${allClasses.size} classes from $inJar.")
+ log.i("Loaded ${allClasses.size} classes from $jarName.")
}
}
timeCollector?.accept(time)
return allClasses
}
}
-} \ No newline at end of file
+}
diff --git a/ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/dumper/ApiDumper.kt b/ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/dumper/ApiDumper.kt
index 5e4e70f0cbaa..bb8cdccafaa6 100644
--- a/ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/dumper/ApiDumper.kt
+++ b/ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/dumper/ApiDumper.kt
@@ -25,6 +25,7 @@ import com.android.hoststubgen.csvEscape
import com.android.hoststubgen.filters.FilterPolicy
import com.android.hoststubgen.filters.FilterPolicyWithReason
import com.android.hoststubgen.filters.OutputFilter
+import com.android.hoststubgen.filters.StatsLabel
import com.android.hoststubgen.log
import org.objectweb.asm.Type
import org.objectweb.asm.tree.ClassNode
@@ -44,7 +45,10 @@ class ApiDumper(
val descriptor: String,
)
- private val javaStandardApiPolicy = FilterPolicy.Keep.withReason("Java standard API")
+ private val javaStandardApiPolicy = FilterPolicy.Keep.withReason(
+ "Java standard API",
+ StatsLabel.Supported,
+ )
private val shownMethods = mutableSetOf<MethodKey>()
@@ -53,7 +57,7 @@ class ApiDumper(
*/
fun dump() {
pw.printf("PackageName,ClassName,FromSubclass,DeclareClass,MethodName,MethodDesc" +
- ",Supported,Policy,Reason\n")
+ ",Supported,Policy,Reason,SupportedLabel\n")
classes.forEach { classNode ->
shownMethods.clear()
@@ -68,23 +72,36 @@ class ApiDumper(
methodClassName: String,
methodName: String,
methodDesc: String,
- policy: FilterPolicyWithReason,
+ classPolicy: FilterPolicyWithReason,
+ methodPolicy: FilterPolicyWithReason,
) {
+ if (methodPolicy.statsLabel == StatsLabel.Ignored) {
+ return
+ }
+ // Label hack -- if the method is supported, but the class is boring, then the
+ // method is boring too.
+ var methodLabel = methodPolicy.statsLabel
+ if (methodLabel == StatsLabel.SupportedButBoring
+ && classPolicy.statsLabel == StatsLabel.SupportedButBoring) {
+ methodLabel = classPolicy.statsLabel
+ }
+
pw.printf(
- "%s,%s,%d,%s,%s,%s,%d,%s,%s\n",
+ "%s,%s,%d,%s,%s,%s,%d,%s,%s,%s\n",
csvEscape(classPackage),
csvEscape(className),
if (isSuperClass) { 1 } else { 0 },
csvEscape(methodClassName),
csvEscape(methodName),
csvEscape(methodDesc),
- if (policy.policy.isSupported) { 1 } else { 0 },
- policy.policy,
- csvEscape(policy.reason),
+ methodLabel.statValue,
+ methodPolicy.policy,
+ csvEscape(methodPolicy.reason),
+ methodLabel,
)
}
- private fun isDuplicate(methodName: String, methodDesc: String): Boolean {
+ private fun shownAlready(methodName: String, methodDesc: String): Boolean {
val methodKey = MethodKey(methodName, methodDesc)
if (shownMethods.contains(methodKey)) {
@@ -98,6 +115,12 @@ class ApiDumper(
dumpClass: ClassNode,
methodClass: ClassNode,
) {
+ val classPolicy = filter.getPolicyForClass(dumpClass.name)
+ if (classPolicy.statsLabel == StatsLabel.Ignored) {
+ return
+ }
+ log.d("Class ${dumpClass.name} -- policy $classPolicy")
+
val pkg = getPackageNameFromFullClassName(dumpClass.name).toHumanReadableClassName()
val cls = getClassNameFromFullClassName(dumpClass.name).toHumanReadableClassName()
@@ -112,23 +135,23 @@ class ApiDumper(
}
}
// If we already printed the method from a subclass, don't print it.
- if (isDuplicate(method.name, method.desc)) {
+ if (shownAlready(method.name, method.desc)) {
return@forEach
}
- val policy = filter.getPolicyForMethod(methodClass.name, method.name, method.desc)
+ val methodPolicy = filter.getPolicyForMethod(methodClass.name, method.name, method.desc)
// Let's skip "Remove" APIs. Ideally we want to print it, just to make the CSV
// complete, we still need to hide methods substituted (== @RavenwoodReplace) methods
// and for now we don't have an easy way to detect it.
- if (policy.policy == FilterPolicy.Remove) {
+ if (methodPolicy.policy == FilterPolicy.Remove) {
return@forEach
}
val renameTo = filter.getRenameTo(methodClass.name, method.name, method.desc)
dumpMethod(pkg, cls, isSuperClass, methodClass.name.toHumanReadableClassName(),
- renameTo ?: method.name, method.desc, policy)
+ renameTo ?: method.name, method.desc, classPolicy, methodPolicy)
}
// Dump super class methods.
@@ -155,10 +178,13 @@ class ApiDumper(
dump(dumpClass, methodClass)
return
}
- if (methodClassName.startsWith("java/") ||
- methodClassName.startsWith("javax/")
- ) {
- dumpStandardClass(dumpClass, methodClassName)
+
+ // Dump overriding methods from Java standard classes, except for the Object methods,
+ // which are obvious.
+ if (methodClassName.startsWith("java/") || methodClassName.startsWith("javax/")) {
+ if (methodClassName != "java/lang/Object") {
+ dumpStandardClass(dumpClass, methodClassName)
+ }
return
}
log.w("Super class or interface $methodClassName (used by ${dumpClass.name}) not found.")
@@ -188,12 +214,12 @@ class ApiDumper(
val methodDesc = Type.getMethodDescriptor(method)
// If we already printed the method from a subclass, don't print it.
- if (isDuplicate(methodName, methodDesc)) {
+ if (shownAlready(methodName, methodDesc)) {
return@forEach
}
dumpMethod(pkg, cls, true, methodClassName,
- methodName, methodDesc, javaStandardApiPolicy)
+ methodName, methodDesc, javaStandardApiPolicy, javaStandardApiPolicy)
}
} catch (e: ClassNotFoundException) {
log.w("JVM type $methodClassName (used by ${dumpClass.name}) not found.")
diff --git a/ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/filters/ClassWidePolicyPropagatingFilter.kt b/ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/filters/ClassWidePolicyPropagatingFilter.kt
index f8bb526d0a86..760999f5e129 100644
--- a/ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/filters/ClassWidePolicyPropagatingFilter.kt
+++ b/ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/filters/ClassWidePolicyPropagatingFilter.kt
@@ -60,7 +60,7 @@ class ClassWidePolicyPropagatingFilter(
}
return p.withReason(policy.reason)
- .wrapReason("class-wide in $className")
+ .wrapReason("class-wide in $className", policy.statsLabelOverride)
}
// If the class's policy is remove, then remove it.
if (policy.policy == FilterPolicy.Remove) {
diff --git a/ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/filters/FilterPolicy.kt b/ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/filters/FilterPolicy.kt
index 81c26ffdf1f4..c3c870f59347 100644
--- a/ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/filters/FilterPolicy.kt
+++ b/ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/filters/FilterPolicy.kt
@@ -155,7 +155,10 @@ enum class FilterPolicy(val policyStringOrPrefix: String) {
/**
* Create a [FilterPolicyWithReason] with a given reason.
*/
- fun withReason(reason: String): FilterPolicyWithReason {
- return FilterPolicyWithReason(this, reason)
+ fun withReason(
+ reason: String,
+ statsLabelOverride: StatsLabel? = null,
+ ): FilterPolicyWithReason {
+ return FilterPolicyWithReason(this, reason, statsLabelOverride = statsLabelOverride)
}
}
diff --git a/ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/filters/FilterPolicyWithReason.kt b/ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/filters/FilterPolicyWithReason.kt
index b10165b835f2..e082bbb0a119 100644
--- a/ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/filters/FilterPolicyWithReason.kt
+++ b/ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/filters/FilterPolicyWithReason.kt
@@ -16,32 +16,54 @@
package com.android.hoststubgen.filters
/**
+ * How each entry should be handled on the dashboard.
+ */
+enum class StatsLabel(val statValue: Int, val label: String) {
+ /** Entry shouldn't show up in the dashboard. */
+ Ignored(-1, ""),
+
+ /** Entry should be shown as "not supported" */
+ NotSupported(0, "NotSupported"),
+
+ /**
+ * Entry should be shown as "supported", but are too "boring" to show on the dashboard,
+ * e.g. annotation classes.
+ */
+ SupportedButBoring(1, "Boring"),
+
+ /** Entry should be shown as "supported" */
+ Supported(2, "Supported"),
+}
+
+/**
* Captures a [FilterPolicy] with a human-readable reason.
*/
data class FilterPolicyWithReason (
- val policy: FilterPolicy,
- val reason: String = "",
+ val policy: FilterPolicy,
+ val reason: String = "",
+ val statsLabelOverride: StatsLabel? = null
) {
/**
* Return a new [FilterPolicy] with an updated reason, while keeping the original reason
* as an "inner-reason".
*/
- fun wrapReason(reason: String): FilterPolicyWithReason {
- return FilterPolicyWithReason(policy, "$reason [inner-reason: ${this.reason}]")
+ fun wrapReason(reason: String, statsLabelOverride: StatsLabel? = null): FilterPolicyWithReason {
+ return FilterPolicyWithReason(
+ policy,
+ "$reason [inner-reason: ${this.reason}]",
+ statsLabelOverride = statsLabelOverride ?: this.statsLabelOverride,
+ )
}
override fun toString(): String {
- return "[$policy - reason: $reason]"
+ return "[$policy/$statsLabel - reason: $reason]"
}
- /** Returns whether this policy should be ignored for stats. */
- val isIgnoredForStats: Boolean
- get() {
- return reason.contains("anonymous-inner-class")
- || reason.contains("is-annotation")
- || reason.contains("is-enum")
- || reason.contains("is-synthetic-method")
- || reason.contains("special-class")
- || reason.contains("substitute-to")
+ val statsLabel: StatsLabel get() {
+ statsLabelOverride?.let { return it }
+ if (policy.isSupported) {
+ return StatsLabel.Supported
}
+ return StatsLabel.NotSupported
+ }
}
diff --git a/ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/filters/ImplicitOutputFilter.kt b/ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/filters/ImplicitOutputFilter.kt
index d44d016f7c5b..1145da635606 100644
--- a/ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/filters/ImplicitOutputFilter.kt
+++ b/ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/filters/ImplicitOutputFilter.kt
@@ -48,7 +48,11 @@ class ImplicitOutputFilter(
// If the outer class needs to be in impl, it should be in impl too.
val outerPolicy = outermostFilter.getPolicyForClass(cn.outerClass)
if (outerPolicy.policy.needsInOutput) {
- return FilterPolicy.KeepClass.withReason("anonymous-inner-class")
+ // We keep this class, but don't need to show it in the dashboard.
+ return FilterPolicy.KeepClass.withReason(
+ "anonymous-inner-class",
+ StatsLabel.Ignored,
+ )
}
}
return null
@@ -62,6 +66,15 @@ class ImplicitOutputFilter(
// Use the implicit policy, if any.
getClassImplicitPolicy(cn)?.let { return it }
+ // If it's an annotation class and we need to keep it, then
+ // change the reason to hide it from the stats.
+ if (cn.isAnnotation() && fallback.policy.needsInOutput) {
+ return FilterPolicy.KeepClass.withReason(
+ "is-annotation",
+ StatsLabel.Ignored,
+ )
+ }
+
return fallback
}
@@ -102,14 +115,20 @@ class ImplicitOutputFilter(
if (cn.isEnum()) {
mn?.let { mn ->
if (isAutoGeneratedEnumMember(mn)) {
- return memberPolicy.withReason(classPolicy.reason).wrapReason("is-enum")
+ return memberPolicy.withReason(classPolicy.reason).wrapReason(
+ "is-enum",
+ StatsLabel.Ignored,
+ )
}
}
}
// Keep (or stub) all members of annotations.
if (cn.isAnnotation()) {
- return memberPolicy.withReason(classPolicy.reason).wrapReason("is-annotation")
+ return memberPolicy.withReason(classPolicy.reason).wrapReason(
+ "is-annotation",
+ StatsLabel.Ignored,
+ )
}
mn?.let {
@@ -117,7 +136,8 @@ class ImplicitOutputFilter(
// For synthetic methods (such as lambdas), let's just inherit the class's
// policy.
return memberPolicy.withReason(classPolicy.reason).wrapReason(
- "is-synthetic-method"
+ "is-synthetic-method",
+ StatsLabel.Ignored,
)
}
}
diff --git a/ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/filters/KeepNativeFilter.kt b/ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/filters/KeepNativeFilter.kt
index 00e7d77fa6e7..57309b49a2cd 100644
--- a/ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/filters/KeepNativeFilter.kt
+++ b/ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/filters/KeepNativeFilter.kt
@@ -22,6 +22,9 @@ import com.android.hoststubgen.asm.isNative
* For native methods that weren't handled by outer filters, we keep it so that
* native method registration will not crash at runtime. Ideally we shouldn't need
* this, but in practice unsupported native method registrations do occur.
+ *
+ * Native methods kept by this filter will all have a "Keep" policy, but they won't show
+ * up as "supported" in the stats dashboard beucase we set reallySupported to false.
*/
class KeepNativeFilter(
private val classes: ClassNodes,
@@ -34,7 +37,7 @@ class KeepNativeFilter(
): FilterPolicyWithReason {
return classes.findMethod(className, methodName, descriptor)?.let { mn ->
if (mn.isNative()) {
- FilterPolicy.Keep.withReason("native-preserve")
+ FilterPolicy.Keep.withReason("native-preserve", StatsLabel.NotSupported)
} else {
null
}
diff --git a/ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/filters/TextFileFilterPolicyParser.kt b/ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/filters/TextFileFilterPolicyParser.kt
index 97fc35302528..cdcea4c15820 100644
--- a/ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/filters/TextFileFilterPolicyParser.kt
+++ b/ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/filters/TextFileFilterPolicyParser.kt
@@ -528,7 +528,8 @@ class TextFileFilterPolicyParser {
)
}
val p = policy.withReason(
- "$FILTER_REASON (special-class AIDL)"
+ "$FILTER_REASON (special-class AIDL)",
+ StatsLabel.SupportedButBoring,
)
processor.onSpecialClassPolicy(classType, p)
aidlPolicy = p
@@ -541,7 +542,8 @@ class TextFileFilterPolicyParser {
)
}
val p = policy.withReason(
- "$FILTER_REASON (special-class feature flags)"
+ "$FILTER_REASON (special-class feature flags)",
+ StatsLabel.SupportedButBoring,
)
processor.onSpecialClassPolicy(classType, p)
featureFlagsPolicy = p
@@ -554,7 +556,8 @@ class TextFileFilterPolicyParser {
)
}
val p = policy.withReason(
- "$FILTER_REASON (special-class sysprops)"
+ "$FILTER_REASON (special-class sysprops)",
+ StatsLabel.SupportedButBoring,
)
processor.onSpecialClassPolicy(classType, p)
syspropsPolicy = p
@@ -567,7 +570,8 @@ class TextFileFilterPolicyParser {
)
}
val p = policy.withReason(
- "$FILTER_REASON (special-class R file)"
+ "$FILTER_REASON (special-class R file)",
+ StatsLabel.SupportedButBoring,
)
processor.onSpecialClassPolicy(classType, p)
rFilePolicy = p
diff --git a/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/HostStubGen.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/HostStubGen.kt
index 333540573364..3291ff6b8bc6 100644
--- a/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/HostStubGen.kt
+++ b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/HostStubGen.kt
@@ -19,13 +19,11 @@ import com.android.hoststubgen.asm.ClassNodes
import com.android.hoststubgen.dumper.ApiDumper
import com.android.hoststubgen.filters.FilterPolicy
import com.android.hoststubgen.filters.printAsTextPolicy
-import java.io.BufferedInputStream
-import java.io.BufferedOutputStream
import java.io.FileOutputStream
import java.io.PrintWriter
-import java.util.zip.ZipEntry
-import java.util.zip.ZipFile
-import java.util.zip.ZipOutputStream
+import org.apache.commons.compress.archivers.zip.ZipArchiveEntry
+import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream
+import org.apache.commons.compress.archivers.zip.ZipFile
/**
* Actual main class.
@@ -33,10 +31,12 @@ import java.util.zip.ZipOutputStream
class HostStubGen(val options: HostStubGenOptions) {
fun run() {
val errors = HostStubGenErrors()
- val stats = HostStubGenStats()
+ val inJar = ZipFile(options.inJar.get)
// Load all classes.
- val allClasses = ClassNodes.loadClassStructures(options.inJar.get)
+ val allClasses = ClassNodes.loadClassStructures(inJar, options.inJar.get)
+
+ val stats = HostStubGenStats(allClasses)
// Dump the classes, if specified.
options.inputJarDumpFile.ifSet {
@@ -59,7 +59,7 @@ class HostStubGen(val options: HostStubGenOptions) {
val processor = HostStubGenClassProcessor(options, allClasses, errors, stats)
// Transform the jar.
- convert(
+ inJar.convert(
options.inJar.get,
options.outJar.get,
processor,
@@ -88,7 +88,7 @@ class HostStubGen(val options: HostStubGenOptions) {
/**
* Convert a JAR file into "stub" and "impl" JAR files.
*/
- private fun convert(
+ private fun ZipFile.convert(
inJar: String,
outJar: String?,
processor: HostStubGenClassProcessor,
@@ -100,45 +100,39 @@ class HostStubGen(val options: HostStubGenOptions) {
log.i("ASM CheckClassAdapter is %s", if (enableChecker) "enabled" else "disabled")
log.iTime("Transforming jar") {
- var itemIndex = 0
var numItemsProcessed = 0
var numItems = -1 // == Unknown
log.withIndent {
- // Open the input jar file and process each entry.
- ZipFile(inJar).use { inZip ->
-
- numItems = inZip.size()
- val shardStart = numItems * shard / numShards
- val shardNextStart = numItems * (shard + 1) / numShards
-
- maybeWithZipOutputStream(outJar) { outStream ->
- val inEntries = inZip.entries()
- while (inEntries.hasMoreElements()) {
- val entry = inEntries.nextElement()
- val inShard = (shardStart <= itemIndex)
- && (itemIndex < shardNextStart)
- itemIndex++
- if (!inShard) {
- continue
- }
- convertSingleEntry(inZip, entry, outStream, processor)
- numItemsProcessed++
+ val entries = entries.toList()
+
+ numItems = entries.size
+ val shardStart = numItems * shard / numShards
+ val shardNextStart = numItems * (shard + 1) / numShards
+
+ maybeWithZipOutputStream(outJar) { outStream ->
+ entries.forEachIndexed { itemIndex, entry ->
+ val inShard = (shardStart <= itemIndex)
+ && (itemIndex < shardNextStart)
+ if (!inShard) {
+ return@forEachIndexed
}
- log.i("Converted all entries.")
+ convertSingleEntry(this, entry, outStream, processor)
+ numItemsProcessed++
}
- outJar?.let { log.i("Created: $it") }
+ log.i("Converted all entries.")
}
+ outJar?.let { log.i("Created: $it") }
}
log.i("%d / %d item(s) processed.", numItemsProcessed, numItems)
}
}
- private fun <T> maybeWithZipOutputStream(filename: String?, block: (ZipOutputStream?) -> T): T {
+ private fun <T> maybeWithZipOutputStream(filename: String?, block: (ZipArchiveOutputStream?) -> T): T {
if (filename == null) {
return block(null)
}
- return ZipOutputStream(BufferedOutputStream(FileOutputStream(filename))).use(block)
+ return ZipArchiveOutputStream(FileOutputStream(filename).buffered()).use(block)
}
/**
@@ -146,8 +140,8 @@ class HostStubGen(val options: HostStubGenOptions) {
*/
private fun convertSingleEntry(
inZip: ZipFile,
- entry: ZipEntry,
- outStream: ZipOutputStream?,
+ entry: ZipArchiveEntry,
+ outStream: ZipArchiveOutputStream?,
processor: HostStubGenClassProcessor
) {
log.d("Entry: %s", entry.name)
@@ -181,32 +175,12 @@ class HostStubGen(val options: HostStubGenOptions) {
}
/**
- * Copy a single ZIP entry to the output.
- */
- private fun copyZipEntry(
- inZip: ZipFile,
- entry: ZipEntry,
- out: ZipOutputStream,
- ) {
- // TODO: It seems like copying entries this way is _very_ slow,
- // even with out.setLevel(0). Look for other ways to do it.
-
- inZip.getInputStream(entry).use { ins ->
- // Copy unknown entries as is to the impl out. (but not to the stub out.)
- val outEntry = ZipEntry(entry.name)
- out.putNextEntry(outEntry)
- ins.transferTo(out)
- out.closeEntry()
- }
- }
-
- /**
* Convert a single class.
*/
private fun processSingleClass(
inZip: ZipFile,
- entry: ZipEntry,
- outStream: ZipOutputStream?,
+ entry: ZipArchiveEntry,
+ outStream: ZipArchiveOutputStream?,
processor: HostStubGenClassProcessor
) {
val classInternalName = entry.name.replaceFirst("\\.class$".toRegex(), "")
@@ -227,12 +201,10 @@ class HostStubGen(val options: HostStubGenOptions) {
if (outStream != null) {
log.v("Creating class: %s Policy: %s", classInternalName, classPolicy)
log.withIndent {
- BufferedInputStream(inZip.getInputStream(entry)).use { bis ->
- val newEntry = ZipEntry(newName)
- outStream.putNextEntry(newEntry)
- val classBytecode = bis.readAllBytes()
- outStream.write(processor.processClassBytecode(classBytecode))
- outStream.closeEntry()
+ inZip.getInputStream(entry).use { zis ->
+ var classBytecode = zis.readAllBytes()
+ classBytecode = processor.processClassBytecode(classBytecode)
+ outStream.addBytesEntry(newName, classBytecode)
}
}
}
diff --git a/ravenwood/tools/hoststubgen/test-tiny-framework/README.md b/ravenwood/tools/hoststubgen/test-tiny-framework/README.md
deleted file mode 100644
index 344b4e953b23..000000000000
--- a/ravenwood/tools/hoststubgen/test-tiny-framework/README.md
+++ /dev/null
@@ -1,21 +0,0 @@
-# HostStubGen: tiny-framework test
-
-This directory contains a small classes that "simulates" framework.jar, and tests against it.
-
-This test is agnostic to Android, and it doesn't use any android framework code or knowledge.
-
-## How to run
-
-- With `atest`. This is the proper way to run it, but `atest` has known problems that may
- affect the result. If you see weird problems, try the next `run-ravenwood-test` command.
-
-```
-$ atest hoststubgen-test-tiny-test
-```
-
-- `run-test-manually.sh` also run the test, but it builds the stub/impl jars and the test without
- using the build system. This is useful for debugging the tool.
-
-```
-$ ./run-test-manually.sh
-``` \ No newline at end of file
diff --git a/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework-dump-test.py b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework-dump-test.py
index 761748265726..0c2269ab5e0d 100755
--- a/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework-dump-test.py
+++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework-dump-test.py
@@ -33,7 +33,7 @@ def run_diff(file1, file2):
'--ignore-space-change',
# Ignore the class file version.
- '--ignore-matching-lines=^ *\(major\|minor\) version:$',
+ '--ignore-matching-lines=^ *\\(major\\|minor\\) version:$',
# We shouldn't need `--ignore-matching-lines`, but somehow
# the golden files were generated without these lines for b/388562869,
diff --git a/ravenwood/tools/ravenizer/src/com/android/platform/test/ravenwood/ravenizer/Ravenizer.kt b/ravenwood/tools/ravenizer/src/com/android/platform/test/ravenwood/ravenizer/Ravenizer.kt
index 04e3bda2ba27..8e36323fd495 100644
--- a/ravenwood/tools/ravenizer/src/com/android/platform/test/ravenwood/ravenizer/Ravenizer.kt
+++ b/ravenwood/tools/ravenizer/src/com/android/platform/test/ravenwood/ravenizer/Ravenizer.kt
@@ -17,17 +17,17 @@ package com.android.platform.test.ravenwood.ravenizer
import com.android.hoststubgen.GeneralUserErrorException
import com.android.hoststubgen.HostStubGenClassProcessor
+import com.android.hoststubgen.addBytesEntry
import com.android.hoststubgen.asm.ClassNodes
import com.android.hoststubgen.asm.zipEntryNameToClassName
+import com.android.hoststubgen.copyZipEntry
import com.android.hoststubgen.executableName
import com.android.hoststubgen.log
import com.android.platform.test.ravenwood.ravenizer.adapter.RunnerRewritingAdapter
-import java.io.BufferedInputStream
-import java.io.BufferedOutputStream
import java.io.FileOutputStream
-import java.util.zip.ZipEntry
-import java.util.zip.ZipFile
-import java.util.zip.ZipOutputStream
+import org.apache.commons.compress.archivers.zip.ZipArchiveEntry
+import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream
+import org.apache.commons.compress.archivers.zip.ZipFile
import org.objectweb.asm.ClassReader
import org.objectweb.asm.ClassVisitor
import org.objectweb.asm.ClassWriter
@@ -93,13 +93,14 @@ class Ravenizer {
val stats = RavenizerStats()
stats.totalTime = log.nTime {
- val allClasses = ClassNodes.loadClassStructures(options.inJar.get) {
+ val inJar = ZipFile(options.inJar.get)
+ val allClasses = ClassNodes.loadClassStructures(inJar, options.inJar.get) {
stats.loadStructureTime = it
}
val processor = HostStubGenClassProcessor(options, allClasses)
- process(
- options.inJar.get,
+ inJar.process(
+ options.outJar.get,
options.outJar.get,
options.enableValidation.get,
options.fatalValidation.get,
@@ -111,7 +112,7 @@ class Ravenizer {
log.i(stats.toString())
}
- private fun process(
+ private fun ZipFile.process(
inJar: String,
outJar: String,
enableValidation: Boolean,
@@ -138,40 +139,34 @@ class Ravenizer {
}
stats.totalProcessTime = log.vTime("$executableName processing $inJar") {
- ZipFile(inJar).use { inZip ->
- val inEntries = inZip.entries()
-
- stats.totalEntries = inZip.size()
-
- ZipOutputStream(BufferedOutputStream(FileOutputStream(outJar))).use { outZip ->
- while (inEntries.hasMoreElements()) {
- val entry = inEntries.nextElement()
-
- if (entry.name.endsWith(".dex")) {
- // Seems like it's an ART jar file. We can't process it.
- // It's a fatal error.
- throw GeneralUserErrorException(
- "$inJar is not a desktop jar file. It contains a *.dex file."
- )
- }
+ ZipArchiveOutputStream(FileOutputStream(outJar).buffered()).use { outZip ->
+ entries.asSequence().forEach { entry ->
+ stats.totalEntries++
+ if (entry.name.endsWith(".dex")) {
+ // Seems like it's an ART jar file. We can't process it.
+ // It's a fatal error.
+ throw GeneralUserErrorException(
+ "$inJar is not a desktop jar file. It contains a *.dex file."
+ )
+ }
- if (stripMockito && entry.name.isMockitoFile()) {
- // Skip this entry
- continue
- }
+ if (stripMockito && entry.name.isMockitoFile()) {
+ // Skip this entry
+ return@forEach
+ }
- val className = zipEntryNameToClassName(entry.name)
+ val className = zipEntryNameToClassName(entry.name)
- if (className != null) {
- stats.totalClasses += 1
- }
+ if (className != null) {
+ stats.totalClasses += 1
+ }
- if (className != null &&
- shouldProcessClass(processor.allClasses, className)) {
- processSingleClass(inZip, entry, outZip, processor, stats)
- } else {
- // Too slow, let's use merge_zips to bring back the original classes.
- copyZipEntry(inZip, entry, outZip, stats)
+ if (className != null &&
+ shouldProcessClass(processor.allClasses, className)) {
+ processSingleClass(this, entry, outZip, processor, stats)
+ } else {
+ stats.totalCopyTime += log.nTime {
+ copyZipEntry(this, entry, outZip)
}
}
}
@@ -179,53 +174,25 @@ class Ravenizer {
}
}
- /**
- * Copy a single ZIP entry to the output.
- */
- private fun copyZipEntry(
- inZip: ZipFile,
- entry: ZipEntry,
- out: ZipOutputStream,
- stats: RavenizerStats,
- ) {
- stats.totalCopyTime += log.nTime {
- inZip.getInputStream(entry).use { ins ->
- // Copy unknown entries as is to the impl out. (but not to the stub out.)
- val outEntry = ZipEntry(entry.name)
- outEntry.method = 0
- outEntry.size = entry.size
- outEntry.crc = entry.crc
- out.putNextEntry(outEntry)
-
- ins.transferTo(out)
-
- out.closeEntry()
- }
- }
- }
-
private fun processSingleClass(
inZip: ZipFile,
- entry: ZipEntry,
- outZip: ZipOutputStream,
+ entry: ZipArchiveEntry,
+ outZip: ZipArchiveOutputStream,
processor: HostStubGenClassProcessor,
stats: RavenizerStats,
) {
stats.processedClasses += 1
- val newEntry = ZipEntry(entry.name)
- outZip.putNextEntry(newEntry)
-
- BufferedInputStream(inZip.getInputStream(entry)).use { bis ->
- var classBytes = bis.readBytes()
+ inZip.getInputStream(entry).use { zis ->
+ var classBytes = zis.readAllBytes()
stats.totalRavenizeTime += log.vTime("Ravenize ${entry.name}") {
classBytes = ravenizeSingleClass(entry, classBytes, processor.allClasses)
}
stats.totalHostStubGenTime += log.vTime("HostStubGen ${entry.name}") {
classBytes = processor.processClassBytecode(classBytes)
}
- outZip.write(classBytes)
+ // TODO: if the class does not change, use copyZipEntry
+ outZip.addBytesEntry(entry.name, classBytes)
}
- outZip.closeEntry()
}
/**
@@ -237,7 +204,7 @@ class Ravenizer {
}
private fun ravenizeSingleClass(
- entry: ZipEntry,
+ entry: ZipArchiveEntry,
input: ByteArray,
allClasses: ClassNodes,
): ByteArray {
diff --git a/services/Android.bp b/services/Android.bp
index efd35ce8f1a3..8657bfc79316 100644
--- a/services/Android.bp
+++ b/services/Android.bp
@@ -181,7 +181,7 @@ art_profile_java_defaults {
conditions_default: {
dex_preopt: {
app_image: true,
- profile: "art-profile",
+ profile: ":art-profile-combined",
},
},
},
@@ -391,9 +391,14 @@ platform_compat_config {
src: ":services",
}
-filegroup {
- name: "art-profile",
- srcs: ["art-profile"],
+genrule {
+ name: "art-profile-combined",
+ srcs: [
+ "art-profile",
+ "art-profile-extra",
+ ],
+ out: ["art-profile-combined"],
+ cmd: "cat $(location art-profile) $(location art-profile-extra) > $(location art-profile-combined)",
}
// API stub
diff --git a/services/accessibility/accessibility.aconfig b/services/accessibility/accessibility.aconfig
index 35db3c6f0a6d..a133131a1d3f 100644
--- a/services/accessibility/accessibility.aconfig
+++ b/services/accessibility/accessibility.aconfig
@@ -223,6 +223,16 @@ flag {
}
flag {
+ name: "manager_lifecycle_user_change"
+ namespace: "accessibility"
+ description: "Use A11yManagerService's Lifecycle to change users, instead of listening for user changed events."
+ bug: "393626471"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
name: "motion_event_injector_cancel_fix"
namespace: "accessibility"
description: "Fix the ACTION_CANCEL logic used in MotionEventInjector to avoid InputDispatcher inconsistency"
diff --git a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
index 47aa8f5736bf..aae8879e9199 100644
--- a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
@@ -1942,14 +1942,9 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
}
public void notifyGesture(AccessibilityGestureEvent gestureEvent) {
- if (android.view.accessibility.Flags.copyEventsForGestureDetection()) {
- // We will use this event async, so copy it because it contains MotionEvents.
- mInvocationHandler.obtainMessage(InvocationHandler.MSG_ON_GESTURE,
- gestureEvent.copyForAsync()).sendToTarget();
- } else {
- mInvocationHandler.obtainMessage(InvocationHandler.MSG_ON_GESTURE,
- gestureEvent).sendToTarget();
- }
+ // We will use this event async, so copy it because it contains MotionEvents.
+ mInvocationHandler.obtainMessage(InvocationHandler.MSG_ON_GESTURE,
+ gestureEvent.copyForAsync()).sendToTarget();
}
public void notifySystemActionsChangedLocked() {
@@ -2426,9 +2421,7 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
case MSG_ON_GESTURE: {
if (message.obj instanceof AccessibilityGestureEvent gesture) {
notifyGestureInternal(gesture);
- if (android.view.accessibility.Flags.copyEventsForGestureDetection()) {
- gesture.recycle();
- }
+ gesture.recycle();
}
} break;
case MSG_CLEAR_ACCESSIBILITY_CACHE: {
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 703e37fad5ad..39c1fa73b7ce 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -500,6 +500,12 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
mService = new AccessibilityManagerService(context);
}
+ @VisibleForTesting
+ public Lifecycle(Context context, AccessibilityManagerService service) {
+ super(context);
+ mService = service;
+ }
+
@Override
public void onStart() {
LocalServices.addService(AccessibilityManagerInternal.class,
@@ -511,17 +517,19 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
public void onBootPhase(int phase) {
mService.onBootPhase(phase);
}
+
+ @Override
+ public void onUserSwitching(@androidx.annotation.Nullable TargetUser from,
+ @androidx.annotation.NonNull TargetUser to) {
+ super.onUserSwitching(from, to);
+ if (Flags.managerLifecycleUserChange()) {
+ mService.switchUser(to.getUserIdentifier());
+ }
+ }
}
private InputManager.KeyGestureEventHandler mKeyGestureEventHandler =
- new InputManager.KeyGestureEventHandler() {
- @Override
- public boolean handleKeyGestureEvent(
- @NonNull KeyGestureEvent event,
- @Nullable IBinder focusedToken) {
- return AccessibilityManagerService.this.handleKeyGestureEvent(event);
- }
- };
+ (event, focusedToken) -> AccessibilityManagerService.this.handleKeyGestureEvent(event);
@VisibleForTesting
AccessibilityManagerService(
@@ -637,7 +645,11 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
new AccessibilityContentObserver(mMainHandler).register(
mContext.getContentResolver());
if (enableTalkbackAndMagnifierKeyGestures()) {
- mInputManager.registerKeyGestureEventHandler(mKeyGestureEventHandler);
+ List<Integer> supportedGestures = List.of(
+ KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_MAGNIFICATION,
+ KeyGestureEvent.KEY_GESTURE_TYPE_ACTIVATE_SELECT_TO_SPEAK);
+ mInputManager.registerKeyGestureEventHandler(supportedGestures,
+ mKeyGestureEventHandler);
}
if (com.android.settingslib.flags.Flags.hearingDevicesInputRoutingControl()) {
if (mHearingDeviceNotificationController != null) {
@@ -686,13 +698,13 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
}
@VisibleForTesting
- boolean handleKeyGestureEvent(KeyGestureEvent event) {
+ void handleKeyGestureEvent(KeyGestureEvent event) {
final boolean complete =
event.getAction() == KeyGestureEvent.ACTION_GESTURE_COMPLETE
&& !event.isCancelled();
final int gestureType = event.getKeyGestureType();
if (!complete) {
- return false;
+ return;
}
String targetName;
@@ -703,7 +715,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
case KeyGestureEvent.KEY_GESTURE_TYPE_ACTIVATE_SELECT_TO_SPEAK:
targetName = mContext.getString(R.string.config_defaultSelectToSpeakService);
if (targetName.isEmpty()) {
- return false;
+ return;
}
final ComponentName targetServiceComponent = TextUtils.isEmpty(targetName)
@@ -715,7 +727,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
userState.getInstalledServiceInfoLocked(targetServiceComponent);
}
if (accessibilityServiceInfo == null) {
- return false;
+ return;
}
// Skip enabling if a warning dialog is required for the feature.
@@ -725,11 +737,13 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
Slog.w(LOG_TAG,
"Accessibility warning is required before this service can be "
+ "activated automatically via KEY_GESTURE shortcut.");
- return false;
+ return;
}
break;
default:
- return false;
+ Slog.w(LOG_TAG, "Received a key gesture " + event
+ + " that was not registered by this handler");
+ return;
}
List<String> shortcutTargets = getAccessibilityShortcutTargets(
@@ -748,14 +762,12 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
// this will be a separate dialog that appears that requires the user to confirm
// which will resolve this race condition. For now, just require two presses the
// first time it is activated.
- return true;
+ return;
}
final int displayId = event.getDisplayId() != INVALID_DISPLAY
? event.getDisplayId() : getLastNonProxyTopFocusedDisplayId();
performAccessibilityShortcutInternal(displayId, KEY_GESTURE, targetName);
-
- return true;
}
@Override
@@ -1055,7 +1067,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
String action = intent.getAction();
if (Intent.ACTION_USER_SWITCHED.equals(action)) {
- switchUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0));
+ if (!Flags.managerLifecycleUserChange()) {
+ switchUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0));
+ }
} else if (Intent.ACTION_USER_UNLOCKED.equals(action)) {
unlockUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0));
} else if (Intent.ACTION_USER_REMOVED.equals(action)) {
diff --git a/services/accessibility/java/com/android/server/accessibility/HearingDevicePhoneCallNotificationController.java b/services/accessibility/java/com/android/server/accessibility/HearingDevicePhoneCallNotificationController.java
index 94cef418b6c8..8cf94b464a1a 100644
--- a/services/accessibility/java/com/android/server/accessibility/HearingDevicePhoneCallNotificationController.java
+++ b/services/accessibility/java/com/android/server/accessibility/HearingDevicePhoneCallNotificationController.java
@@ -17,6 +17,7 @@
package com.android.server.accessibility;
import android.Manifest;
+import android.annotation.CallbackExecutor;
import android.annotation.NonNull;
import android.annotation.SuppressLint;
import android.app.Notification;
@@ -149,11 +150,7 @@ public class HearingDevicePhoneCallNotificationController {
}
if (state == TelephonyManager.CALL_STATE_IDLE) {
- if (mIsCommDeviceChangedRegistered) {
- mIsCommDeviceChangedRegistered = false;
- mAudioManager.removeOnCommunicationDeviceChangedListener(
- mCommDeviceChangedListener);
- }
+ removeOnCommunicationDeviceChangedListenerIfNeeded(mCommDeviceChangedListener);
dismissNotificationIfNeeded();
if (mHearingDevice != null) {
@@ -163,30 +160,41 @@ public class HearingDevicePhoneCallNotificationController {
mHearingDevice = null;
}
if (state == TelephonyManager.CALL_STATE_OFFHOOK) {
- if (com.android.server.accessibility.Flags.hearingInputChangeWhenCommDevice()) {
- AudioDeviceInfo commDevice = mAudioManager.getCommunicationDevice();
- if (commDevice == null) {
- return;
- }
- mHearingDevice = getSupportedInputHearingDeviceInfo(List.of(commDevice));
- if (mHearingDevice != null) {
- showNotificationIfNeeded();
- } else {
- mAudioManager.addOnCommunicationDeviceChangedListener(
- mCommDeviceChangedExecutor,
- mCommDeviceChangedListener);
- mIsCommDeviceChangedRegistered = true;
- }
+ AudioDeviceInfo commDevice = mAudioManager.getCommunicationDevice();
+ if (commDevice == null) {
+ return;
+ }
+ mHearingDevice = getSupportedInputHearingDeviceInfo(List.of(commDevice));
+ if (mHearingDevice != null) {
+ showNotificationIfNeeded();
} else {
- mHearingDevice = getSupportedInputHearingDeviceInfo(
- mAudioManager.getAvailableCommunicationDevices());
- if (mHearingDevice != null) {
- showNotificationIfNeeded();
- }
+ addOnCommunicationDeviceChangedListenerIfNeeded(mCommDeviceChangedExecutor,
+ mCommDeviceChangedListener);
}
}
}
+ private void addOnCommunicationDeviceChangedListenerIfNeeded(
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull AudioManager.OnCommunicationDeviceChangedListener listener) {
+ if (mIsCommDeviceChangedRegistered) {
+ return;
+ }
+
+ mIsCommDeviceChangedRegistered = true;
+ mAudioManager.addOnCommunicationDeviceChangedListener(executor, listener);
+ }
+
+ private void removeOnCommunicationDeviceChangedListenerIfNeeded(
+ @NonNull AudioManager.OnCommunicationDeviceChangedListener listener) {
+ if (!mIsCommDeviceChangedRegistered) {
+ return;
+ }
+
+ mAudioManager.removeOnCommunicationDeviceChangedListener(listener);
+ mIsCommDeviceChangedRegistered = false;
+ }
+
private void showNotificationIfNeeded() {
if (mIsNotificationShown) {
return;
diff --git a/services/accessibility/java/com/android/server/accessibility/autoclick/AutoclickController.java b/services/accessibility/java/com/android/server/accessibility/autoclick/AutoclickController.java
index 0b9c45de6e40..60343e9e81e5 100644
--- a/services/accessibility/java/com/android/server/accessibility/autoclick/AutoclickController.java
+++ b/services/accessibility/java/com/android/server/accessibility/autoclick/AutoclickController.java
@@ -152,9 +152,20 @@ public class AutoclickController extends BaseEventStreamTransformation {
if (direction == AutoclickScrollPanel.DIRECTION_EXIT) {
return;
}
- // For direction buttons, perform scroll action immediately.
- if (hovered && direction != AutoclickScrollPanel.DIRECTION_NONE) {
- handleScroll(direction);
+
+ // Handle all non-exit buttons when hovered.
+ if (hovered) {
+ // Clear the indicator.
+ if (mAutoclickIndicatorScheduler != null) {
+ mAutoclickIndicatorScheduler.cancel();
+ if (mAutoclickIndicatorView != null) {
+ mAutoclickIndicatorView.clearIndicator();
+ }
+ }
+ // Perform scroll action.
+ if (direction != DIRECTION_NONE) {
+ handleScroll(direction);
+ }
}
}
};
diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/GestureMatcher.java b/services/accessibility/java/com/android/server/accessibility/gestures/GestureMatcher.java
index 3668eefe293d..62b6b85afa58 100644
--- a/services/accessibility/java/com/android/server/accessibility/gestures/GestureMatcher.java
+++ b/services/accessibility/java/com/android/server/accessibility/gestures/GestureMatcher.java
@@ -336,13 +336,8 @@ public abstract class GestureMatcher {
// Recycle the old event first if necessary, to handle duplicate calls to post.
recycleEvent();
mTargetState = state;
- if (android.view.accessibility.Flags.copyEventsForGestureDetection()) {
- mEvent = event.copy();
- mRawEvent = rawEvent.copy();
- } else {
- mEvent = event;
- mRawEvent = rawEvent;
- }
+ mEvent = event.copy();
+ mRawEvent = rawEvent.copy();
mPolicyFlags = policyFlags;
mHandler.postDelayed(this, delay);
if (DEBUG) {
@@ -379,15 +374,13 @@ public abstract class GestureMatcher {
}
private void recycleEvent() {
- if (android.view.accessibility.Flags.copyEventsForGestureDetection()) {
- if (mEvent == null || mRawEvent == null) {
- return;
- }
- mEvent.recycle();
- mRawEvent.recycle();
- mEvent = null;
- mRawEvent = null;
+ if (mEvent == null || mRawEvent == null) {
+ return;
}
+ mEvent.recycle();
+ mRawEvent.recycle();
+ mEvent = null;
+ mRawEvent = null;
}
}
diff --git a/services/art-profile-extra b/services/art-profile-extra
new file mode 100644
index 000000000000..54362411e5ea
--- /dev/null
+++ b/services/art-profile-extra
@@ -0,0 +1 @@
+HSPLcom/android/server/am/ActivityManagerService$LocalService;->checkContentProviderAccess(Ljava/lang/String;I)Ljava/lang/String;
diff --git a/services/core/Android.bp b/services/core/Android.bp
index decac40d20f8..cf85dd957b3f 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -258,7 +258,6 @@ java_library_static {
"dreams_flags_lib",
"aconfig_new_storage_flags_lib",
"powerstats_flags_lib",
- "locksettings_flags_lib",
"MmdProperties",
"mmd_flags_lib",
"profiling_flags_lib",
diff --git a/services/core/java/com/android/server/adb/AdbDebuggingManager.java b/services/core/java/com/android/server/adb/AdbDebuggingManager.java
index 658ea4c27e4c..193d82743a34 100644
--- a/services/core/java/com/android/server/adb/AdbDebuggingManager.java
+++ b/services/core/java/com/android/server/adb/AdbDebuggingManager.java
@@ -168,7 +168,6 @@ public class AdbDebuggingManager {
private AdbConnectionInfo mAdbConnectionInfo = new AdbConnectionInfo();
// Polls for a tls port property when adb wifi is enabled
private AdbConnectionPortPoller mConnectionPortPoller;
- private final PortListenerImpl mPortListener = new PortListenerImpl();
private final Ticker mTicker;
public AdbDebuggingManager(Context context) {
@@ -323,10 +322,6 @@ public class AdbDebuggingManager {
}
}
- interface AdbConnectionPortListener {
- void onPortReceived(int port);
- }
-
/**
* This class will poll for a period of time for adbd to write the port
* it connected to.
@@ -336,16 +331,11 @@ public class AdbDebuggingManager {
* port through different means. A better fix would be to always start AdbDebuggingManager, but
* it needs to adjust accordingly on whether ro.adb.secure is set.
*/
- static class AdbConnectionPortPoller extends Thread {
+ private class AdbConnectionPortPoller extends Thread {
private final String mAdbPortProp = "service.adb.tls.port";
- private AdbConnectionPortListener mListener;
private final int mDurationSecs = 10;
private AtomicBoolean mCanceled = new AtomicBoolean(false);
- AdbConnectionPortPoller(AdbConnectionPortListener listener) {
- mListener = listener;
- }
-
@Override
public void run() {
Slog.d(TAG, "Starting adb port property poller");
@@ -362,13 +352,22 @@ public class AdbDebuggingManager {
// to start the server. Otherwise we should have a valid port.
int port = SystemProperties.getInt(mAdbPortProp, Integer.MAX_VALUE);
if (port == -1 || (port > 0 && port <= 65535)) {
- mListener.onPortReceived(port);
+ onPortReceived(port);
return;
}
SystemClock.sleep(1000);
}
Slog.w(TAG, "Failed to receive adb connection port");
- mListener.onPortReceived(-1);
+ onPortReceived(-1);
+ }
+
+ private void onPortReceived(int port) {
+ Slog.d(TAG, "Received tls port=" + port);
+ Message msg = mHandler.obtainMessage(port > 0
+ ? AdbDebuggingHandler.MSG_SERVER_CONNECTED
+ : AdbDebuggingHandler.MSG_SERVER_DISCONNECTED);
+ msg.obj = port;
+ mHandler.sendMessage(msg);
}
public void cancelAndWait() {
@@ -382,17 +381,6 @@ public class AdbDebuggingManager {
}
}
- class PortListenerImpl implements AdbConnectionPortListener {
- public void onPortReceived(int port) {
- Slog.d(TAG, "Received tls port=" + port);
- Message msg = mHandler.obtainMessage(port > 0
- ? AdbDebuggingHandler.MSG_SERVER_CONNECTED
- : AdbDebuggingHandler.MSG_SERVER_DISCONNECTED);
- msg.obj = port;
- mHandler.sendMessage(msg);
- }
- }
-
@VisibleForTesting
static class AdbDebuggingThread extends Thread {
private boolean mStopped;
@@ -800,7 +788,6 @@ public class AdbDebuggingManager {
// === Messages we can send to adbd ===========
static final String MSG_DISCONNECT_DEVICE = "DD";
- static final String MSG_DISABLE_ADBDWIFI = "DA";
@Nullable @VisibleForTesting AdbKeyStore mAdbKeyStore;
@@ -1088,8 +1075,7 @@ public class AdbDebuggingManager {
mContext.registerReceiver(mBroadcastReceiver, intentFilter);
SystemProperties.set(AdbService.WIFI_PERSISTENT_CONFIG_PROPERTY, "1");
- mConnectionPortPoller =
- new AdbDebuggingManager.AdbConnectionPortPoller(mPortListener);
+ mConnectionPortPoller = new AdbDebuggingManager.AdbConnectionPortPoller();
mConnectionPortPoller.start();
startAdbDebuggingThread();
@@ -1106,9 +1092,6 @@ public class AdbDebuggingManager {
setAdbConnectionInfo(null);
mContext.unregisterReceiver(mBroadcastReceiver);
- if (mThread != null) {
- mThread.sendResponse(MSG_DISABLE_ADBDWIFI);
- }
onAdbdWifiServerDisconnected(-1);
stopAdbDebuggingThread();
break;
@@ -1138,8 +1121,7 @@ public class AdbDebuggingManager {
mContext.registerReceiver(mBroadcastReceiver, intentFilter);
SystemProperties.set(AdbService.WIFI_PERSISTENT_CONFIG_PROPERTY, "1");
- mConnectionPortPoller =
- new AdbDebuggingManager.AdbConnectionPortPoller(mPortListener);
+ mConnectionPortPoller = new AdbDebuggingManager.AdbConnectionPortPoller();
mConnectionPortPoller.start();
startAdbDebuggingThread();
@@ -1257,7 +1239,7 @@ public class AdbDebuggingManager {
if (mAdbWifiEnabled) {
// In scenarios where adbd is restarted, the tls port may change.
mConnectionPortPoller =
- new AdbDebuggingManager.AdbConnectionPortPoller(mPortListener);
+ new AdbDebuggingManager.AdbConnectionPortPoller();
mConnectionPortPoller.start();
}
break;
diff --git a/services/core/java/com/android/server/adb/AdbService.java b/services/core/java/com/android/server/adb/AdbService.java
index 40f7c873eae8..c338a1ef15c9 100644
--- a/services/core/java/com/android/server/adb/AdbService.java
+++ b/services/core/java/com/android/server/adb/AdbService.java
@@ -21,10 +21,8 @@ import android.annotation.NonNull;
import android.annotation.UserIdInt;
import android.content.ContentResolver;
import android.content.Context;
-import android.content.Intent;
import android.content.pm.PackageManager;
import android.database.ContentObserver;
-import android.debug.AdbManager;
import android.debug.AdbManagerInternal;
import android.debug.AdbTransportType;
import android.debug.FingerprintAndPairDevice;
@@ -34,16 +32,15 @@ import android.debug.IAdbTransport;
import android.debug.PairDevice;
import android.hardware.usb.UsbManager;
import android.net.Uri;
+import android.net.wifi.WifiManager;
import android.os.Binder;
import android.os.IBinder;
import android.os.ParcelFileDescriptor;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.os.SystemProperties;
-import android.os.UserHandle;
import android.provider.Settings;
import android.service.adb.AdbServiceDumpProto;
-import android.sysprop.AdbProperties;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Slog;
@@ -63,7 +60,6 @@ import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.Collections;
import java.util.Map;
-import java.util.concurrent.atomic.AtomicInteger;
/**
* The Android Debug Bridge (ADB) service. This controls the availability of ADB and authorization
@@ -85,12 +81,6 @@ public class AdbService extends IAdbManager.Stub {
*/
static final String CTL_STOP = "ctl.stop";
- // The tcp port adb is currently using
- AtomicInteger mConnectionPort = new AtomicInteger(-1);
-
- private final AdbConnectionPortListener mPortListener = new AdbConnectionPortListener();
- private AdbDebuggingManager.AdbConnectionPortPoller mConnectionPortPoller;
-
private final RemoteCallbackList<IAdbCallback> mCallbacks = new RemoteCallbackList<>();
/**
* Manages the service lifecycle for {@code AdbService} in {@code SystemServer}.
@@ -203,6 +193,7 @@ public class AdbService extends IAdbManager.Stub {
@Override
public void onChange(boolean selfChange, @NonNull Uri uri, @UserIdInt int userId) {
+ Slog.d("AdbSettingsObserver", "onChange " + uri.toString());
if (mAdbUsbUri.equals(uri)) {
boolean shouldEnable = (Settings.Global.getInt(mContentResolver,
Settings.Global.ADB_ENABLED, 0) > 0);
@@ -404,39 +395,6 @@ public class AdbService extends IAdbManager.Stub {
Slog.d(TAG, "Unregistering callback " + callback);
mCallbacks.unregister(callback);
}
- /**
- * This listener is only used when ro.adb.secure=0. Otherwise, AdbDebuggingManager will
- * do this.
- */
- class AdbConnectionPortListener implements AdbDebuggingManager.AdbConnectionPortListener {
- public void onPortReceived(int port) {
- if (port > 0 && port <= 65535) {
- mConnectionPort.set(port);
- } else {
- mConnectionPort.set(-1);
- // Turn off wifi debugging, since the server did not start.
- try {
- Settings.Global.putInt(mContentResolver,
- Settings.Global.ADB_WIFI_ENABLED, 0);
- } catch (SecurityException e) {
- // If UserManager.DISALLOW_DEBUGGING_FEATURES is on, that this setting can't
- // be changed.
- Slog.d(TAG, "ADB_ENABLED is restricted.");
- }
- }
- broadcastPortInfo(mConnectionPort.get());
- }
- }
-
- private void broadcastPortInfo(int port) {
- Intent intent = new Intent(AdbManager.WIRELESS_DEBUG_STATE_CHANGED_ACTION);
- intent.putExtra(AdbManager.WIRELESS_STATUS_EXTRA, (port >= 0)
- ? AdbManager.WIRELESS_STATUS_CONNECTED
- : AdbManager.WIRELESS_STATUS_DISCONNECTED);
- intent.putExtra(AdbManager.WIRELESS_DEBUG_PORT_EXTRA, port);
- AdbDebuggingManager.sendBroadcastWithDebugPermission(mContext, intent, UserHandle.ALL);
- Slog.i(TAG, "sent port broadcast port=" + port);
- }
private void startAdbd() {
SystemProperties.set(CTL_START, ADBD);
@@ -461,6 +419,28 @@ public class AdbService extends IAdbManager.Stub {
}
}
+ private WifiManager.MulticastLock mAdbMulticastLock = null;
+
+ private void acquireMulticastLock() {
+ if (mAdbMulticastLock == null) {
+ WifiManager wifiManager = (WifiManager)
+ mContext.getApplicationContext().getSystemService(Context.WIFI_SERVICE);
+ mAdbMulticastLock = wifiManager.createMulticastLock("AdbMulticastLock");
+ }
+
+ if (!mAdbMulticastLock.isHeld()) {
+ mAdbMulticastLock.acquire();
+ Slog.d(TAG, "Acquired multicast lock");
+ }
+ }
+
+ private void releaseMulticastLock() {
+ if (mAdbMulticastLock != null && mAdbMulticastLock.isHeld()) {
+ mAdbMulticastLock.release();
+ Slog.d(TAG, "Released multicast lock");
+ }
+ }
+
private void setAdbEnabled(boolean enable, byte transportType) {
Slog.d(TAG, "setAdbEnabled(" + enable + "), mIsAdbUsbEnabled=" + mIsAdbUsbEnabled
+ ", mIsAdbWifiEnabled=" + mIsAdbWifiEnabled + ", transportType=" + transportType);
@@ -470,20 +450,13 @@ public class AdbService extends IAdbManager.Stub {
} else if (transportType == AdbTransportType.WIFI && enable != mIsAdbWifiEnabled) {
mIsAdbWifiEnabled = enable;
if (mIsAdbWifiEnabled) {
- if (!AdbProperties.secure().orElse(false)) {
- // Start adbd. If this is secure adb, then we defer enabling adb over WiFi.
- SystemProperties.set(WIFI_PERSISTENT_CONFIG_PROPERTY, "1");
- mConnectionPortPoller =
- new AdbDebuggingManager.AdbConnectionPortPoller(mPortListener);
- mConnectionPortPoller.start();
- }
+ // Start adb over WiFi.
+ SystemProperties.set(WIFI_PERSISTENT_CONFIG_PROPERTY, "1");
+ acquireMulticastLock();
} else {
// Stop adb over WiFi.
SystemProperties.set(WIFI_PERSISTENT_CONFIG_PROPERTY, "0");
- if (mConnectionPortPoller != null) {
- mConnectionPortPoller.cancelAndWait();
- mConnectionPortPoller = null;
- }
+ releaseMulticastLock();
}
} else {
// No change
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 5395d2a914ec..c15915ba39a4 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -790,7 +790,7 @@ public final class ActiveServices {
"SHORT_FGS_TIMEOUT");
this.mServiceFGAnrTimer = new ServiceAnrTimer(service,
ActivityManagerService.SERVICE_FOREGROUND_TIMEOUT_MSG,
- "SERVICE_FOREGROUND_TIMEOUT");
+ "SERVICE_FOREGROUND_TIMEOUT", new AnrTimer.Args().extend(true));
}
void systemServicesReady() {
@@ -7702,6 +7702,11 @@ public final class ActiveServices {
super(Objects.requireNonNull(am).mHandler, msg, label);
}
+ ServiceAnrTimer(ActivityManagerService am, int msg, String label,
+ @NonNull AnrTimer.Args args) {
+ super(Objects.requireNonNull(am).mHandler, msg, label, args);
+ }
+
@Override
public int getPid(@NonNull ServiceRecord service) {
return (service.app != null) ? service.app.getPid() : 0;
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index ce5a12179d44..59c5e0e7fc2c 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -137,6 +137,7 @@ import static android.security.Flags.preventIntentRedirectThrowExceptionIfNested
import static android.util.FeatureFlagUtils.SETTINGS_ENABLE_MONITOR_PHANTOM_PROCS;
import static android.view.Display.INVALID_DISPLAY;
+import static com.android.internal.util.FrameworkStatsLog.EXTRA_INTENT_KEYS_COLLECTED_ON_SERVER;
import static com.android.internal.util.FrameworkStatsLog.INTENT_CREATOR_TOKEN_ADDED;
import static com.android.internal.util.FrameworkStatsLog.UNSAFE_INTENT_EVENT_REPORTED__EVENT_TYPE__NEW_MUTABLE_IMPLICIT_PENDING_INTENT_RETRIEVED;
import static com.android.sdksandbox.flags.Flags.sdkSandboxInstrumentationInfo;
@@ -19415,12 +19416,14 @@ public class ActivityManagerService extends IActivityManager.Stub
if (!preventIntentRedirect()) return;
if (intent == null) return;
+ int callingUid = Binder.getCallingUid();
if (((intent.getExtendedFlags() & Intent.EXTENDED_FLAG_NESTED_INTENT_KEYS_COLLECTED) == 0)
&& intent.getExtras() != null && intent.getExtras().hasIntent()) {
Slog.wtf(TAG,
"[IntentRedirect Hardening] The intent does not have its nested keys collected as a "
+ "preparation for creating intent creator tokens. Intent: "
+ intent + "; creatorPackage: " + creatorPackage);
+ FrameworkStatsLog.write(EXTRA_INTENT_KEYS_COLLECTED_ON_SERVER, callingUid);
if (preventIntentRedirectShowToastIfNestedKeysNotCollectedRW()) {
UiThread.getHandler().post(
() -> Toast.makeText(mContext,
@@ -19447,7 +19450,7 @@ public class ActivityManagerService extends IActivityManager.Stub
targetPackage);
final boolean noExtraIntentKeys =
intent.getExtraIntentKeys() == null || intent.getExtraIntentKeys().isEmpty();
- final int creatorUid = noExtraIntentKeys ? DEFAULT_INTENT_CREATOR_UID : Binder.getCallingUid();
+ final int creatorUid = noExtraIntentKeys ? DEFAULT_INTENT_CREATOR_UID : callingUid;
intent.forEachNestedCreatorToken(extraIntent -> {
if (isCreatorSameAsTarget) {
diff --git a/services/core/java/com/android/server/am/AppProfiler.java b/services/core/java/com/android/server/am/AppProfiler.java
index 225c7ca2ca9e..83db027e1b41 100644
--- a/services/core/java/com/android/server/am/AppProfiler.java
+++ b/services/core/java/com/android/server/am/AppProfiler.java
@@ -2264,8 +2264,8 @@ public class AppProfiler {
final int idleTime = mProcessCpuTracker.getLastIdleTime();
bstats.addCpuStatsLocked(totalUTime, totalSTime, userTime,
systemTime, iowaitTime, irqTime, softIrqTime, idleTime);
+ bstats.finishAddingCpuStatsLocked();
}
- bstats.finishAddingCpuStatsLocked();
}
if (mLastWriteTime < (now - BATTERY_STATS_TIME)) {
diff --git a/services/core/java/com/android/server/am/AppStartInfoTracker.java b/services/core/java/com/android/server/am/AppStartInfoTracker.java
index 517279bd7527..8b3eb48fc783 100644
--- a/services/core/java/com/android/server/am/AppStartInfoTracker.java
+++ b/services/core/java/com/android/server/am/AppStartInfoTracker.java
@@ -98,6 +98,9 @@ public final class AppStartInfoTracker {
@VisibleForTesting static final int APP_START_INFO_HISTORY_LIST_SIZE = 16;
+ @VisibleForTesting
+ static final long APP_START_INFO_HISTORY_LENGTH_MS = TimeUnit.DAYS.toMillis(14);
+
/**
* The max number of records that can be present in {@link mInProgressRecords}.
*
@@ -120,9 +123,13 @@ public final class AppStartInfoTracker {
* Monotonic clock which does not reset on reboot.
*
* Time for offset is persisted along with records, see {@link #persistProcessStartInfo}.
- * This does not follow the recommendation of {@link MonotonicClock} to persist on shutdown as
- * it's ok in this case to lose any time change past the last persist as records added since
- * then will be lost as well and the purpose of this clock is to keep records in order.
+ * This does not currently follow the recommendation of {@link MonotonicClock} to persist on
+ * shutdown as it's ok in this case to lose any time change past the last persist as records
+ * added since then will be lost as well. Since this time is used for cleanup as well, the
+ * potential old offset may result in the cleanup window being extended slightly beyond the
+ * targeted 14 days.
+ *
+ * TODO: b/402794215 - Persist on shutdown once persist performance is sufficiently improved.
*/
@VisibleForTesting MonotonicClock mMonotonicClock = null;
@@ -296,7 +303,7 @@ public final class AppStartInfoTracker {
if (!mEnabled) {
return;
}
- ApplicationStartInfo start = new ApplicationStartInfo(getMonotonicTime());
+ ApplicationStartInfo start = new ApplicationStartInfo(getMonotonicTimeMs());
start.setStartupState(ApplicationStartInfo.STARTUP_STATE_STARTED);
start.setIntent(intent);
start.setStartType(ApplicationStartInfo.START_TYPE_UNSET);
@@ -454,7 +461,7 @@ public final class AppStartInfoTracker {
if (!mEnabled) {
return;
}
- ApplicationStartInfo start = new ApplicationStartInfo(getMonotonicTime());
+ ApplicationStartInfo start = new ApplicationStartInfo(getMonotonicTimeMs());
addBaseFieldsFromProcessRecord(start, app);
start.setStartupState(ApplicationStartInfo.STARTUP_STATE_STARTED);
start.addStartupTimestamp(
@@ -484,7 +491,7 @@ public final class AppStartInfoTracker {
if (!mEnabled) {
return;
}
- ApplicationStartInfo start = new ApplicationStartInfo(getMonotonicTime());
+ ApplicationStartInfo start = new ApplicationStartInfo(getMonotonicTimeMs());
addBaseFieldsFromProcessRecord(start, app);
start.setStartupState(ApplicationStartInfo.STARTUP_STATE_STARTED);
start.addStartupTimestamp(
@@ -511,7 +518,7 @@ public final class AppStartInfoTracker {
if (!mEnabled) {
return;
}
- ApplicationStartInfo start = new ApplicationStartInfo(getMonotonicTime());
+ ApplicationStartInfo start = new ApplicationStartInfo(getMonotonicTimeMs());
addBaseFieldsFromProcessRecord(start, app);
start.setStartupState(ApplicationStartInfo.STARTUP_STATE_STARTED);
start.addStartupTimestamp(
@@ -533,7 +540,7 @@ public final class AppStartInfoTracker {
if (!mEnabled) {
return;
}
- ApplicationStartInfo start = new ApplicationStartInfo(getMonotonicTime());
+ ApplicationStartInfo start = new ApplicationStartInfo(getMonotonicTimeMs());
addBaseFieldsFromProcessRecord(start, app);
start.setStartupState(ApplicationStartInfo.STARTUP_STATE_STARTED);
start.addStartupTimestamp(
@@ -721,8 +728,8 @@ public final class AppStartInfoTracker {
Collections.sort(
list, (a, b) ->
- Long.compare(b.getMonoticCreationTimeMs(),
- a.getMonoticCreationTimeMs()));
+ Long.compare(b.getMonotonicCreationTimeMs(),
+ a.getMonotonicCreationTimeMs()));
int size = list.size();
if (maxNum > 0) {
size = Math.min(size, maxNum);
@@ -1098,7 +1105,7 @@ public final class AppStartInfoTracker {
mLastAppStartInfoPersistTimestamp = now;
}
}
- proto.write(AppsStartInfoProto.MONOTONIC_TIME, getMonotonicTime());
+ proto.write(AppsStartInfoProto.MONOTONIC_TIME, getMonotonicTimeMs());
if (succeeded) {
proto.flush();
af.finishWrite(out);
@@ -1219,7 +1226,11 @@ public final class AppStartInfoTracker {
}
}
- private long getMonotonicTime() {
+ /**
+ * Monotonic time that doesn't change with reboot or device time change for ordering records.
+ */
+ @VisibleForTesting
+ public long getMonotonicTimeMs() {
if (mMonotonicClock == null) {
// This should never happen. Return 0 to not interfere with past or future records.
return 0;
@@ -1229,7 +1240,7 @@ public final class AppStartInfoTracker {
/** A container class of (@link android.app.ApplicationStartInfo) */
final class AppStartInfoContainer {
- private ArrayList<ApplicationStartInfo> mInfos; // Always kept sorted by first timestamp.
+ private ArrayList<ApplicationStartInfo> mInfos; // Always kept sorted by monotonic time.
private int mMaxCapacity;
private int mUid;
private boolean mMonitoringModeEnabled = false;
@@ -1260,9 +1271,12 @@ public final class AppStartInfoTracker {
return;
}
- // Sort records so we can remove the least recent ones.
- Collections.sort(mInfos, (a, b) ->
- Long.compare(b.getMonoticCreationTimeMs(), a.getMonoticCreationTimeMs()));
+ if (!android.app.Flags.appStartInfoKeepRecordsSorted()) {
+ // Sort records so we can remove the least recent ones.
+ Collections.sort(mInfos, (a, b) ->
+ Long.compare(b.getMonotonicCreationTimeMs(),
+ a.getMonotonicCreationTimeMs()));
+ }
// Remove records and trim list object back to size.
mInfos.subList(0, mInfos.size() - getMaxCapacity()).clear();
@@ -1277,25 +1291,34 @@ public final class AppStartInfoTracker {
@GuardedBy("mLock")
void addStartInfoLocked(ApplicationStartInfo info) {
- int size = mInfos.size();
- if (size >= getMaxCapacity()) {
- // Remove oldest record if size is over max capacity.
- int oldestIndex = -1;
- long oldestTimeStamp = Long.MAX_VALUE;
- for (int i = 0; i < size; i++) {
- ApplicationStartInfo startInfo = mInfos.get(i);
- if (startInfo.getMonoticCreationTimeMs() < oldestTimeStamp) {
- oldestTimeStamp = startInfo.getMonoticCreationTimeMs();
- oldestIndex = i;
- }
+ if (android.app.Flags.appStartInfoKeepRecordsSorted()) {
+ while (mInfos.size() >= getMaxCapacity()) {
+ // Expected to execute at most once.
+ mInfos.removeLast();
}
- if (oldestIndex >= 0) {
- mInfos.remove(oldestIndex);
+ mInfos.addFirst(info);
+ } else {
+ int size = mInfos.size();
+ if (size >= getMaxCapacity()) {
+ // Remove oldest record if size is over max capacity.
+ int oldestIndex = -1;
+ long oldestTimeStamp = Long.MAX_VALUE;
+ for (int i = 0; i < size; i++) {
+ ApplicationStartInfo startInfo = mInfos.get(i);
+ if (startInfo.getMonotonicCreationTimeMs() < oldestTimeStamp) {
+ oldestTimeStamp = startInfo.getMonotonicCreationTimeMs();
+ oldestIndex = i;
+ }
+ }
+ if (oldestIndex >= 0) {
+ mInfos.remove(oldestIndex);
+ }
}
+ mInfos.add(info);
+ Collections.sort(mInfos, (a, b) ->
+ Long.compare(b.getMonotonicCreationTimeMs(),
+ a.getMonotonicCreationTimeMs()));
}
- mInfos.add(info);
- Collections.sort(mInfos, (a, b) ->
- Long.compare(b.getMonoticCreationTimeMs(), a.getMonoticCreationTimeMs()));
}
/**
@@ -1439,9 +1462,25 @@ public final class AppStartInfoTracker {
long token = proto.start(fieldId);
proto.write(AppsStartInfoProto.Package.User.UID, mUid);
int size = mInfos.size();
- for (int i = 0; i < size; i++) {
- mInfos.get(i).writeToProto(proto, AppsStartInfoProto.Package.User.APP_START_INFO,
- byteArrayOutputStream, objectOutputStream, typedXmlSerializer);
+ if (android.app.Flags.appStartInfoCleanupOldRecords()) {
+ long removeOlderThan = getMonotonicTimeMs() - APP_START_INFO_HISTORY_LENGTH_MS;
+ // Iterate backwards so we can remove old records as we go.
+ for (int i = size - 1; i >= 0; i--) {
+ if (mInfos.get(i).getMonotonicCreationTimeMs() < removeOlderThan) {
+ // Remove the record.
+ mInfos.remove(i);
+ } else {
+ mInfos.get(i).writeToProto(
+ proto, AppsStartInfoProto.Package.User.APP_START_INFO,
+ byteArrayOutputStream, objectOutputStream, typedXmlSerializer);
+ }
+ }
+ } else {
+ for (int i = 0; i < size; i++) {
+ mInfos.get(i).writeToProto(
+ proto, AppsStartInfoProto.Package.User.APP_START_INFO,
+ byteArrayOutputStream, objectOutputStream, typedXmlSerializer);
+ }
}
proto.write(AppsStartInfoProto.Package.User.MONITORING_ENABLED, mMonitoringModeEnabled);
proto.end(token);
@@ -1466,7 +1505,13 @@ public final class AppStartInfoTracker {
info.readFromProto(proto, AppsStartInfoProto.Package.User.APP_START_INFO,
byteArrayInputStream, objectInputStream, typedXmlPullParser);
info.setPackageName(packageName);
- mInfos.add(info);
+ if (android.app.Flags.appStartInfoKeepRecordsSorted()) {
+ // Since the writes are done from oldest to newest, each additional
+ // record will be newer than the previous so use addFirst.
+ mInfos.addFirst(info);
+ } else {
+ mInfos.add(info);
+ }
break;
case (int) AppsStartInfoProto.Package.User.MONITORING_ENABLED:
mMonitoringModeEnabled = proto.readBoolean(
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index 5ff6999e40b3..6b4a99cc4fae 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -211,7 +211,6 @@ public final class BatteryStatsService extends IBatteryStats.Stub
private static final int POWER_STATS_QUERY_TIMEOUT_MILLIS = 2000;
private static final String DEVICE_CONFIG_NAMESPACE = "backstage_power";
private static final String MIN_CONSUMED_POWER_THRESHOLD_KEY = "min_consumed_power_threshold";
- private static final String EMPTY = "Empty";
private final HandlerThread mHandlerThread;
private final Handler mHandler;
@@ -336,55 +335,6 @@ public final class BatteryStatsService extends IBatteryStats.Stub
}
}
- @Override
- public String getSubsystemLowPowerStats() {
- synchronized (mPowerStatsLock) {
- if (mPowerStatsInternal == null || mEntityNames.isEmpty() || mStateNames.isEmpty()) {
- return EMPTY;
- }
- }
-
- final StateResidencyResult[] results;
- try {
- results = mPowerStatsInternal.getStateResidencyAsync(new int[0])
- .get(POWER_STATS_QUERY_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
- } catch (Exception e) {
- Slog.e(TAG, "Failed to getStateResidencyAsync", e);
- return EMPTY;
- }
-
- if (results == null || results.length == 0) return EMPTY;
-
- int charsLeft = MAX_LOW_POWER_STATS_SIZE;
- StringBuilder builder = new StringBuilder("SubsystemPowerState");
- for (int i = 0; i < results.length; i++) {
- final StateResidencyResult result = results[i];
- StringBuilder subsystemBuilder = new StringBuilder();
- subsystemBuilder.append(" subsystem_" + i);
- subsystemBuilder.append(" name=" + mEntityNames.get(result.id));
-
- for (int j = 0; j < result.stateResidencyData.length; j++) {
- final StateResidency stateResidency = result.stateResidencyData[j];
- subsystemBuilder.append(" state_" + j);
- subsystemBuilder.append(" name=" + mStateNames.get(result.id).get(
- stateResidency.id));
- subsystemBuilder.append(" time=" + stateResidency.totalTimeInStateMs);
- subsystemBuilder.append(" count=" + stateResidency.totalStateEntryCount);
- subsystemBuilder.append(" last entry=" + stateResidency.lastEntryTimestampMs);
- }
-
- if (subsystemBuilder.length() <= charsLeft) {
- charsLeft -= subsystemBuilder.length();
- builder.append(subsystemBuilder);
- } else {
- Slog.e(TAG, "getSubsystemLowPowerStats: buffer not enough");
- break;
- }
- }
-
- return builder.toString();
- }
-
private ConnectivityManager.NetworkCallback mNetworkCallback =
new ConnectivityManager.NetworkCallback() {
@Override
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index fa35da30bf4b..115ae41d12ff 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -2643,7 +2643,7 @@ public class OomAdjuster {
}
capability |= getDefaultCapability(app, procState);
- capability |= getCpuCapability(app, now);
+ capability |= getCpuCapability(app, now, foregroundActivities);
// Procstates below BFGS should never have this capability.
if (procState > PROCESS_STATE_BOUND_FOREGROUND_SERVICE) {
@@ -3422,15 +3422,18 @@ public class OomAdjuster {
return baseCapabilities | networkCapabilities;
}
- private static int getCpuCapability(ProcessRecord app, long nowUptime) {
+ private static int getCpuCapability(ProcessRecord app, long nowUptime,
+ boolean hasForegroundActivities) {
// Note: persistent processes get all capabilities, including CPU_TIME.
final UidRecord uidRec = app.getUidRecord();
if (uidRec != null && uidRec.isCurAllowListed()) {
// Process is in the power allowlist.
return PROCESS_CAPABILITY_CPU_TIME;
}
- if (app.mState.getCachedHasVisibleActivities()) {
- // Process has user visible activities.
+ if (hasForegroundActivities) {
+ // TODO: b/402987519 - This grants the Top Sleeping process CPU_TIME but eventually
+ // should not.
+ // Process has user perceptible activities.
return PROCESS_CAPABILITY_CPU_TIME;
}
if (Flags.prototypeAggressiveFreezing()) {
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index a61368c4bc36..ad47e67b9332 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -793,6 +793,7 @@ public final class ProcessList {
final ProcessMap<ProcessRecord> mDyingProcesses = new ProcessMap<>();
// Self locked with the inner lock within the RemoteCallbackList
+ @GuardedBy("mProcessObservers")
private final RemoteCallbackList<IProcessObserver> mProcessObservers =
new RemoteCallbackList<>();
@@ -4980,11 +4981,15 @@ public final class ProcessList {
}
void registerProcessObserver(IProcessObserver observer) {
- mProcessObservers.register(observer);
+ synchronized (mProcessObservers) {
+ mProcessObservers.register(observer);
+ }
}
void unregisterProcessObserver(IProcessObserver observer) {
- mProcessObservers.unregister(observer);
+ synchronized (mProcessObservers) {
+ mProcessObservers.unregister(observer);
+ }
}
void dispatchProcessesChanged() {
@@ -5002,38 +5007,41 @@ public final class ProcessList {
}
}
- int i = mProcessObservers.beginBroadcast();
- while (i > 0) {
- i--;
- final IProcessObserver observer = mProcessObservers.getBroadcastItem(i);
- if (observer != null) {
- try {
- for (int j = 0; j < numOfChanges; j++) {
- ProcessChangeItem item = mActiveProcessChanges[j];
- if ((item.changes & ProcessChangeItem.CHANGE_ACTIVITIES) != 0) {
- if (DEBUG_PROCESS_OBSERVERS) {
- Slog.i(TAG_PROCESS_OBSERVERS,
- "ACTIVITIES CHANGED pid=" + item.pid + " uid="
- + item.uid + ": " + item.foregroundActivities);
+ synchronized (mProcessObservers) {
+ int i = mProcessObservers.beginBroadcast();
+ while (i > 0) {
+ i--;
+ final IProcessObserver observer = mProcessObservers.getBroadcastItem(i);
+ if (observer != null) {
+ try {
+ for (int j = 0; j < numOfChanges; j++) {
+ ProcessChangeItem item = mActiveProcessChanges[j];
+ if ((item.changes & ProcessChangeItem.CHANGE_ACTIVITIES) != 0) {
+ if (DEBUG_PROCESS_OBSERVERS) {
+ Slog.i(TAG_PROCESS_OBSERVERS,
+ "ACTIVITIES CHANGED pid=" + item.pid + " uid="
+ + item.uid + ": " + item.foregroundActivities);
+ }
+ observer.onForegroundActivitiesChanged(item.pid, item.uid,
+ item.foregroundActivities);
}
- observer.onForegroundActivitiesChanged(item.pid, item.uid,
- item.foregroundActivities);
- }
- if ((item.changes & ProcessChangeItem.CHANGE_FOREGROUND_SERVICES) != 0) {
- if (DEBUG_PROCESS_OBSERVERS) {
- Slog.i(TAG_PROCESS_OBSERVERS,
- "FOREGROUND SERVICES CHANGED pid=" + item.pid + " uid="
- + item.uid + ": " + item.foregroundServiceTypes);
+ if ((item.changes & ProcessChangeItem.CHANGE_FOREGROUND_SERVICES)
+ != 0) {
+ if (DEBUG_PROCESS_OBSERVERS) {
+ Slog.i(TAG_PROCESS_OBSERVERS,
+ "FOREGROUND SERVICES CHANGED pid=" + item.pid + " uid="
+ + item.uid + ": " + item.foregroundServiceTypes);
+ }
+ observer.onForegroundServicesChanged(item.pid, item.uid,
+ item.foregroundServiceTypes);
}
- observer.onForegroundServicesChanged(item.pid, item.uid,
- item.foregroundServiceTypes);
}
+ } catch (RemoteException e) {
}
- } catch (RemoteException e) {
}
}
+ mProcessObservers.finishBroadcast();
}
- mProcessObservers.finishBroadcast();
synchronized (mProcessChangeLock) {
for (int j = 0; j < numOfChanges; j++) {
@@ -5122,22 +5130,42 @@ public final class ProcessList {
}
void dispatchProcessStarted(ProcessRecord app, int pid) {
- // TODO(b/323959187) Add the implementation.
+ if (!android.app.Flags.enableProcessObserverBroadcastOnProcessStarted()) {
+ Slog.i(TAG, "ProcessObserver broadcast disabled");
+ return;
+ }
+ synchronized (mProcessObservers) {
+ int i = mProcessObservers.beginBroadcast();
+ while (i > 0) {
+ i--;
+ final IProcessObserver observer = mProcessObservers.getBroadcastItem(i);
+ if (observer != null) {
+ try {
+ observer.onProcessStarted(pid, app.uid, app.info.uid,
+ app.info.packageName, app.processName);
+ } catch (RemoteException e) {
+ }
+ }
+ }
+ mProcessObservers.finishBroadcast();
+ }
}
void dispatchProcessDied(int pid, int uid) {
- int i = mProcessObservers.beginBroadcast();
- while (i > 0) {
- i--;
- final IProcessObserver observer = mProcessObservers.getBroadcastItem(i);
- if (observer != null) {
- try {
- observer.onProcessDied(pid, uid);
- } catch (RemoteException e) {
+ synchronized (mProcessObservers) {
+ int i = mProcessObservers.beginBroadcast();
+ while (i > 0) {
+ i--;
+ final IProcessObserver observer = mProcessObservers.getBroadcastItem(i);
+ if (observer != null) {
+ try {
+ observer.onProcessDied(pid, uid);
+ } catch (RemoteException e) {
+ }
}
}
+ mProcessObservers.finishBroadcast();
}
- mProcessObservers.finishBroadcast();
}
@GuardedBy(anyOf = {"mService", "mProcLock"})
diff --git a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
index cb4342f27bc8..4eadab27aa26 100644
--- a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
+++ b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
@@ -256,6 +256,7 @@ public class SettingsToPropertiesMapper {
"pixel_vpn",
"pixel_watch",
"pixel_watch_debug_trace",
+ "pixel_watch_watchfaces",
"pixel_wifi",
"platform_compat",
"platform_security",
@@ -316,8 +317,6 @@ public class SettingsToPropertiesMapper {
private final String[] mDeviceConfigScopes;
- private final String[] mDeviceConfigAconfigScopes;
-
private final ContentResolver mContentResolver;
@VisibleForTesting
@@ -328,7 +327,6 @@ public class SettingsToPropertiesMapper {
mContentResolver = contentResolver;
mGlobalSettings = globalSettings;
mDeviceConfigScopes = deviceConfigScopes;
- mDeviceConfigAconfigScopes = deviceConfigAconfigScopes;
}
@VisibleForTesting
@@ -374,36 +372,6 @@ public class SettingsToPropertiesMapper {
return;
}
setProperty(propertyName, properties.getString(key, null));
-
- // for legacy namespaces, they can also be used for trunk stable
- // purposes. so push flag also into trunk stable slot in sys prop,
- // later all legacy usage will be refactored and the sync to old
- // sys prop slot can be removed.
- String aconfigPropertyName = makeAconfigFlagPropertyName(scope, key);
- if (aconfigPropertyName == null) {
- logErr("unable to construct system property for " + scope + "/"
- + key);
- return;
- }
- setProperty(aconfigPropertyName, properties.getString(key, null));
- }
- });
- }
-
- for (String deviceConfigAconfigScope : mDeviceConfigAconfigScopes) {
- DeviceConfig.addOnPropertiesChangedListener(
- deviceConfigAconfigScope,
- AsyncTask.THREAD_POOL_EXECUTOR,
- (DeviceConfig.Properties properties) -> {
- String scope = properties.getNamespace();
- for (String key : properties.getKeyset()) {
- String aconfigPropertyName = makeAconfigFlagPropertyName(scope, key);
- if (aconfigPropertyName == null) {
- logErr("unable to construct system property for " + scope + "/"
- + key);
- return;
- }
- setProperty(aconfigPropertyName, properties.getString(key, null));
}
});
}
@@ -419,34 +387,6 @@ public class SettingsToPropertiesMapper {
stageFlagsInNewStorage(properties);
return;
}
-
- for (String flagName : properties.getKeyset()) {
- String flagValue = properties.getString(flagName, null);
- if (flagName == null || flagValue == null) {
- continue;
- }
-
- int idx = flagName.indexOf(NAMESPACE_REBOOT_STAGING_DELIMITER);
- if (idx == -1 || idx == flagName.length() - 1 || idx == 0) {
- logErr("invalid staged flag: " + flagName);
- continue;
- }
-
- String actualNamespace = flagName.substring(0, idx);
- String actualFlagName = flagName.substring(idx+1);
- String propertyName = "next_boot." + makeAconfigFlagPropertyName(
- actualNamespace, actualFlagName);
-
- if (Flags.supportLocalOverridesSysprops()) {
- // Don't propagate if there is a local override.
- String overrideName = actualNamespace + ":" + actualFlagName;
- if (DeviceConfig.getProperty(NAMESPACE_LOCAL_OVERRIDES, overrideName) != null) {
- continue;
- }
- }
- setProperty(propertyName, flagValue);
- }
-
});
// add prop sync callback for flag local overrides
@@ -458,42 +398,6 @@ public class SettingsToPropertiesMapper {
setLocalOverridesInNewStorage(properties);
return;
}
-
- if (Flags.supportLocalOverridesSysprops()) {
- String overridesNamespace = properties.getNamespace();
- for (String key : properties.getKeyset()) {
- String realNamespace = key.split(":")[0];
- String realFlagName = key.split(":")[1];
- String aconfigPropertyName =
- makeAconfigFlagPropertyName(realNamespace, realFlagName);
- if (aconfigPropertyName == null) {
- logErr("unable to construct system property for " + realNamespace + "/"
- + key);
- return;
- }
-
- if (properties.getString(key, null) == null) {
- String deviceConfigValue =
- DeviceConfig.getProperty(realNamespace, realFlagName);
- String stagedDeviceConfigValue =
- DeviceConfig.getProperty(NAMESPACE_REBOOT_STAGING,
- realNamespace + "*" + realFlagName);
-
- setProperty(aconfigPropertyName, deviceConfigValue);
- if (stagedDeviceConfigValue == null) {
- setProperty("next_boot." + aconfigPropertyName, deviceConfigValue);
- } else {
- setProperty("next_boot." + aconfigPropertyName, stagedDeviceConfigValue);
- }
- } else {
- // Otherwise, propagate the override to sysprops.
- setProperty(aconfigPropertyName, properties.getString(key, null));
- // If there's a staged value, make sure it's the override value.
- setProperty("next_boot." + aconfigPropertyName,
- properties.getString(key, null));
- }
- }
- }
});
}
@@ -821,28 +725,6 @@ public class SettingsToPropertiesMapper {
sendAconfigdRequests(requests);
}
- /**
- * system property name constructing rule for aconfig flags:
- * "persist.device_config.aconfig_flags.[category_name].[flag_name]".
- * If the name contains invalid characters or substrings for system property name,
- * will return null.
- * @param categoryName
- * @param flagName
- * @return
- */
- @VisibleForTesting
- static String makeAconfigFlagPropertyName(String categoryName, String flagName) {
- String propertyName = SYSTEM_PROPERTY_PREFIX + "aconfig_flags." +
- categoryName + "." + flagName;
-
- if (!propertyName.matches(SYSTEM_PROPERTY_VALID_CHARACTERS_REGEX)
- || propertyName.contains(SYSTEM_PROPERTY_INVALID_SUBSTRING)) {
- return null;
- }
-
- return propertyName;
- }
-
private void setProperty(String key, String value) {
// Check if need to clear the property
if (value == null) {
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index 5ecac2253b49..2e229ca9d10f 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -55,7 +55,6 @@ import static android.app.AppOpsManager.SAMPLING_STRATEGY_RARELY_USED;
import static android.app.AppOpsManager.SAMPLING_STRATEGY_UNIFORM;
import static android.app.AppOpsManager.SAMPLING_STRATEGY_UNIFORM_OPS;
import static android.app.AppOpsManager.SECURITY_EXCEPTION_ON_INVALID_ATTRIBUTION_TAG_CHANGE;
-import static android.app.AppOpsManager.UID_STATE_NONEXISTENT;
import static android.app.AppOpsManager.WATCH_FOREGROUND_CHANGES;
import static android.app.AppOpsManager._NUM_OP;
import static android.app.AppOpsManager.extractFlagsFromKey;
@@ -464,7 +463,19 @@ public class AppOpsService extends IAppOpsService.Stub {
Clock.SYSTEM_CLOCK, mConstants);
mUidStateTracker.addUidStateChangedCallback(new HandlerExecutor(mHandler),
- this::onUidStateChanged);
+ new AppOpsUidStateTracker.UidStateChangedCallback() {
+ @Override
+ public void onUidStateChanged(int uid, int uidState,
+ boolean foregroundModeMayChange) {
+ AppOpsService.this
+ .onUidStateChanged(uid, uidState, foregroundModeMayChange);
+ }
+
+ @Override
+ public void onUidProcessDeath(int uid) {
+ AppOpsService.this.onUidProcessDeath(uid);
+ }
+ });
}
return mUidStateTracker;
}
@@ -1500,9 +1511,6 @@ public class AppOpsService extends IAppOpsService.Stub {
// The callback method from AppOpsUidStateTracker
private void onUidStateChanged(int uid, int state, boolean foregroundModeMayChange) {
synchronized (this) {
- if (state == UID_STATE_NONEXISTENT) {
- onUidProcessDeathLocked(uid);
- }
UidState uidState = getUidStateLocked(uid, false);
boolean hasForegroundWatchers = false;
@@ -1590,11 +1598,6 @@ public class AppOpsService extends IAppOpsService.Stub {
}
}
- if (state == UID_STATE_NONEXISTENT) {
- // For UID_STATE_NONEXISTENT, we don't call onUidStateChanged for AttributedOps
- return;
- }
-
if (uidState != null) {
int numPkgs = uidState.pkgOps.size();
for (int pkgNum = 0; pkgNum < numPkgs; pkgNum++) {
@@ -1619,31 +1622,32 @@ public class AppOpsService extends IAppOpsService.Stub {
}
}
- @GuardedBy("this")
- private void onUidProcessDeathLocked(int uid) {
- if (!mUidStates.contains(uid) || !Flags.finishRunningOpsForKilledPackages()) {
- return;
- }
- final SparseLongArray chainsToFinish = new SparseLongArray();
- doForAllAttributedOpsInUidLocked(uid, (attributedOp) -> {
- attributedOp.doForAllInProgressStartOpEvents((event) -> {
- if (event == null) {
- return;
- }
- int chainId = event.getAttributionChainId();
- if (chainId != ATTRIBUTION_CHAIN_ID_NONE) {
- long currentEarliestStartTime =
- chainsToFinish.get(chainId, Long.MAX_VALUE);
- if (event.getStartTime() < currentEarliestStartTime) {
- // Store the earliest chain link we're finishing, so that we can go back
- // and finish any links in the chain that started after this one
- chainsToFinish.put(chainId, event.getStartTime());
+ private void onUidProcessDeath(int uid) {
+ synchronized (this) {
+ if (!mUidStates.contains(uid) || !Flags.finishRunningOpsForKilledPackages()) {
+ return;
+ }
+ final SparseLongArray chainsToFinish = new SparseLongArray();
+ doForAllAttributedOpsInUidLocked(uid, (attributedOp) -> {
+ attributedOp.doForAllInProgressStartOpEvents((event) -> {
+ if (event == null) {
+ return;
}
- }
- attributedOp.finished(event.getClientId());
+ int chainId = event.getAttributionChainId();
+ if (chainId != ATTRIBUTION_CHAIN_ID_NONE) {
+ long currentEarliestStartTime =
+ chainsToFinish.get(chainId, Long.MAX_VALUE);
+ if (event.getStartTime() < currentEarliestStartTime) {
+ // Store the earliest chain link we're finishing, so that we can go back
+ // and finish any links in the chain that started after this one
+ chainsToFinish.put(chainId, event.getStartTime());
+ }
+ }
+ attributedOp.finished(event.getClientId());
+ });
});
- });
- finishChainsLocked(chainsToFinish);
+ finishChainsLocked(chainsToFinish);
+ }
}
@GuardedBy("this")
diff --git a/services/core/java/com/android/server/appop/AppOpsUidStateTracker.java b/services/core/java/com/android/server/appop/AppOpsUidStateTracker.java
index 268b286d8fe1..9bd72990f7b7 100644
--- a/services/core/java/com/android/server/appop/AppOpsUidStateTracker.java
+++ b/services/core/java/com/android/server/appop/AppOpsUidStateTracker.java
@@ -19,6 +19,7 @@ package com.android.server.appop;
import static android.app.ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE;
import static android.app.ActivityManager.PROCESS_STATE_BOUND_TOP;
import static android.app.ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE;
+import static android.app.ActivityManager.PROCESS_STATE_NONEXISTENT;
import static android.app.ActivityManager.PROCESS_STATE_PERSISTENT_UI;
import static android.app.ActivityManager.PROCESS_STATE_RECEIVER;
import static android.app.ActivityManager.PROCESS_STATE_TOP;
@@ -27,8 +28,10 @@ import static android.app.AppOpsManager.UID_STATE_BACKGROUND;
import static android.app.AppOpsManager.UID_STATE_CACHED;
import static android.app.AppOpsManager.UID_STATE_FOREGROUND;
import static android.app.AppOpsManager.UID_STATE_FOREGROUND_SERVICE;
+import static android.app.AppOpsManager.UID_STATE_NONEXISTENT;
import static android.app.AppOpsManager.UID_STATE_PERSISTENT;
import static android.app.AppOpsManager.UID_STATE_TOP;
+import static android.permission.flags.Flags.finishRunningOpsForKilledPackages;
import android.annotation.CallbackExecutor;
import android.util.SparseArray;
@@ -68,6 +71,14 @@ interface AppOpsUidStateTracker {
return UID_STATE_BACKGROUND;
}
+ if (finishRunningOpsForKilledPackages()) {
+ if (procState < PROCESS_STATE_NONEXISTENT) {
+ return UID_STATE_CACHED;
+ }
+
+ return UID_STATE_NONEXISTENT;
+ }
+
// UID_STATE_NONEXISTENT is deliberately excluded here
return UID_STATE_CACHED;
}
@@ -119,6 +130,8 @@ interface AppOpsUidStateTracker {
* evaluated result may have changed.
*/
void onUidStateChanged(int uid, int uidState, boolean foregroundModeMayChange);
+
+ void onUidProcessDeath(int uid);
}
void dumpUidState(PrintWriter pw, int uid, long nowElapsed);
diff --git a/services/core/java/com/android/server/appop/AppOpsUidStateTrackerImpl.java b/services/core/java/com/android/server/appop/AppOpsUidStateTrackerImpl.java
index 6f8c241a86ae..1a1077ad0e7b 100644
--- a/services/core/java/com/android/server/appop/AppOpsUidStateTrackerImpl.java
+++ b/services/core/java/com/android/server/appop/AppOpsUidStateTrackerImpl.java
@@ -21,7 +21,6 @@ import static android.app.ActivityManager.PROCESS_CAPABILITY_FOREGROUND_CAMERA;
import static android.app.ActivityManager.PROCESS_CAPABILITY_FOREGROUND_LOCATION;
import static android.app.ActivityManager.PROCESS_CAPABILITY_FOREGROUND_MICROPHONE;
import static android.app.ActivityManager.PROCESS_CAPABILITY_NONE;
-import static android.app.ActivityManager.PROCESS_STATE_NONEXISTENT;
import static android.app.ActivityManager.ProcessCapability;
import static android.app.AppOpsManager.MIN_PRIORITY_UID_STATE;
import static android.app.AppOpsManager.MODE_ALLOWED;
@@ -32,6 +31,7 @@ import static android.app.AppOpsManager.OP_CONTROL_AUDIO;
import static android.app.AppOpsManager.OP_NONE;
import static android.app.AppOpsManager.OP_RECEIVE_EXPLICIT_USER_INTERACTION_AUDIO;
import static android.app.AppOpsManager.OP_RECORD_AUDIO;
+import static android.app.AppOpsManager.UID_STATE_CACHED;
import static android.app.AppOpsManager.UID_STATE_FOREGROUND_SERVICE;
import static android.app.AppOpsManager.UID_STATE_MAX_LAST_NON_RESTRICTED;
import static android.app.AppOpsManager.UID_STATE_NONEXISTENT;
@@ -75,7 +75,6 @@ class AppOpsUidStateTrackerImpl implements AppOpsUidStateTracker {
private SparseBooleanArray mAppWidgetVisible = new SparseBooleanArray();
private SparseBooleanArray mPendingAppWidgetVisible = new SparseBooleanArray();
private SparseLongArray mPendingCommitTime = new SparseLongArray();
- private SparseBooleanArray mPendingGone = new SparseBooleanArray();
private ArrayMap<UidStateChangedCallback, Executor>
mUidStateChangedCallbacks = new ArrayMap<>();
@@ -221,11 +220,12 @@ class AppOpsUidStateTrackerImpl implements AppOpsUidStateTracker {
public void updateUidProcState(int uid, int procState, int capability) {
int uidState = processStateToUidState(procState);
- int prevUidState = mUidStates.get(uid, AppOpsManager.MIN_PRIORITY_UID_STATE);
+ int prevUidState = mUidStates.get(uid, AppOpsManager.UID_STATE_NONEXISTENT);
int prevCapability = mCapability.get(uid, PROCESS_CAPABILITY_NONE);
- int pendingUidState = mPendingUidStates.get(uid, MIN_PRIORITY_UID_STATE);
+ int pendingUidState = mPendingUidStates.get(uid, UID_STATE_NONEXISTENT);
int pendingCapability = mPendingCapability.get(uid, PROCESS_CAPABILITY_NONE);
long pendingStateCommitTime = mPendingCommitTime.get(uid, 0);
+
if ((pendingStateCommitTime == 0
&& (uidState != prevUidState || capability != prevCapability))
|| (pendingStateCommitTime != 0
@@ -239,8 +239,7 @@ class AppOpsUidStateTrackerImpl implements AppOpsUidStateTracker {
boolean hasLostCapability = (prevCapability & ~capability) != 0;
- if (procState == PROCESS_STATE_NONEXISTENT) {
- mPendingGone.put(uid, true);
+ if (uidState == UID_STATE_NONEXISTENT) {
commitUidPendingState(uid);
} else if (uidState < prevUidState) {
// We are moving to a more important state, or the new state may be in the
@@ -342,7 +341,7 @@ class AppOpsUidStateTrackerImpl implements AppOpsUidStateTracker {
private void commitUidPendingState(int uid) {
- int uidState = mUidStates.get(uid, MIN_PRIORITY_UID_STATE);
+ int uidState = mUidStates.get(uid, UID_STATE_NONEXISTENT);
int capability = mCapability.get(uid, PROCESS_CAPABILITY_NONE);
boolean appWidgetVisible = mAppWidgetVisible.get(uid, false);
@@ -350,18 +349,23 @@ class AppOpsUidStateTrackerImpl implements AppOpsUidStateTracker {
int pendingCapability = mPendingCapability.get(uid, capability);
boolean pendingAppWidgetVisible = mPendingAppWidgetVisible.get(uid, appWidgetVisible);
- boolean foregroundChange = uidState <= UID_STATE_MAX_LAST_NON_RESTRICTED
- != pendingUidState <= UID_STATE_MAX_LAST_NON_RESTRICTED
+ // UID_STATE_NONEXISTENT is a state that isn't used outside of this class, nonexistent
+ // processes have always been represented as CACHED
+ int externalUidState = Math.min(uidState, UID_STATE_CACHED);
+ int externalPendingUidState = Math.min(pendingUidState, UID_STATE_CACHED);
+
+ boolean foregroundChange = externalUidState <= UID_STATE_MAX_LAST_NON_RESTRICTED
+ != externalPendingUidState <= UID_STATE_MAX_LAST_NON_RESTRICTED
|| capability != pendingCapability
|| appWidgetVisible != pendingAppWidgetVisible;
- if (uidState != pendingUidState
+ if (externalUidState != externalPendingUidState
|| capability != pendingCapability
|| appWidgetVisible != pendingAppWidgetVisible) {
if (foregroundChange) {
// To save on memory usage, log only interesting changes.
- mEventLog.logCommitUidState(uid, pendingUidState, pendingCapability,
+ mEventLog.logCommitUidState(uid, externalPendingUidState, pendingCapability,
pendingAppWidgetVisible, appWidgetVisible != pendingAppWidgetVisible);
}
@@ -370,24 +374,23 @@ class AppOpsUidStateTrackerImpl implements AppOpsUidStateTracker {
Executor executor = mUidStateChangedCallbacks.valueAt(i);
executor.execute(PooledLambda.obtainRunnable(
- UidStateChangedCallback::onUidStateChanged, cb, uid, pendingUidState,
- foregroundChange));
+ UidStateChangedCallback::onUidStateChanged, cb, uid,
+ externalPendingUidState, foregroundChange));
}
}
- if (mPendingGone.get(uid, false)) {
+ if (pendingUidState == UID_STATE_NONEXISTENT && uidState != pendingUidState) {
mUidStates.delete(uid);
mCapability.delete(uid);
mAppWidgetVisible.delete(uid);
- mPendingGone.delete(uid);
if (finishRunningOpsForKilledPackages()) {
for (int i = 0; i < mUidStateChangedCallbacks.size(); i++) {
UidStateChangedCallback cb = mUidStateChangedCallbacks.keyAt(i);
Executor executor = mUidStateChangedCallbacks.valueAt(i);
+ // If foregroundness changed it should be handled in earlier callback invocation
executor.execute(PooledLambda.obtainRunnable(
- UidStateChangedCallback::onUidStateChanged, cb, uid,
- UID_STATE_NONEXISTENT, foregroundChange));
+ UidStateChangedCallback::onUidProcessDeath, cb, uid));
}
}
} else {
diff --git a/services/core/java/com/android/server/audio/AudioServerPermissionProvider.java b/services/core/java/com/android/server/audio/AudioServerPermissionProvider.java
index 7502664a9628..180ef855cfda 100644
--- a/services/core/java/com/android/server/audio/AudioServerPermissionProvider.java
+++ b/services/core/java/com/android/server/audio/AudioServerPermissionProvider.java
@@ -39,6 +39,7 @@ import android.os.Trace;
import android.os.UserHandle;
import android.util.ArraySet;
import android.util.IntArray;
+import android.util.Log;
import com.android.internal.annotations.GuardedBy;
import com.android.media.permission.INativePermissionController;
@@ -62,6 +63,8 @@ import java.util.stream.Collectors;
/** Responsible for synchronizing system server permission state to the native audioserver. */
public class AudioServerPermissionProvider {
+ static final String TAG = "AudioServerPermissionProvider";
+
static final String[] MONITORED_PERMS = new String[PermissionEnum.ENUM_SIZE];
static final byte[] HDS_PERMS = new byte[] {PermissionEnum.CAPTURE_AUDIO_HOTWORD,
@@ -219,10 +222,13 @@ public class AudioServerPermissionProvider {
public void setIsolatedServiceUid(int uid, int owningUid) {
synchronized (mLock) {
if (mHdsUid == uid) return;
- var packageNameSet = mPackageMap.get(owningUid);
- if (packageNameSet == null) return;
- var packageName = packageNameSet.iterator().next();
- onModifyPackageState(uid, packageName, /* isRemove= */ false);
+ var packageNameSet = mPackageMap.get(UserHandle.getAppId(owningUid));
+ if (packageNameSet != null) {
+ var packageName = packageNameSet.iterator().next();
+ onModifyPackageState(uid, packageName, /* isRemove= */ false);
+ } else {
+ Log.wtf(TAG, "setIsolatedService owning uid not found");
+ }
// permissions
mHdsUid = uid;
if (mDest == null) {
@@ -249,11 +255,19 @@ public class AudioServerPermissionProvider {
public void clearIsolatedServiceUid(int uid) {
synchronized (mLock) {
- if (mHdsUid != uid) return;
- var packageNameSet = mPackageMap.get(uid);
- if (packageNameSet == null) return;
- var packageName = packageNameSet.iterator().next();
- onModifyPackageState(uid, packageName, /* isRemove= */ true);
+ var packageNameSet = mPackageMap.get(UserHandle.getAppId(uid));
+ if (mHdsUid != uid) {
+ Log.wtf(TAG,
+ "Unexpected isolated service uid cleared: " + uid + packageNameSet
+ + ", expected " + mHdsUid);
+ return;
+ }
+ if (packageNameSet != null) {
+ var packageName = packageNameSet.iterator().next();
+ onModifyPackageState(uid, packageName, /* isRemove= */ true);
+ } else {
+ Log.wtf(TAG, "clearIsolatedService uid not found");
+ }
// permissions
if (mDest == null) {
mIsUpdateDeferred = true;
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index bf7f1946531c..b1acfe830eed 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -186,6 +186,7 @@ import android.media.audiopolicy.AudioPolicyConfig;
import android.media.audiopolicy.AudioProductStrategy;
import android.media.audiopolicy.AudioVolumeGroup;
import android.media.audiopolicy.IAudioPolicyCallback;
+import android.media.audiopolicy.IAudioVolumeChangeDispatcher;
import android.media.permission.ClearCallingIdentityContext;
import android.media.permission.SafeCloseable;
import android.media.projection.IMediaProjection;
@@ -929,7 +930,8 @@ public class AudioService extends IAudioService.Stub
private final Object mAbsoluteVolumeDeviceInfoMapLock = new Object();
// Devices where the framework sends a full scale audio signal, and controls the volume of
// the external audio system separately.
- // For possible volume behaviors, see {@link AudioManager.AbsoluteDeviceVolumeBehavior}.
+ // For possible volume behaviors, see
+ // {@link AudioDeviceVolumeManager.AbsoluteDeviceVolumeBehavior}.
@GuardedBy("mAbsoluteVolumeDeviceInfoMapLock")
Map<Integer, AbsoluteVolumeDeviceInfo> mAbsoluteVolumeDeviceInfoMap = new ArrayMap<>();
@@ -942,7 +944,7 @@ public class AudioService extends IAudioService.Stub
private final List<VolumeInfo> mVolumeInfos;
private final IAudioDeviceVolumeDispatcher mCallback;
private final boolean mHandlesVolumeAdjustment;
- private @AudioManager.AbsoluteDeviceVolumeBehavior int mDeviceVolumeBehavior;
+ private @AudioDeviceVolumeManager.AbsoluteDeviceVolumeBehavior int mDeviceVolumeBehavior;
private AbsoluteVolumeDeviceInfo(
AudioService parent,
@@ -950,7 +952,7 @@ public class AudioService extends IAudioService.Stub
List<VolumeInfo> volumeInfos,
IAudioDeviceVolumeDispatcher callback,
boolean handlesVolumeAdjustment,
- @AudioManager.AbsoluteDeviceVolumeBehavior int behavior) {
+ @AudioDeviceVolumeManager.AbsoluteDeviceVolumeBehavior int behavior) {
this.mParent = parent;
this.mDevice = device;
this.mVolumeInfos = volumeInfos;
@@ -1387,6 +1389,7 @@ public class AudioService extends IAudioService.Stub
mUseVolumeGroupAliases = mContext.getResources().getBoolean(
com.android.internal.R.bool.config_handleVolumeAliasesUsingVolumeGroups);
+ mAudioVolumeChangeHandler = new AudioVolumeChangeHandler(mAudioSystem);
// Initialize volume
// Priority 1 - Android Property
// Priority 2 - Audio Policy Service
@@ -4187,7 +4190,13 @@ public class AudioService extends IAudioService.Stub
Log.d(TAG, "adjustStreamVolume postSetHearingAidVolumeIndex index="
+ newIndex + " stream=" + streamType);
}
- mDeviceBroker.postSetHearingAidVolumeIndex(newIndex, streamType);
+ int haIndex;
+ final VolumeStreamState vss = getVssForStreamOrDefault(streamType);
+ synchronized (mVolumeStateLock) {
+ haIndex = (int) (vss.getMinIndex() + (newIndex - vss.getMinIndex())
+ / vss.getIndexStepFactor());
+ }
+ mDeviceBroker.postSetHearingAidVolumeIndex(haIndex, streamType);
}
}
@@ -4451,6 +4460,21 @@ public class AudioService extends IAudioService.Stub
}
}
+ //================================
+ // Audio Volume Change Dispatcher
+ //================================
+ private final AudioVolumeChangeHandler mAudioVolumeChangeHandler;
+
+ /** @see AudioManager#registerVolumeGroupCallback(executor, callback) */
+ public void registerAudioVolumeCallback(IAudioVolumeChangeDispatcher callback) {
+ mAudioVolumeChangeHandler.registerListener(callback);
+ }
+
+ /** @see AudioManager#unregisterVolumeGroupCallback(callback) */
+ public void unregisterAudioVolumeCallback(IAudioVolumeChangeDispatcher callback) {
+ mAudioVolumeChangeHandler.unregisterListener(callback);
+ }
+
@Override
@android.annotation.EnforcePermission(anyOf = {
MODIFY_AUDIO_SETTINGS_PRIVILEGED, MODIFY_AUDIO_ROUTING })
@@ -5125,7 +5149,13 @@ public class AudioService extends IAudioService.Stub
mDeviceBroker.postSetLeAudioVolumeIndex(index * 10,
getVssForStreamOrDefault(streamType).getMaxIndex(), streamType);
} else if (device == AudioSystem.DEVICE_OUT_HEARING_AID) {
- mDeviceBroker.postSetHearingAidVolumeIndex(index * 10, streamType);
+ int haIndex = index * 10;
+ final VolumeStreamState vss = getVssForStreamOrDefault(streamType);
+ synchronized (mVolumeStateLock) {
+ haIndex = (int) (vss.getMinIndex()
+ + (haIndex - vss.getMinIndex()) / vss.getIndexStepFactor());
+ }
+ mDeviceBroker.postSetHearingAidVolumeIndex(haIndex, streamType);
} else if (AudioSystem.DEVICE_OUT_ALL_A2DP_SET.contains(device)) {
mDeviceBroker.postSetAvrcpAbsoluteVolumeIndex(index);
} else {
@@ -5256,7 +5286,13 @@ public class AudioService extends IAudioService.Stub
&& streamType == getBluetoothContextualVolumeStream()) {
Log.i(TAG, "setStreamVolume postSetHearingAidVolumeIndex index=" + index
+ " stream=" + streamType);
- mDeviceBroker.postSetHearingAidVolumeIndex(index, streamType);
+ int haIndex;
+ final VolumeStreamState vss = getVssForStreamOrDefault(streamType);
+ synchronized (mVolumeStateLock) {
+ haIndex = (int) (vss.getMinIndex()
+ + (index - vss.getMinIndex()) / vss.getIndexStepFactor());
+ }
+ mDeviceBroker.postSetHearingAidVolumeIndex(haIndex, streamType);
}
synchronized (mHdmiClientLock) {
@@ -8173,7 +8209,7 @@ public class AudioService extends IAudioService.Stub
IAudioDeviceVolumeDispatcher cb, String packageName,
AudioDeviceAttributes device, List<VolumeInfo> volumes,
boolean handlesVolumeAdjustment,
- @AudioManager.AbsoluteDeviceVolumeBehavior int deviceVolumeBehavior) {
+ @AudioDeviceVolumeManager.AbsoluteDeviceVolumeBehavior int deviceVolumeBehavior) {
// verify permissions
if (mContext.checkCallingOrSelfPermission(MODIFY_AUDIO_ROUTING)
!= PackageManager.PERMISSION_GRANTED
@@ -8240,12 +8276,13 @@ public class AudioService extends IAudioService.Stub
@android.annotation.EnforcePermission(anyOf = {
MODIFY_AUDIO_ROUTING, MODIFY_AUDIO_SETTINGS_PRIVILEGED })
public void setDeviceVolumeBehavior(@NonNull AudioDeviceAttributes device,
- @AudioManager.DeviceVolumeBehavior int deviceVolumeBehavior, @Nullable String pkgName) {
+ @AudioDeviceVolumeManager.DeviceVolumeBehavior int deviceVolumeBehavior,
+ @Nullable String pkgName) {
// verify permissions
super.setDeviceVolumeBehavior_enforcePermission();
// verify arguments
Objects.requireNonNull(device);
- AudioManager.enforceValidVolumeBehavior(deviceVolumeBehavior);
+ AudioDeviceVolumeManager.enforceValidVolumeBehavior(deviceVolumeBehavior);
device = retrieveBluetoothAddress(device);
@@ -8268,7 +8305,8 @@ public class AudioService extends IAudioService.Stub
}
private void setDeviceVolumeBehaviorInternal(@NonNull AudioDeviceAttributes device,
- @AudioManager.DeviceVolumeBehavior int deviceVolumeBehavior, @NonNull String caller) {
+ @AudioDeviceVolumeManager.DeviceVolumeBehavior int deviceVolumeBehavior,
+ @NonNull String caller) {
int audioSystemDeviceOut = device.getInternalType();
boolean volumeBehaviorChanged = false;
// update device masks based on volume behavior
@@ -8323,7 +8361,7 @@ public class AudioService extends IAudioService.Stub
@android.annotation.EnforcePermission(anyOf = {
MODIFY_AUDIO_ROUTING, QUERY_AUDIO_STATE, MODIFY_AUDIO_SETTINGS_PRIVILEGED
})
- public @AudioManager.DeviceVolumeBehavior
+ public @AudioDeviceVolumeManager.DeviceVolumeBehavior
int getDeviceVolumeBehavior(@NonNull AudioDeviceAttributes device) {
// verify permissions
super.getDeviceVolumeBehavior_enforcePermission();
@@ -8335,7 +8373,7 @@ public class AudioService extends IAudioService.Stub
return getDeviceVolumeBehaviorInt(device);
}
- private @AudioManager.DeviceVolumeBehavior
+ private @AudioDeviceVolumeManager.DeviceVolumeBehavior
int getDeviceVolumeBehaviorInt(@NonNull AudioDeviceAttributes device) {
// Get the internal type set by the AudioDeviceAttributes constructor which is always more
// exact (avoids double conversions) than a conversion from SDK type via
@@ -15354,7 +15392,8 @@ public class AudioService extends IAudioService.Stub
/**
* Returns whether the input device uses absolute volume behavior, including its variants.
- * For included volume behaviors, see {@link AudioManager.AbsoluteDeviceVolumeBehavior}.
+ * For included volume behaviors, see
+ * {@link AudioDeviceVolumeManager.AbsoluteDeviceVolumeBehavior}.
* <p>This is distinct from Bluetooth A2DP absolute volume behavior
* ({@link #isA2dpAbsoluteVolumeDevice}).
*/
@@ -15381,7 +15420,7 @@ public class AudioService extends IAudioService.Stub
}
private void persistDeviceVolumeBehavior(int deviceType,
- @AudioManager.DeviceVolumeBehavior int deviceVolumeBehavior) {
+ @AudioDeviceVolumeManager.DeviceVolumeBehavior int deviceVolumeBehavior) {
if (DEBUG_VOL) {
Log.d(TAG, "Persisting Volume Behavior for DeviceType: " + deviceType);
}
@@ -15396,7 +15435,7 @@ public class AudioService extends IAudioService.Stub
}
}
- @AudioManager.DeviceVolumeBehaviorState
+ @AudioDeviceVolumeManager.DeviceVolumeBehaviorState
private int retrieveStoredDeviceVolumeBehavior(int deviceType) {
return mSettings.getSystemIntForUser(mContentResolver,
getSettingsNameForDeviceVolumeBehavior(deviceType),
diff --git a/services/core/java/com/android/server/audio/AudioSystemAdapter.java b/services/core/java/com/android/server/audio/AudioSystemAdapter.java
index a6267c156fb3..ced5faeeff27 100644
--- a/services/core/java/com/android/server/audio/AudioSystemAdapter.java
+++ b/services/core/java/com/android/server/audio/AudioSystemAdapter.java
@@ -23,6 +23,7 @@ import android.media.AudioDeviceAttributes;
import android.media.AudioMixerAttributes;
import android.media.AudioSystem;
import android.media.IDevicesForAttributesCallback;
+import android.media.INativeAudioVolumeGroupCallback;
import android.media.ISoundDose;
import android.media.ISoundDoseCallback;
import android.media.audiopolicy.AudioMix;
@@ -758,6 +759,29 @@ public class AudioSystemAdapter implements AudioSystem.RoutingUpdateCallback,
}
/**
+ * Same as {@link AudioSystem#registerAudioVolumeGroupCallback(INativeAudioVolumeGroupCallback)}
+ * @param callback to register
+ * @return {@link #SUCCESS} if successfully registered.
+ *
+ * @hide
+ */
+ public int registerAudioVolumeGroupCallback(INativeAudioVolumeGroupCallback callback) {
+ return AudioSystem.registerAudioVolumeGroupCallback(callback);
+ }
+
+ /**
+ * Same as
+ * {@link AudioSystem#unregisterAudioVolumeGroupCallback(INativeAudioVolumeGroupCallback)}.
+ * @param callback to register
+ * @return {@link #SUCCESS} if successfully registered.
+ *
+ * @hide
+ */
+ public int unregisterAudioVolumeGroupCallback(INativeAudioVolumeGroupCallback callback) {
+ return AudioSystem.unregisterAudioVolumeGroupCallback(callback);
+ }
+
+ /**
* Part of AudioService dump
* @param pw
*/
diff --git a/services/core/java/com/android/server/audio/AudioVolumeChangeHandler.java b/services/core/java/com/android/server/audio/AudioVolumeChangeHandler.java
new file mode 100644
index 000000000000..2bb4301bb8fd
--- /dev/null
+++ b/services/core/java/com/android/server/audio/AudioVolumeChangeHandler.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2025 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.server.audio;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.media.INativeAudioVolumeGroupCallback;
+import android.media.audio.common.AudioVolumeGroupChangeEvent;
+import android.media.audiopolicy.IAudioVolumeChangeDispatcher;
+import android.os.RemoteCallbackList;
+import android.os.RemoteException;
+import android.util.Slog;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.Preconditions;
+
+/**
+ * The AudioVolumeChangeHandler handles AudioVolume callbacks invoked by native
+ * {@link INativeAudioVolumeGroupCallback} callback.
+ */
+/* private package */ class AudioVolumeChangeHandler {
+ private static final String TAG = "AudioVolumeChangeHandler";
+
+ private final Object mLock = new Object();
+ @GuardedBy("mLock")
+ private final RemoteCallbackList<IAudioVolumeChangeDispatcher> mListeners =
+ new RemoteCallbackList<>();
+ private final @NonNull AudioSystemAdapter mAudioSystem;
+ private @Nullable AudioVolumeGroupCallback mAudioVolumeGroupCallback;
+
+ AudioVolumeChangeHandler(@NonNull AudioSystemAdapter asa) {
+ mAudioSystem = asa;
+ }
+
+ @GuardedBy("mLock")
+ private void lazyInitLocked() {
+ mAudioVolumeGroupCallback = new AudioVolumeGroupCallback();
+ mAudioSystem.registerAudioVolumeGroupCallback(mAudioVolumeGroupCallback);
+ }
+
+ private void sendAudioVolumeGroupChangedToClients(int groupId, int index) {
+ RemoteCallbackList<IAudioVolumeChangeDispatcher> listeners;
+ int nbDispatchers;
+ synchronized (mLock) {
+ listeners = mListeners;
+ nbDispatchers = mListeners.beginBroadcast();
+ }
+ for (int i = 0; i < nbDispatchers; i++) {
+ try {
+ listeners.getBroadcastItem(i).onAudioVolumeGroupChanged(groupId, index);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to broadcast Volume Changed event");
+ }
+ }
+ synchronized (mLock) {
+ mListeners.finishBroadcast();
+ }
+ }
+
+ /**
+ * @param cb the {@link IAudioVolumeChangeDispatcher} to register
+ */
+ public void registerListener(@NonNull IAudioVolumeChangeDispatcher cb) {
+ Preconditions.checkNotNull(cb, "Volume group callback must not be null");
+ synchronized (mLock) {
+ if (mAudioVolumeGroupCallback == null) {
+ lazyInitLocked();
+ }
+ mListeners.register(cb);
+ }
+ }
+
+ /**
+ * @param cb the {@link IAudioVolumeChangeDispatcher} to unregister
+ */
+ public void unregisterListener(@NonNull IAudioVolumeChangeDispatcher cb) {
+ Preconditions.checkNotNull(cb, "Volume group callback must not be null");
+ synchronized (mLock) {
+ mListeners.unregister(cb);
+ }
+ }
+
+ private final class AudioVolumeGroupCallback extends INativeAudioVolumeGroupCallback.Stub {
+ public void onAudioVolumeGroupChanged(AudioVolumeGroupChangeEvent volumeEvent) {
+ Slog.v(TAG, "onAudioVolumeGroupChanged volumeEvent=" + volumeEvent);
+ sendAudioVolumeGroupChangedToClients(volumeEvent.groupId, volumeEvent.flags);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index a28069bbf050..95e58e1a7300 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -684,8 +684,9 @@ public final class DisplayManagerService extends SystemService {
final var backupManager = new BackupManager(mContext);
Consumer<Pair<DisplayTopology, DisplayTopologyGraph>> topologyChangedCallback =
update -> {
- if (mInputManagerInternal != null) {
- mInputManagerInternal.setDisplayTopology(update.second);
+ DisplayTopologyGraph graph = update.second;
+ if (mInputManagerInternal != null && graph != null) {
+ mInputManagerInternal.setDisplayTopology(graph);
}
deliverTopologyUpdate(update.first);
};
@@ -3647,7 +3648,7 @@ public final class DisplayManagerService extends SystemService {
private void deliverTopologyUpdate(DisplayTopology topology) {
if (DEBUG) {
- Slog.d(TAG, "Delivering topology update");
+ Slog.d(TAG, "Delivering topology update: " + topology);
}
if (Trace.isTagEnabled(Trace.TRACE_TAG_POWER)) {
Trace.instant(Trace.TRACE_TAG_POWER, "deliverTopologyUpdate");
@@ -4209,13 +4210,18 @@ public final class DisplayManagerService extends SystemService {
public boolean mWifiDisplayScanRequested;
- // A single pending event.
+ // A single pending display event.
private record Event(int displayId, @DisplayEvent int event) { };
- // The list of pending events. This is null until there is a pending event to be saved.
- // This is only used if {@link deferDisplayEventsWhenFrozen()} is true.
+ // The list of pending display events. This is null until there is a pending event to be
+ // saved. This is only used if {@link deferDisplayEventsWhenFrozen()} is true.
+ @GuardedBy("mCallback")
+ @Nullable
+ private ArrayList<Event> mPendingDisplayEvents;
+
@GuardedBy("mCallback")
- private ArrayList<Event> mPendingEvents;
+ @Nullable
+ private DisplayTopology mPendingTopology;
// Process states: a process is ready to receive events if it is neither cached nor
// frozen.
@@ -4285,7 +4291,10 @@ public final class DisplayManagerService extends SystemService {
*/
@GuardedBy("mCallback")
private boolean hasPendingAndIsReadyLocked() {
- return isReadyLocked() && mPendingEvents != null && !mPendingEvents.isEmpty() && mAlive;
+ boolean pendingDisplayEvents = mPendingDisplayEvents != null
+ && !mPendingDisplayEvents.isEmpty();
+ boolean pendingTopology = mPendingTopology != null;
+ return isReadyLocked() && (pendingDisplayEvents || pendingTopology) && mAlive;
}
/**
@@ -4366,7 +4375,8 @@ public final class DisplayManagerService extends SystemService {
// occurs as the client is transitioning to ready but pending events have not
// been dispatched. The new event must be added to the pending list to
// preserve event ordering.
- if (!isReadyLocked() || (mPendingEvents != null && !mPendingEvents.isEmpty())) {
+ if (!isReadyLocked() || (mPendingDisplayEvents != null
+ && !mPendingDisplayEvents.isEmpty())) {
// The client is interested in the event but is not ready to receive it.
// Put the event on the pending list.
addDisplayEvent(displayId, event);
@@ -4453,13 +4463,13 @@ public final class DisplayManagerService extends SystemService {
// This is only used if {@link deferDisplayEventsWhenFrozen()} is true.
@GuardedBy("mCallback")
private void addDisplayEvent(int displayId, int event) {
- if (mPendingEvents == null) {
- mPendingEvents = new ArrayList<>();
+ if (mPendingDisplayEvents == null) {
+ mPendingDisplayEvents = new ArrayList<>();
}
- if (!mPendingEvents.isEmpty()) {
+ if (!mPendingDisplayEvents.isEmpty()) {
// Ignore redundant events. Further optimization is possible by merging adjacent
// events.
- Event last = mPendingEvents.get(mPendingEvents.size() - 1);
+ Event last = mPendingDisplayEvents.get(mPendingDisplayEvents.size() - 1);
if (last.displayId == displayId && last.event == event) {
if (DEBUG) {
Slog.d(TAG, "Ignore redundant display event " + displayId + "/" + event
@@ -4468,12 +4478,13 @@ public final class DisplayManagerService extends SystemService {
return;
}
}
- mPendingEvents.add(new Event(displayId, event));
+ mPendingDisplayEvents.add(new Event(displayId, event));
}
/**
* @return {@code false} if RemoteException happens; otherwise {@code true} for
- * success.
+ * success. This returns true even if the update was deferred because the remote client is
+ * cached or frozen.
*/
boolean notifyTopologyUpdateAsync(DisplayTopology topology) {
if ((mInternalEventFlagsMask.get()
@@ -4490,6 +4501,18 @@ public final class DisplayManagerService extends SystemService {
// The client is not interested in this event, so do nothing.
return true;
}
+
+ if (deferDisplayEventsWhenFrozen()) {
+ synchronized (mCallback) {
+ // Save the new update if the client frozen or cached (not ready).
+ if (!isReadyLocked()) {
+ // The client is interested in the update but is not ready to receive it.
+ mPendingTopology = topology;
+ return true;
+ }
+ }
+ }
+
return transmitTopologyUpdate(topology);
}
@@ -4514,37 +4537,54 @@ public final class DisplayManagerService extends SystemService {
// would be unusual to do so. The method returns true on success.
// This is only used if {@link deferDisplayEventsWhenFrozen()} is true.
public boolean dispatchPending() {
- Event[] pending;
+ Event[] pendingDisplayEvents = null;
+ DisplayTopology pendingTopology;
synchronized (mCallback) {
- if (mPendingEvents == null || mPendingEvents.isEmpty() || !mAlive) {
+ if (!mAlive) {
return true;
}
if (!isReadyLocked()) {
return false;
}
- pending = new Event[mPendingEvents.size()];
- pending = mPendingEvents.toArray(pending);
- mPendingEvents.clear();
+
+ if (mPendingDisplayEvents != null && !mPendingDisplayEvents.isEmpty()) {
+ pendingDisplayEvents = new Event[mPendingDisplayEvents.size()];
+ pendingDisplayEvents = mPendingDisplayEvents.toArray(pendingDisplayEvents);
+ mPendingDisplayEvents.clear();
+ }
+
+ pendingTopology = mPendingTopology;
+ mPendingTopology = null;
}
try {
- for (int i = 0; i < pending.length; i++) {
- Event displayEvent = pending[i];
- if (DEBUG) {
- Slog.d(TAG, "Send pending display event #" + i + " "
- + displayEvent.displayId + "/"
- + displayEvent.event + " to " + mUid + "/" + mPid);
- }
+ if (pendingDisplayEvents != null) {
+ for (int i = 0; i < pendingDisplayEvents.length; i++) {
+ Event displayEvent = pendingDisplayEvents[i];
+ if (DEBUG) {
+ Slog.d(TAG, "Send pending display event #" + i + " "
+ + displayEvent.displayId + "/"
+ + displayEvent.event + " to " + mUid + "/" + mPid);
+ }
+
+ if (!shouldReceiveRefreshRateWithChangeUpdate(displayEvent.event)) {
+ continue;
+ }
- if (!shouldReceiveRefreshRateWithChangeUpdate(displayEvent.event)) {
- continue;
+ transmitDisplayEvent(displayEvent.displayId, displayEvent.event);
}
+ }
- transmitDisplayEvent(displayEvent.displayId, displayEvent.event);
+ if (pendingTopology != null) {
+ if (DEBUG) {
+ Slog.d(TAG, "Send pending topology: " + pendingTopology
+ + " to " + mUid + "/" + mPid);
+ }
+ mCallback.onTopologyChanged(pendingTopology);
}
+
return true;
} catch (RemoteException ex) {
- Slog.w(TAG, "Failed to notify process "
- + mPid + " that display topology changed, assuming it died.", ex);
+ Slog.w(TAG, "Failed to notify process " + mPid + ", assuming it died.", ex);
binderDied();
return false;
@@ -4556,11 +4596,12 @@ public final class DisplayManagerService extends SystemService {
if (deferDisplayEventsWhenFrozen()) {
final String fmt =
"mPid=%d mUid=%d mWifiDisplayScanRequested=%s"
- + " cached=%s frozen=%s pending=%d";
+ + " cached=%s frozen=%s pendingDisplayEvents=%d pendingTopology=%b";
synchronized (mCallback) {
return formatSimple(fmt,
mPid, mUid, mWifiDisplayScanRequested, mCached, mFrozen,
- (mPendingEvents == null) ? 0 : mPendingEvents.size());
+ (mPendingDisplayEvents == null) ? 0 : mPendingDisplayEvents.size(),
+ mPendingTopology != null);
}
} else {
final String fmt =
diff --git a/services/core/java/com/android/server/display/DisplayTopologyCoordinator.java b/services/core/java/com/android/server/display/DisplayTopologyCoordinator.java
index b4df1f76dccb..569a426b80d5 100644
--- a/services/core/java/com/android/server/display/DisplayTopologyCoordinator.java
+++ b/services/core/java/com/android/server/display/DisplayTopologyCoordinator.java
@@ -111,13 +111,14 @@ class DisplayTopologyCoordinator {
* @param info The display info
*/
void onDisplayAdded(DisplayInfo info) {
- if (!isDisplayAllowedInTopology(info)) {
+ if (!isDisplayAllowedInTopology(info, /* shouldLog= */ true)) {
return;
}
synchronized (mSyncRoot) {
addDisplayIdMappingLocked(info);
mDensities.put(info.displayId, info.logicalDensityDpi);
mTopology.addDisplay(info.displayId, getWidth(info), getHeight(info));
+ Slog.i(TAG, "Display " + info.displayId + " added, new topology: " + mTopology);
restoreTopologyLocked();
sendTopologyUpdateLocked();
}
@@ -128,7 +129,7 @@ class DisplayTopologyCoordinator {
* @param info The new display info
*/
void onDisplayChanged(DisplayInfo info) {
- if (!isDisplayAllowedInTopology(info)) {
+ if (!isDisplayAllowedInTopology(info, /* shouldLog= */ false)) {
return;
}
synchronized (mSyncRoot) {
@@ -149,6 +150,7 @@ class DisplayTopologyCoordinator {
synchronized (mSyncRoot) {
mDensities.delete(displayId);
if (mTopology.removeDisplay(displayId)) {
+ Slog.i(TAG, "Display " + displayId + " removed, new topology: " + mTopology);
removeDisplayIdMappingLocked(displayId);
restoreTopologyLocked();
sendTopologyUpdateLocked();
@@ -249,22 +251,28 @@ class DisplayTopologyCoordinator {
return pxToDp(info.logicalHeight, info.logicalDensityDpi);
}
- private boolean isDisplayAllowedInTopology(DisplayInfo info) {
+ private boolean isDisplayAllowedInTopology(DisplayInfo info, boolean shouldLog) {
if (info.type != Display.TYPE_INTERNAL && info.type != Display.TYPE_EXTERNAL
&& info.type != Display.TYPE_OVERLAY) {
- Slog.d(TAG, "Display " + info.displayId + " not allowed in topology because "
- + "type is not INTERNAL, EXTERNAL or OVERLAY");
+ if (shouldLog) {
+ Slog.d(TAG, "Display " + info.displayId + " not allowed in topology because "
+ + "type is not INTERNAL, EXTERNAL or OVERLAY");
+ }
return false;
}
if (info.type == Display.TYPE_INTERNAL && info.displayId != Display.DEFAULT_DISPLAY) {
- Slog.d(TAG, "Display " + info.displayId + " not allowed in topology because "
- + "it is a non-default internal display");
+ if (shouldLog) {
+ Slog.d(TAG, "Display " + info.displayId + " not allowed in topology because "
+ + "it is a non-default internal display");
+ }
return false;
}
if ((info.type == Display.TYPE_EXTERNAL || info.type == Display.TYPE_OVERLAY)
&& !mIsExtendedDisplayAllowed.getAsBoolean()) {
- Slog.d(TAG, "Display " + info.displayId + " not allowed in topology because "
- + "type is EXTERNAL or OVERLAY and !mIsExtendedDisplayAllowed");
+ if (shouldLog) {
+ Slog.d(TAG, "Display " + info.displayId + " not allowed in topology because "
+ + "type is EXTERNAL or OVERLAY and !mIsExtendedDisplayAllowed");
+ }
return false;
}
return true;
diff --git a/services/core/java/com/android/server/display/OverlayDisplayAdapter.java b/services/core/java/com/android/server/display/OverlayDisplayAdapter.java
index 60b7fca99e7b..228e6f1c4ddb 100644
--- a/services/core/java/com/android/server/display/OverlayDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/OverlayDisplayAdapter.java
@@ -130,6 +130,14 @@ final class OverlayDisplayAdapter extends DisplayAdapter {
private static final String OVERLAY_DISPLAY_FLAG_FIXED_CONTENT_MODE =
"fixed_content_mode";
+ /**
+ * When this flag is set, disables support for moving and resizing the overlay window.
+ * As the window is made non-touchable, this also makes it possible to directly interact with
+ * the content underneath.
+ */
+ private static final String OVERLAY_DISPLAY_FLAG_DISABLE_WINDOW_INTERACTION =
+ "disable_window_interaction";
+
// Gravity flags to decide where the overlay should be shown.
private static final String GRAVITY_TOP_LEFT = "gravity_top_left";
private static final String GRAVITY_BOTTOM_RIGHT = "gravity_bottom_right";
@@ -571,9 +579,9 @@ final class OverlayDisplayAdapter extends DisplayAdapter {
@Override
public void run() {
OverlayMode mode = mModes.get(mActiveMode);
- OverlayDisplayWindow window = new OverlayDisplayWindow(getContext(),
- mName, mode.mWidth, mode.mHeight, mode.mDensityDpi, mGravity,
- mFlags.mSecure, OverlayDisplayHandle.this);
+ OverlayDisplayWindow window = new OverlayDisplayWindow(getContext(), mName,
+ mode.mWidth, mode.mHeight, mode.mDensityDpi, mGravity, mFlags.mSecure,
+ mFlags.mDisableWindowInteraction, OverlayDisplayHandle.this);
window.show();
synchronized (getSyncRoot()) {
@@ -655,6 +663,9 @@ final class OverlayDisplayAdapter extends DisplayAdapter {
/** See {@link #OVERLAY_DISPLAY_FLAG_FIXED_CONTENT_MODE}. */
final boolean mFixedContentMode;
+ /** See {@link #OVERLAY_DISPLAY_FLAG_DISABLE_WINDOW_INTERACTION}. */
+ final boolean mDisableWindowInteraction;
+
final int mGravity;
OverlayFlags(
@@ -662,11 +673,13 @@ final class OverlayDisplayAdapter extends DisplayAdapter {
boolean ownContentOnly,
boolean shouldShowSystemDecorations,
boolean fixedContentMode,
+ boolean disableWindowInteraction,
int gravity) {
mSecure = secure;
mOwnContentOnly = ownContentOnly;
mShouldShowSystemDecorations = shouldShowSystemDecorations;
mFixedContentMode = fixedContentMode;
+ mDisableWindowInteraction = disableWindowInteraction;
mGravity = gravity;
}
@@ -677,6 +690,7 @@ final class OverlayDisplayAdapter extends DisplayAdapter {
false /* ownContentOnly */,
false /* shouldShowSystemDecorations */,
false /* fixedContentMode */,
+ false /* disableWindowInteraction */,
Gravity.NO_GRAVITY);
}
@@ -684,6 +698,7 @@ final class OverlayDisplayAdapter extends DisplayAdapter {
boolean ownContentOnly = false;
boolean shouldShowSystemDecorations = false;
boolean fixedContentMode = false;
+ boolean disableWindowInteraction = false;
int gravity = Gravity.NO_GRAVITY;
for (String flag: flagString.split(FLAG_SPLITTER)) {
if (OVERLAY_DISPLAY_FLAG_SECURE.equals(flag)) {
@@ -694,12 +709,14 @@ final class OverlayDisplayAdapter extends DisplayAdapter {
shouldShowSystemDecorations = true;
} else if (OVERLAY_DISPLAY_FLAG_FIXED_CONTENT_MODE.equals(flag)) {
fixedContentMode = true;
+ } else if (OVERLAY_DISPLAY_FLAG_DISABLE_WINDOW_INTERACTION.equals(flag)) {
+ disableWindowInteraction = true;
} else {
gravity = parseOverlayGravity(flag);
}
}
return new OverlayFlags(secure, ownContentOnly, shouldShowSystemDecorations,
- fixedContentMode, gravity);
+ fixedContentMode, disableWindowInteraction, gravity);
}
@Override
@@ -709,6 +726,7 @@ final class OverlayDisplayAdapter extends DisplayAdapter {
.append(", ownContentOnly=").append(mOwnContentOnly)
.append(", shouldShowSystemDecorations=").append(mShouldShowSystemDecorations)
.append(", fixedContentMode=").append(mFixedContentMode)
+ .append(", disableWindowInteraction=").append(mDisableWindowInteraction)
.append(", gravity").append(Gravity.toString(mGravity))
.append("}")
.toString();
diff --git a/services/core/java/com/android/server/display/OverlayDisplayWindow.java b/services/core/java/com/android/server/display/OverlayDisplayWindow.java
index 3fd58e8641c3..523bbfa7d69a 100644
--- a/services/core/java/com/android/server/display/OverlayDisplayWindow.java
+++ b/services/core/java/com/android/server/display/OverlayDisplayWindow.java
@@ -69,6 +69,7 @@ final class OverlayDisplayWindow implements DumpUtils.Dump {
private int mDensityDpi;
private final int mGravity;
private final boolean mSecure;
+ private final boolean mDisableWindowInteraction;
private final Listener mListener;
private String mTitle;
@@ -96,15 +97,15 @@ final class OverlayDisplayWindow implements DumpUtils.Dump {
private float mLiveTranslationY;
private float mLiveScale = 1.0f;
- public OverlayDisplayWindow(Context context, String name,
- int width, int height, int densityDpi, int gravity, boolean secure,
- Listener listener) {
+ OverlayDisplayWindow(Context context, String name, int width, int height, int densityDpi,
+ int gravity, boolean secure, boolean disableWindowInteraction, Listener listener) {
// Workaround device freeze (b/38372997)
ThreadedRenderer.disableVsync();
mContext = context;
mName = name;
mGravity = gravity;
mSecure = secure;
+ mDisableWindowInteraction = disableWindowInteraction;
mListener = listener;
mDisplayManager = (DisplayManager)context.getSystemService(
@@ -226,8 +227,10 @@ final class OverlayDisplayWindow implements DumpUtils.Dump {
if (mSecure) {
mWindowParams.flags |= WindowManager.LayoutParams.FLAG_SECURE;
}
- if (DISABLE_MOVE_AND_RESIZE) {
+ if (DISABLE_MOVE_AND_RESIZE || mDisableWindowInteraction) {
mWindowParams.flags |= WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
+ mWindowParams.privateFlags |=
+ WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY;
}
mWindowParams.privateFlags |=
WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_HARDWARE_ACCELERATED;
diff --git a/services/core/java/com/android/server/display/mode/DisplayModeDirector.java b/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
index c37733b05fba..2c90e1919123 100644
--- a/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
+++ b/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
@@ -156,6 +156,8 @@ public class DisplayModeDirector {
private SparseArray<Display.Mode> mDefaultModeByDisplay;
// a map from display id to display device config
private SparseArray<DisplayDeviceConfig> mDisplayDeviceConfigByDisplay = new SparseArray<>();
+ // set containing connected external display ids
+ private final Set<Integer> mExternalDisplaysConnected = new HashSet<>();
private SparseBooleanArray mHasArrSupport;
@@ -425,7 +427,7 @@ public class DisplayModeDirector {
// Some external displays physical refresh rate modes are slightly above 60hz.
// SurfaceFlinger will not enable these display modes unless it is configured to allow
// render rate at least at this frame rate.
- if (mDisplayObserver.isExternalDisplayLocked(displayId)) {
+ if (isExternalDisplayLocked(displayId)) {
primarySummary.maxRenderFrameRate = Math.max(baseMode.getRefreshRate(),
primarySummary.maxRenderFrameRate);
appRequestSummary.maxRenderFrameRate = Math.max(baseMode.getRefreshRate(),
@@ -653,6 +655,10 @@ public class DisplayModeDirector {
}
}
+ boolean isExternalDisplayLocked(int displayId) {
+ return mExternalDisplaysConnected.contains(displayId);
+ }
+
private static String switchingTypeToString(@DisplayManager.SwitchingType int type) {
switch (type) {
case DisplayManager.SWITCHING_TYPE_NONE:
@@ -694,6 +700,11 @@ public class DisplayModeDirector {
}
@VisibleForTesting
+ void addExternalDisplayId(int externalDisplayId) {
+ mExternalDisplaysConnected.add(externalDisplayId);
+ }
+
+ @VisibleForTesting
void injectBrightnessObserver(BrightnessObserver brightnessObserver) {
mBrightnessObserver = brightnessObserver;
}
@@ -1210,7 +1221,7 @@ public class DisplayModeDirector {
@GuardedBy("mLock")
private void updateRefreshRateSettingLocked(float minRefreshRate, float peakRefreshRate,
float defaultRefreshRate, int displayId) {
- if (mDisplayObserver.isExternalDisplayLocked(displayId)) {
+ if (isExternalDisplayLocked(displayId)) {
if (mLoggingEnabled) {
Slog.d(TAG, "skip updateRefreshRateSettingLocked for external display "
+ displayId);
@@ -1309,20 +1320,25 @@ public class DisplayModeDirector {
public void setAppRequest(int displayId, int modeId, float requestedRefreshRate,
float requestedMinRefreshRateRange, float requestedMaxRefreshRateRange) {
Display.Mode requestedMode;
+ boolean isExternalDisplay;
synchronized (mLock) {
requestedMode = findModeLocked(displayId, modeId, requestedRefreshRate);
+ isExternalDisplay = isExternalDisplayLocked(displayId);
}
Vote frameRateVote = getFrameRateVote(
requestedMinRefreshRateRange, requestedMaxRefreshRateRange);
Vote baseModeRefreshRateVote = getBaseModeVote(requestedMode, requestedRefreshRate);
- Vote sizeVote = getSizeVote(requestedMode);
mVotesStorage.updateVote(displayId, Vote.PRIORITY_APP_REQUEST_RENDER_FRAME_RATE_RANGE,
frameRateVote);
mVotesStorage.updateVote(displayId, Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE,
baseModeRefreshRateVote);
- mVotesStorage.updateVote(displayId, Vote.PRIORITY_APP_REQUEST_SIZE, sizeVote);
+
+ if (!isExternalDisplay) {
+ Vote sizeVote = getSizeVote(requestedMode);
+ mVotesStorage.updateVote(displayId, Vote.PRIORITY_APP_REQUEST_SIZE, sizeVote);
+ }
}
private Display.Mode findModeLocked(int displayId, int modeId, float requestedRefreshRate) {
@@ -1420,7 +1436,6 @@ public class DisplayModeDirector {
private int mExternalDisplayPeakHeight;
private int mExternalDisplayPeakRefreshRate;
private final boolean mRefreshRateSynchronizationEnabled;
- private final Set<Integer> mExternalDisplaysConnected = new HashSet<>();
DisplayObserver(Context context, Handler handler, VotesStorage votesStorage,
Injector injector) {
@@ -1541,10 +1556,6 @@ public class DisplayModeDirector {
}
}
- boolean isExternalDisplayLocked(int displayId) {
- return mExternalDisplaysConnected.contains(displayId);
- }
-
@Nullable
private DisplayInfo getDisplayInfo(int displayId) {
DisplayInfo info = new DisplayInfo();
diff --git a/services/core/java/com/android/server/display/mode/ModeChangeObserver.java b/services/core/java/com/android/server/display/mode/ModeChangeObserver.java
index 50782a2f22c8..debfb067b710 100644
--- a/services/core/java/com/android/server/display/mode/ModeChangeObserver.java
+++ b/services/core/java/com/android/server/display/mode/ModeChangeObserver.java
@@ -25,6 +25,8 @@ import android.view.Display;
import android.view.DisplayAddress;
import android.view.DisplayEventReceiver;
+import androidx.annotation.VisibleForTesting;
+
import java.util.HashSet;
import java.util.Set;
@@ -35,8 +37,10 @@ final class ModeChangeObserver {
private final DisplayModeDirector.Injector mInjector;
@SuppressWarnings("unused")
- private DisplayEventReceiver mModeChangeListener;
- private DisplayManager.DisplayListener mDisplayListener;
+ @VisibleForTesting
+ DisplayEventReceiver mModeChangeListener;
+ @VisibleForTesting
+ DisplayManager.DisplayListener mDisplayListener;
private final LongSparseArray<Set<Integer>> mRejectedModesMap =
new LongSparseArray<>();
private final LongSparseArray<Integer> mPhysicalIdToLogicalIdMap = new LongSparseArray<>();
diff --git a/services/core/java/com/android/server/hdmi/AudioDeviceVolumeManagerWrapper.java b/services/core/java/com/android/server/hdmi/AudioDeviceVolumeManagerWrapper.java
index ab86433ca50d..62c3dbd3df8c 100644
--- a/services/core/java/com/android/server/hdmi/AudioDeviceVolumeManagerWrapper.java
+++ b/services/core/java/com/android/server/hdmi/AudioDeviceVolumeManagerWrapper.java
@@ -23,6 +23,7 @@ import android.annotation.CallbackExecutor;
import android.annotation.NonNull;
import android.media.AudioDeviceAttributes;
import android.media.AudioDeviceVolumeManager;
+import android.media.AudioManager;
import android.media.VolumeInfo;
import java.util.concurrent.Executor;
@@ -53,7 +54,7 @@ public interface AudioDeviceVolumeManagerWrapper {
/**
* Wrapper for {@link AudioDeviceVolumeManager#setDeviceAbsoluteVolumeBehavior(
- * AudioDeviceAttributes, VolumeInfo, Executor, OnAudioDeviceVolumeChangedListener, boolean)}
+ * AudioDeviceAttributes, VolumeInfo, boolean, Executor, OnAudioDeviceVolumeChangedListener)}
*/
void setDeviceAbsoluteVolumeBehavior(
@NonNull AudioDeviceAttributes device,
@@ -64,7 +65,7 @@ public interface AudioDeviceVolumeManagerWrapper {
/**
* Wrapper for {@link AudioDeviceVolumeManager#setDeviceAbsoluteVolumeAdjustOnlyBehavior(
- * AudioDeviceAttributes, VolumeInfo, Executor, OnAudioDeviceVolumeChangedListener, boolean)}
+ * AudioDeviceAttributes, VolumeInfo, boolean, Executor, OnAudioDeviceVolumeChangedListener)}
*/
void setDeviceAbsoluteVolumeAdjustOnlyBehavior(
@NonNull AudioDeviceAttributes device,
@@ -72,4 +73,16 @@ public interface AudioDeviceVolumeManagerWrapper {
boolean handlesVolumeAdjustment,
@NonNull @CallbackExecutor Executor executor,
@NonNull AudioDeviceVolumeManager.OnAudioDeviceVolumeChangedListener vclistener);
+
+ /**
+ * Wraps {@link AudioDeviceVolumeManager#getDeviceVolumeBehavior(AudioDeviceAttributes)}
+ */
+ @AudioManager.DeviceVolumeBehavior
+ int getDeviceVolumeBehavior(@NonNull AudioDeviceAttributes device);
+
+ /**
+ * Wraps {@link AudioDeviceVolumeManager#setDeviceVolumeBehavior(AudioDeviceAttributes, int)}
+ */
+ void setDeviceVolumeBehavior(@NonNull AudioDeviceAttributes device,
+ @AudioDeviceVolumeManager.DeviceVolumeBehavior int deviceVolumeBehavior);
}
diff --git a/services/core/java/com/android/server/hdmi/AudioManagerWrapper.java b/services/core/java/com/android/server/hdmi/AudioManagerWrapper.java
index fd4dd516fd51..6d01e2da3e9d 100644
--- a/services/core/java/com/android/server/hdmi/AudioManagerWrapper.java
+++ b/services/core/java/com/android/server/hdmi/AudioManagerWrapper.java
@@ -85,18 +85,6 @@ public interface AudioManagerWrapper {
void setWiredDeviceConnectionState(int device, int state, String address, String name);
/**
- * Wraps {@link AudioManager#getDeviceVolumeBehavior(AudioDeviceAttributes)}
- */
- @AudioManager.DeviceVolumeBehavior
- int getDeviceVolumeBehavior(@NonNull AudioDeviceAttributes device);
-
- /**
- * Wraps {@link AudioManager#setDeviceVolumeBehavior(AudioDeviceAttributes, int)}
- */
- void setDeviceVolumeBehavior(@NonNull AudioDeviceAttributes device,
- @AudioManager.DeviceVolumeBehavior int deviceVolumeBehavior);
-
- /**
* Wraps {@link AudioManager#getDevicesForAttributes(AudioAttributes)}
*/
@NonNull
diff --git a/services/core/java/com/android/server/hdmi/DefaultAudioDeviceVolumeManagerWrapper.java b/services/core/java/com/android/server/hdmi/DefaultAudioDeviceVolumeManagerWrapper.java
index 10cbb00d2398..02d8579f738f 100644
--- a/services/core/java/com/android/server/hdmi/DefaultAudioDeviceVolumeManagerWrapper.java
+++ b/services/core/java/com/android/server/hdmi/DefaultAudioDeviceVolumeManagerWrapper.java
@@ -16,11 +16,14 @@
package com.android.server.hdmi;
+import static android.media.audio.Flags.unifyAbsoluteVolumeManagement;
+
import android.annotation.CallbackExecutor;
import android.annotation.NonNull;
import android.content.Context;
import android.media.AudioDeviceAttributes;
import android.media.AudioDeviceVolumeManager;
+import android.media.AudioManager;
import android.media.VolumeInfo;
import java.util.concurrent.Executor;
@@ -38,9 +41,11 @@ public class DefaultAudioDeviceVolumeManagerWrapper
private static final String TAG = "AudioDeviceVolumeManagerWrapper";
private final AudioDeviceVolumeManager mAudioDeviceVolumeManager;
+ private final AudioManager mAudioManager;
public DefaultAudioDeviceVolumeManagerWrapper(Context context) {
mAudioDeviceVolumeManager = new AudioDeviceVolumeManager(context);
+ mAudioManager = context.getSystemService(AudioManager.class);
}
@Override
@@ -78,4 +83,24 @@ public class DefaultAudioDeviceVolumeManagerWrapper
mAudioDeviceVolumeManager.setDeviceAbsoluteVolumeAdjustOnlyBehavior(device, volume,
handlesVolumeAdjustment, executor, vclistener);
}
+
+ @Override
+ @AudioDeviceVolumeManager.DeviceVolumeBehavior
+ public int getDeviceVolumeBehavior(@NonNull AudioDeviceAttributes device) {
+ if (!unifyAbsoluteVolumeManagement()) {
+ int deviceBehavior = mAudioManager.getDeviceVolumeBehavior(device);
+ return deviceBehavior;
+ }
+ return mAudioDeviceVolumeManager.getDeviceVolumeBehavior(device);
+ }
+
+ @Override
+ public void setDeviceVolumeBehavior(@NonNull AudioDeviceAttributes device,
+ @AudioDeviceVolumeManager.DeviceVolumeBehavior int deviceVolumeBehavior) {
+ if (!unifyAbsoluteVolumeManagement()) {
+ int deviceBehavior = deviceVolumeBehavior;
+ mAudioManager.setDeviceVolumeBehavior(device, deviceBehavior);
+ }
+ mAudioDeviceVolumeManager.setDeviceVolumeBehavior(device, deviceVolumeBehavior);
+ }
}
diff --git a/services/core/java/com/android/server/hdmi/DefaultAudioManagerWrapper.java b/services/core/java/com/android/server/hdmi/DefaultAudioManagerWrapper.java
index 061e145c27f3..662715420ec6 100644
--- a/services/core/java/com/android/server/hdmi/DefaultAudioManagerWrapper.java
+++ b/services/core/java/com/android/server/hdmi/DefaultAudioManagerWrapper.java
@@ -94,18 +94,6 @@ public class DefaultAudioManagerWrapper implements AudioManagerWrapper {
}
@Override
- @AudioManager.DeviceVolumeBehavior
- public int getDeviceVolumeBehavior(@NonNull AudioDeviceAttributes device) {
- return mAudioManager.getDeviceVolumeBehavior(device);
- }
-
- @Override
- public void setDeviceVolumeBehavior(@NonNull AudioDeviceAttributes device,
- @AudioManager.DeviceVolumeBehavior int deviceVolumeBehavior) {
- mAudioManager.setDeviceVolumeBehavior(device, deviceVolumeBehavior);
- }
-
- @Override
@NonNull
public List<AudioDeviceAttributes> getDevicesForAttributes(
@NonNull AudioAttributes attributes) {
diff --git a/services/core/java/com/android/server/hdmi/DeviceSelectActionFromTv.java b/services/core/java/com/android/server/hdmi/DeviceSelectActionFromTv.java
index 9118c46e3b22..574e484edd16 100644
--- a/services/core/java/com/android/server/hdmi/DeviceSelectActionFromTv.java
+++ b/services/core/java/com/android/server/hdmi/DeviceSelectActionFromTv.java
@@ -167,7 +167,11 @@ final class DeviceSelectActionFromTv extends HdmiCecFeatureAction {
private boolean handleReportPowerStatus(int powerStatus) {
switch (powerStatus) {
case HdmiControlManager.POWER_STATUS_ON:
- selectDevice();
+ if (tv().getActiveSource().physicalAddress == mTarget.getPhysicalAddress()) {
+ finishWithCallback(HdmiControlManager.RESULT_SUCCESS);
+ } else {
+ selectDevice();
+ }
return true;
case HdmiControlManager.POWER_STATUS_TRANSIENT_TO_STANDBY:
if (mPowerStatusCounter < 4) {
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
index 60bf4c10ff45..32a61fa2c356 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
@@ -929,8 +929,8 @@ public class HdmiCecLocalDeviceTv extends HdmiCecLocalDevice {
//Ensure mSupportedSads is empty before fetching SADs
synchronized (mLock) {
mSupportedSads.clear();
+ notifyArcStatusToAudioService(true, mSupportedSads);
}
- notifyArcStatusToAudioService(true, mSupportedSads);
mArcEstablished = true;
// Avoid triggering duplicate RequestSadAction events.
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index fdd0ef2f90e1..a2d065400045 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -252,17 +252,22 @@ public class HdmiControlService extends SystemService {
static final AudioDeviceAttributes AUDIO_OUTPUT_DEVICE_HDMI_EARC =
new AudioDeviceAttributes(AudioDeviceAttributes.ROLE_OUTPUT,
AudioDeviceInfo.TYPE_HDMI_EARC, "");
+ static final AudioDeviceAttributes AUDIO_OUTPUT_DEVICE_LINE_DIGITAL =
+ new AudioDeviceAttributes(AudioDeviceAttributes.ROLE_OUTPUT,
+ AudioDeviceInfo.TYPE_LINE_DIGITAL, "");
// Audio output devices used for absolute volume behavior
private static final List<AudioDeviceAttributes> AVB_AUDIO_OUTPUT_DEVICES =
List.of(AUDIO_OUTPUT_DEVICE_HDMI,
AUDIO_OUTPUT_DEVICE_HDMI_ARC,
- AUDIO_OUTPUT_DEVICE_HDMI_EARC);
+ AUDIO_OUTPUT_DEVICE_HDMI_EARC,
+ AUDIO_OUTPUT_DEVICE_LINE_DIGITAL);
// Audio output devices used for absolute volume behavior on TV panels
private static final List<AudioDeviceAttributes> TV_AVB_AUDIO_OUTPUT_DEVICES =
List.of(AUDIO_OUTPUT_DEVICE_HDMI_ARC,
- AUDIO_OUTPUT_DEVICE_HDMI_EARC);
+ AUDIO_OUTPUT_DEVICE_HDMI_EARC,
+ AUDIO_OUTPUT_DEVICE_LINE_DIGITAL);
// Audio output devices used for absolute volume behavior on Playback devices
private static final List<AudioDeviceAttributes> PLAYBACK_AVB_AUDIO_OUTPUT_DEVICES =
@@ -4595,7 +4600,7 @@ public class HdmiControlService extends SystemService {
* Wrapper for {@link AudioManager#getDeviceVolumeBehavior} that takes advantage of cached
* results for the volume behaviors of HDMI audio devices.
*/
- @AudioManager.DeviceVolumeBehavior
+ @AudioDeviceVolumeManager.DeviceVolumeBehavior
private int getDeviceVolumeBehavior(AudioDeviceAttributes device) {
if (AVB_AUDIO_OUTPUT_DEVICES.contains(device)) {
synchronized (mLock) {
@@ -4604,7 +4609,7 @@ public class HdmiControlService extends SystemService {
}
}
}
- return getAudioManager().getDeviceVolumeBehavior(device);
+ return getAudioDeviceVolumeManager().getDeviceVolumeBehavior(device);
}
/**
@@ -4695,7 +4700,7 @@ public class HdmiControlService extends SystemService {
// Condition 3: All AVB-capable audio outputs already use full/absolute volume behavior
// We only need to check the first AVB-capable audio output because only TV panels
// have more than one of them, and they always have the same volume behavior.
- @AudioManager.DeviceVolumeBehavior int currentVolumeBehavior =
+ @AudioDeviceVolumeManager.DeviceVolumeBehavior int currentVolumeBehavior =
getDeviceVolumeBehavior(getAvbCapableAudioOutputDevices().get(0));
boolean alreadyUsingFullOrAbsoluteVolume =
FULL_AND_ABSOLUTE_VOLUME_BEHAVIORS.contains(currentVolumeBehavior);
@@ -4719,7 +4724,8 @@ public class HdmiControlService extends SystemService {
// Condition 5: The System Audio device supports <Set Audio Volume Level>
switch (systemAudioDeviceInfo.getDeviceFeatures().getSetAudioVolumeLevelSupport()) {
case DeviceFeatures.FEATURE_SUPPORTED:
- if (currentVolumeBehavior != AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE) {
+ if (currentVolumeBehavior
+ != AudioDeviceVolumeManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE) {
// Start an action that will call enableAbsoluteVolumeBehavior
// once the System Audio device sends <Report Audio Status>
localCecDevice.startNewAvbAudioStatusAction(
@@ -4731,13 +4737,15 @@ public class HdmiControlService extends SystemService {
// This allows the device to display numeric volume UI for the System Audio device.
if (tv() != null && mNumericSoundbarVolumeUiOnTvFeatureFlagEnabled) {
if (currentVolumeBehavior
- != AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_ADJUST_ONLY) {
+ != AudioDeviceVolumeManager
+ .DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_ADJUST_ONLY) {
// If we're currently using absolute volume behavior, switch to full volume
// behavior until we successfully adopt adjust-only absolute volume behavior
- if (currentVolumeBehavior == AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE) {
+ if (currentVolumeBehavior
+ == AudioDeviceVolumeManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE) {
for (AudioDeviceAttributes device : getAvbCapableAudioOutputDevices()) {
- getAudioManager().setDeviceVolumeBehavior(device,
- AudioManager.DEVICE_VOLUME_BEHAVIOR_FULL);
+ getAudioDeviceVolumeManager().setDeviceVolumeBehavior(device,
+ AudioDeviceVolumeManager.DEVICE_VOLUME_BEHAVIOR_FULL);
}
}
// Start an action that will call enableAbsoluteVolumeBehavior
@@ -4750,7 +4758,8 @@ public class HdmiControlService extends SystemService {
}
return;
case DeviceFeatures.FEATURE_SUPPORT_UNKNOWN:
- if (currentVolumeBehavior == AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE) {
+ if (currentVolumeBehavior
+ == AudioDeviceVolumeManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE) {
switchToFullVolumeBehavior();
}
localCecDevice.querySetAudioVolumeLevelSupport(
@@ -4773,8 +4782,8 @@ public class HdmiControlService extends SystemService {
for (AudioDeviceAttributes device : getAvbCapableAudioOutputDevices()) {
if (ABSOLUTE_VOLUME_BEHAVIORS.contains(getDeviceVolumeBehavior(device))) {
- getAudioManager().setDeviceVolumeBehavior(device,
- AudioManager.DEVICE_VOLUME_BEHAVIOR_FULL);
+ getAudioDeviceVolumeManager().setDeviceVolumeBehavior(device,
+ AudioDeviceVolumeManager.DEVICE_VOLUME_BEHAVIOR_FULL);
}
}
}
diff --git a/services/core/java/com/android/server/input/AppLaunchShortcutManager.java b/services/core/java/com/android/server/input/AppLaunchShortcutManager.java
index 8c028bc92841..eb102294ac32 100644
--- a/services/core/java/com/android/server/input/AppLaunchShortcutManager.java
+++ b/services/core/java/com/android/server/input/AppLaunchShortcutManager.java
@@ -111,7 +111,7 @@ final class AppLaunchShortcutManager {
mContext = context;
}
- public void systemRunning() {
+ public void init() {
loadShortcuts();
}
diff --git a/services/core/java/com/android/server/input/InputGestureManager.java b/services/core/java/com/android/server/input/InputGestureManager.java
index 67e1ccc6a850..e6d71900f106 100644
--- a/services/core/java/com/android/server/input/InputGestureManager.java
+++ b/services/core/java/com/android/server/input/InputGestureManager.java
@@ -94,9 +94,9 @@ final class InputGestureManager {
mContext = context;
}
- public void systemRunning() {
+ public void init(List<InputGestureData> bookmarks) {
initSystemShortcuts();
- blockListBookmarkedTriggers();
+ blockListBookmarkedTriggers(bookmarks);
}
private void initSystemShortcuts() {
@@ -263,10 +263,9 @@ final class InputGestureManager {
}
}
- private void blockListBookmarkedTriggers() {
+ private void blockListBookmarkedTriggers(List<InputGestureData> bookmarks) {
synchronized (mGestureLock) {
- InputManager im = Objects.requireNonNull(mContext.getSystemService(InputManager.class));
- for (InputGestureData bookmark : im.getAppLaunchBookmarks()) {
+ for (InputGestureData bookmark : bookmarks) {
mBlockListedTriggers.add(bookmark.getTrigger());
}
}
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index d9db178e0dc2..6af55300d0b3 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -25,6 +25,7 @@ import static android.view.KeyEvent.KEYCODE_UNKNOWN;
import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
import static com.android.hardware.input.Flags.enableCustomizableInputGestures;
+import static com.android.hardware.input.Flags.fixSearchModifierFallbacks;
import static com.android.hardware.input.Flags.keyEventActivityDetection;
import static com.android.hardware.input.Flags.touchpadVisualizer;
import static com.android.hardware.input.Flags.useKeyGestureEventHandler;
@@ -750,7 +751,8 @@ public class InputManagerService extends IInputManager.Stub
* @return True if the lookup was successful, false otherwise.
*/
@Override // Binder call
- public boolean hasKeys(int deviceId, int sourceMask, int[] keyCodes, boolean[] keyExists) {
+ public boolean hasKeys(int deviceId, int sourceMask, @NonNull int[] keyCodes,
+ @NonNull boolean[] keyExists) {
Objects.requireNonNull(keyCodes, "keyCodes must not be null");
Objects.requireNonNull(keyExists, "keyExists must not be null");
if (keyExists.length < keyCodes.length) {
@@ -791,7 +793,7 @@ public class InputManagerService extends IInputManager.Stub
* @deprecated Use {@link #transferTouchGesture(IBinder, IBinder)}
*/
@Deprecated
- public boolean transferTouch(IBinder destChannelToken, int displayId) {
+ public boolean transferTouch(@NonNull IBinder destChannelToken, int displayId) {
// TODO(b/162194035): Replace this with a SPY window
Objects.requireNonNull(destChannelToken, "destChannelToken must not be null");
return mNative.transferTouch(destChannelToken, displayId);
@@ -803,7 +805,7 @@ public class InputManagerService extends IInputManager.Stub
* @param displayId Target display id.
* @return The input channel.
*/
- public InputChannel monitorInput(String inputChannelName, int displayId) {
+ public InputChannel monitorInput(@NonNull String inputChannelName, int displayId) {
Objects.requireNonNull(inputChannelName, "inputChannelName not be null");
if (displayId < Display.DEFAULT_DISPLAY) {
@@ -835,7 +837,7 @@ public class InputManagerService extends IInputManager.Stub
return outInputChannel;
}
- private void removeSpyWindowGestureMonitor(IBinder inputChannelToken) {
+ private void removeSpyWindowGestureMonitor(@NonNull IBinder inputChannelToken) {
final GestureMonitorSpyWindow monitor;
synchronized (mInputMonitors) {
monitor = mInputMonitors.remove(inputChannelToken);
@@ -854,8 +856,8 @@ public class InputManagerService extends IInputManager.Stub
* @return The input channel.
*/
@Override // Binder call
- public InputMonitor monitorGestureInput(IBinder monitorToken, @NonNull String requestedName,
- int displayId) {
+ public InputMonitor monitorGestureInput(@NonNull IBinder monitorToken,
+ @NonNull String requestedName, int displayId) {
if (!checkCallingPermission(android.Manifest.permission.MONITOR_INPUT,
"monitorGestureInput()")) {
throw new SecurityException("Requires MONITOR_INPUT permission");
@@ -902,7 +904,7 @@ public class InputManagerService extends IInputManager.Stub
* Removes an input channel.
* @param connectionToken The input channel to unregister.
*/
- public void removeInputChannel(IBinder connectionToken) {
+ public void removeInputChannel(@NonNull IBinder connectionToken) {
Objects.requireNonNull(connectionToken, "connectionToken must not be null");
mNative.removeInputChannel(connectionToken);
}
@@ -977,12 +979,12 @@ public class InputManagerService extends IInputManager.Stub
}
@Override // Binder call
- public boolean injectInputEvent(InputEvent event, int mode) {
+ public boolean injectInputEvent(@NonNull InputEvent event, int mode) {
return injectInputEventToTarget(event, mode, Process.INVALID_UID);
}
@Override // Binder call
- public boolean injectInputEventToTarget(InputEvent event, int mode, int targetUid) {
+ public boolean injectInputEventToTarget(@NonNull InputEvent event, int mode, int targetUid) {
if (!checkCallingPermission(android.Manifest.permission.INJECT_EVENTS,
"injectInputEvent()", true /*checkInstrumentationSource*/)) {
throw new SecurityException(
@@ -1032,7 +1034,7 @@ public class InputManagerService extends IInputManager.Stub
}
@Override // Binder call
- public VerifiedInputEvent verifyInputEvent(InputEvent event) {
+ public VerifiedInputEvent verifyInputEvent(@NonNull InputEvent event) {
Objects.requireNonNull(event, "event must not be null");
return mNative.verifyInputEvent(event);
}
@@ -1106,7 +1108,8 @@ public class InputManagerService extends IInputManager.Stub
}
@Override // Binder call
- public void registerInputDevicesChangedListener(IInputDevicesChangedListener listener) {
+ public void registerInputDevicesChangedListener(
+ @NonNull IInputDevicesChangedListener listener) {
Objects.requireNonNull(listener, "listener must not be null");
synchronized (mInputDevicesLock) {
@@ -1176,7 +1179,7 @@ public class InputManagerService extends IInputManager.Stub
}
@Override // Binder call & native callback
- public TouchCalibration getTouchCalibrationForInputDevice(String inputDeviceDescriptor,
+ public TouchCalibration getTouchCalibrationForInputDevice(@NonNull String inputDeviceDescriptor,
int surfaceRotation) {
Objects.requireNonNull(inputDeviceDescriptor, "inputDeviceDescriptor must not be null");
@@ -1186,8 +1189,8 @@ public class InputManagerService extends IInputManager.Stub
}
@Override // Binder call
- public void setTouchCalibrationForInputDevice(String inputDeviceDescriptor, int surfaceRotation,
- TouchCalibration calibration) {
+ public void setTouchCalibrationForInputDevice(@NonNull String inputDeviceDescriptor,
+ int surfaceRotation, @NonNull TouchCalibration calibration) {
if (!checkCallingPermission(android.Manifest.permission.SET_INPUT_CALIBRATION,
"setTouchCalibrationForInputDevice()")) {
throw new SecurityException("Requires SET_INPUT_CALIBRATION permission");
@@ -1225,12 +1228,12 @@ public class InputManagerService extends IInputManager.Stub
}
@Override // Binder call
- public void registerTabletModeChangedListener(ITabletModeChangedListener listener) {
+ public void registerTabletModeChangedListener(@NonNull ITabletModeChangedListener listener) {
if (!checkCallingPermission(android.Manifest.permission.TABLET_MODE,
"registerTabletModeChangedListener()")) {
throw new SecurityException("Requires TABLET_MODE_LISTENER permission");
}
- Objects.requireNonNull(listener, "event must not be null");
+ Objects.requireNonNull(listener, "listener must not be null");
synchronized (mTabletModeLock) {
final int callingPid = Binder.getCallingPid();
@@ -1341,8 +1344,8 @@ public class InputManagerService extends IInputManager.Stub
}
@Override
- public void requestPointerCapture(IBinder inputChannelToken, boolean enabled) {
- Objects.requireNonNull(inputChannelToken, "event must not be null");
+ public void requestPointerCapture(@NonNull IBinder inputChannelToken, boolean enabled) {
+ Objects.requireNonNull(inputChannelToken, "inputChannelToken must not be null");
mNative.requestPointerCapture(inputChannelToken, enabled);
}
@@ -1664,7 +1667,8 @@ public class InputManagerService extends IInputManager.Stub
}
@Override // Binder call
- public boolean registerVibratorStateListener(int deviceId, IVibratorStateListener listener) {
+ public boolean registerVibratorStateListener(int deviceId,
+ @NonNull IVibratorStateListener listener) {
Objects.requireNonNull(listener, "listener must not be null");
RemoteCallbackList<IVibratorStateListener> listeners;
@@ -1717,8 +1721,8 @@ public class InputManagerService extends IInputManager.Stub
// Binder call
@Override
- public boolean setPointerIcon(PointerIcon icon, int displayId, int deviceId, int pointerId,
- IBinder inputToken) {
+ public boolean setPointerIcon(@NonNull PointerIcon icon, int displayId, int deviceId,
+ int pointerId, IBinder inputToken) {
Objects.requireNonNull(icon);
return mNative.setPointerIcon(icon, displayId, deviceId, pointerId, inputToken);
}
@@ -1896,7 +1900,7 @@ public class InputManagerService extends IInputManager.Stub
}
@Override // Binder call
- public boolean registerSensorListener(IInputSensorEventListener listener) {
+ public boolean registerSensorListener(@NonNull IInputSensorEventListener listener) {
if (DEBUG) {
Slog.d(TAG, "registerSensorListener: listener=" + listener + " callingPid="
+ Binder.getCallingPid());
@@ -1927,7 +1931,7 @@ public class InputManagerService extends IInputManager.Stub
}
@Override // Binder call
- public void unregisterSensorListener(IInputSensorEventListener listener) {
+ public void unregisterSensorListener(@NonNull IInputSensorEventListener listener) {
if (DEBUG) {
Slog.d(TAG, "unregisterSensorListener: listener=" + listener + " callingPid="
+ Binder.getCallingPid());
@@ -2016,7 +2020,8 @@ public class InputManagerService extends IInputManager.Stub
/**
* Set specified light state with for a specific input device.
*/
- private void setLightStateInternal(int deviceId, Light light, LightState lightState) {
+ private void setLightStateInternal(int deviceId, @NonNull Light light,
+ @NonNull LightState lightState) {
Objects.requireNonNull(light, "light does not exist");
if (DEBUG) {
Slog.d(TAG, "setLightStateInternal device " + deviceId + " light " + light
@@ -2079,7 +2084,7 @@ public class InputManagerService extends IInputManager.Stub
}
@Override
- public void openLightSession(int deviceId, String opPkg, IBinder token) {
+ public void openLightSession(int deviceId, String opPkg, @NonNull IBinder token) {
Objects.requireNonNull(token);
synchronized (mLightLock) {
Preconditions.checkState(mLightSessions.get(token) == null, "already registered");
@@ -2098,7 +2103,7 @@ public class InputManagerService extends IInputManager.Stub
}
@Override
- public void closeLightSession(int deviceId, IBinder token) {
+ public void closeLightSession(int deviceId, @NonNull IBinder token) {
Objects.requireNonNull(token);
synchronized (mLightLock) {
LightSession lightSession = mLightSessions.get(token);
@@ -2128,13 +2133,15 @@ public class InputManagerService extends IInputManager.Stub
}
@Override
- public void registerBatteryListener(int deviceId, IInputDeviceBatteryListener listener) {
+ public void registerBatteryListener(int deviceId,
+ @NonNull IInputDeviceBatteryListener listener) {
Objects.requireNonNull(listener);
mBatteryController.registerBatteryListener(deviceId, listener, Binder.getCallingPid());
}
@Override
- public void unregisterBatteryListener(int deviceId, IInputDeviceBatteryListener listener) {
+ public void unregisterBatteryListener(int deviceId,
+ @NonNull IInputDeviceBatteryListener listener) {
Objects.requireNonNull(listener);
mBatteryController.unregisterBatteryListener(deviceId, listener, Binder.getCallingPid());
}
@@ -2155,7 +2162,7 @@ public class InputManagerService extends IInputManager.Stub
@EnforcePermission(Manifest.permission.MONITOR_INPUT)
@Override
- public void pilferPointers(IBinder inputChannelToken) {
+ public void pilferPointers(@NonNull IBinder inputChannelToken) {
super.pilferPointers_enforcePermission();
Objects.requireNonNull(inputChannelToken);
@@ -2164,7 +2171,7 @@ public class InputManagerService extends IInputManager.Stub
@Override
@EnforcePermission(Manifest.permission.MONITOR_KEYBOARD_BACKLIGHT)
- public void registerKeyboardBacklightListener(IKeyboardBacklightListener listener) {
+ public void registerKeyboardBacklightListener(@NonNull IKeyboardBacklightListener listener) {
super.registerKeyboardBacklightListener_enforcePermission();
Objects.requireNonNull(listener);
mKeyboardBacklightController.registerKeyboardBacklightListener(listener,
@@ -2173,7 +2180,7 @@ public class InputManagerService extends IInputManager.Stub
@Override
@EnforcePermission(Manifest.permission.MONITOR_KEYBOARD_BACKLIGHT)
- public void unregisterKeyboardBacklightListener(IKeyboardBacklightListener listener) {
+ public void unregisterKeyboardBacklightListener(@NonNull IKeyboardBacklightListener listener) {
super.unregisterKeyboardBacklightListener_enforcePermission();
Objects.requireNonNull(listener);
mKeyboardBacklightController.unregisterKeyboardBacklightListener(listener,
@@ -2653,6 +2660,8 @@ public class InputManagerService extends IInputManager.Stub
@SuppressWarnings("unused")
@VisibleForTesting
long interceptKeyBeforeDispatching(IBinder focus, KeyEvent event, int policyFlags) {
+ final long keyNotConsumedGoFallback = -2;
+ final long keyConsumed = -1;
final long keyNotConsumed = 0;
long value = keyNotConsumed;
// TODO(b/358569822) Remove below once we have nicer API for listening to shortcuts
@@ -2667,6 +2676,16 @@ public class InputManagerService extends IInputManager.Stub
value = mWindowManagerCallbacks.interceptKeyBeforeDispatching(focus, event,
policyFlags);
}
+ if (fixSearchModifierFallbacks() && value == keyNotConsumed && event.isMetaPressed()) {
+ // If the key has not been consumed and includes the meta key, do not send the event
+ // to the app and attempt to generate a fallback.
+ final KeyCharacterMap kcm = event.getKeyCharacterMap();
+ final KeyCharacterMap.FallbackAction fallbackAction =
+ kcm.getFallbackAction(event.getKeyCode(), event.getMetaState());
+ if (fallbackAction != null) {
+ return keyNotConsumedGoFallback;
+ }
+ }
return value;
}
@@ -2751,18 +2770,23 @@ public class InputManagerService extends IInputManager.Stub
@SuppressLint("MissingPermission")
private void initKeyGestures() {
InputManager im = Objects.requireNonNull(mContext.getSystemService(InputManager.class));
- im.registerKeyGestureEventHandler(new InputManager.KeyGestureEventHandler() {
- @Override
- public boolean handleKeyGestureEvent(@NonNull KeyGestureEvent event,
- @Nullable IBinder focussedToken) {
- return InputManagerService.this.handleKeyGestureEvent(event);
- }
- });
+ List<Integer> supportedGestures = List.of(
+ KeyGestureEvent.KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_UP,
+ KeyGestureEvent.KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_DOWN,
+ KeyGestureEvent.KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_TOGGLE,
+ KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_CAPS_LOCK,
+ KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_BOUNCE_KEYS,
+ KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_MOUSE_KEYS,
+ KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_STICKY_KEYS,
+ KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_SLOW_KEYS
+ );
+ im.registerKeyGestureEventHandler(supportedGestures,
+ (event, focusedToken) -> InputManagerService.this.handleKeyGestureEvent(event));
}
@SuppressLint("MissingPermission")
@VisibleForTesting
- boolean handleKeyGestureEvent(@NonNull KeyGestureEvent event) {
+ void handleKeyGestureEvent(@NonNull KeyGestureEvent event) {
int deviceId = event.getDeviceId();
boolean complete = event.getAction() == KeyGestureEvent.ACTION_GESTURE_COMPLETE
&& !event.isCancelled();
@@ -2771,20 +2795,20 @@ public class InputManagerService extends IInputManager.Stub
if (complete) {
mKeyboardBacklightController.incrementKeyboardBacklight(deviceId);
}
- return true;
+ break;
case KeyGestureEvent.KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_DOWN:
if (complete) {
mKeyboardBacklightController.decrementKeyboardBacklight(deviceId);
}
- return true;
+ break;
case KeyGestureEvent.KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_TOGGLE:
// TODO(b/367748270): Add functionality to turn keyboard backlight on/off.
- return true;
+ break;
case KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_CAPS_LOCK:
if (complete) {
mNative.toggleCapsLock(deviceId);
}
- return true;
+ break;
case KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_BOUNCE_KEYS:
if (complete) {
final boolean bounceKeysEnabled =
@@ -2792,7 +2816,6 @@ public class InputManagerService extends IInputManager.Stub
InputSettings.setAccessibilityBounceKeysThreshold(mContext,
bounceKeysEnabled ? 0
: InputSettings.DEFAULT_BOUNCE_KEYS_THRESHOLD_MILLIS);
- return true;
}
break;
case KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_MOUSE_KEYS:
@@ -2800,7 +2823,6 @@ public class InputManagerService extends IInputManager.Stub
final boolean mouseKeysEnabled = InputSettings.isAccessibilityMouseKeysEnabled(
mContext);
InputSettings.setAccessibilityMouseKeysEnabled(mContext, !mouseKeysEnabled);
- return true;
}
break;
case KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_STICKY_KEYS:
@@ -2808,7 +2830,6 @@ public class InputManagerService extends IInputManager.Stub
final boolean stickyKeysEnabled =
InputSettings.isAccessibilityStickyKeysEnabled(mContext);
InputSettings.setAccessibilityStickyKeysEnabled(mContext, !stickyKeysEnabled);
- return true;
}
break;
case KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_SLOW_KEYS:
@@ -2817,14 +2838,13 @@ public class InputManagerService extends IInputManager.Stub
InputSettings.isAccessibilitySlowKeysEnabled(mContext);
InputSettings.setAccessibilitySlowKeysThreshold(mContext,
slowKeysEnabled ? 0 : InputSettings.DEFAULT_SLOW_KEYS_THRESHOLD_MILLIS);
- return true;
}
break;
default:
- return false;
-
+ Log.w(TAG, "Received a key gesture " + event
+ + " that was not registered by this handler");
+ break;
}
- return false;
}
// Native callback.
@@ -3147,11 +3167,14 @@ public class InputManagerService extends IInputManager.Stub
@Override
@PermissionManuallyEnforced
- public void registerKeyGestureHandler(@NonNull IKeyGestureHandler handler) {
+ public void registerKeyGestureHandler(int[] keyGesturesToHandle,
+ @NonNull IKeyGestureHandler handler) {
enforceManageKeyGesturePermission();
Objects.requireNonNull(handler);
- mKeyGestureController.registerKeyGestureHandler(handler, Binder.getCallingPid());
+ Objects.requireNonNull(keyGesturesToHandle);
+ mKeyGestureController.registerKeyGestureHandler(keyGesturesToHandle, handler,
+ Binder.getCallingPid());
}
@Override
@@ -3306,9 +3329,10 @@ public class InputManagerService extends IInputManager.Stub
* @param token the window token that's about to receive this event
* @param event the key event that's being dispatched
* @param policyFlags the policy flags
- * @return negative value if the key should be skipped (not sent to the app). 0 if the key
- * should proceed getting dispatched to the app. positive value to indicate the additional
- * time delay, in nanoseconds, to wait before sending this key to the app.
+ * @return -1 if the key should be skipped (not sent to the app). -2 if the key should not
+ * be sent to the app, but it should still generate a fallback.
+ * 0 if the key should proceed getting dispatched to the app. positive value to indicate the
+ * additional time delay, in nanoseconds, to wait before sending this key to the app.
*/
long interceptKeyBeforeDispatching(IBinder token, KeyEvent event, int policyFlags);
@@ -3425,7 +3449,7 @@ public class InputManagerService extends IInputManager.Stub
}
@Override
- public void sendInputEvent(InputEvent event, int policyFlags) {
+ public void sendInputEvent(@NonNull InputEvent event, int policyFlags) {
if (!checkCallingPermission(android.Manifest.permission.INJECT_EVENTS,
"sendInputEvent()")) {
throw new SecurityException(
@@ -3447,9 +3471,9 @@ public class InputManagerService extends IInputManager.Stub
* Interface for the system to handle request from InputMonitors.
*/
private final class InputMonitorHost extends IInputMonitorHost.Stub {
- private final IBinder mInputChannelToken;
+ private final @NonNull IBinder mInputChannelToken;
- InputMonitorHost(IBinder inputChannelToken) {
+ InputMonitorHost(@NonNull IBinder inputChannelToken) {
mInputChannelToken = inputChannelToken;
}
diff --git a/services/core/java/com/android/server/input/KeyGestureController.java b/services/core/java/com/android/server/input/KeyGestureController.java
index 395c77322c04..5de432e5849b 100644
--- a/services/core/java/com/android/server/input/KeyGestureController.java
+++ b/services/core/java/com/android/server/input/KeyGestureController.java
@@ -58,6 +58,7 @@ import android.util.IndentingPrintWriter;
import android.util.Log;
import android.util.Slog;
import android.util.SparseArray;
+import android.util.SparseIntArray;
import android.view.Display;
import android.view.InputDevice;
import android.view.KeyCharacterMap;
@@ -79,11 +80,11 @@ import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.ArrayDeque;
+import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;
-import java.util.TreeMap;
/**
* A thread-safe component of {@link InputManagerService} responsible for managing callbacks when a
@@ -166,11 +167,14 @@ final class KeyGestureController {
private final SparseArray<KeyGestureEventListenerRecord>
mKeyGestureEventListenerRecords = new SparseArray<>();
- // List of currently registered key gesture event handler keyed by process pid. The map sorts
- // in the order of preference of the handlers, and we prioritize handlers in system server
- // over external handlers..
+ // Map of currently registered key gesture event handlers keyed by pid.
@GuardedBy("mKeyGestureHandlerRecords")
- private final TreeMap<Integer, KeyGestureHandlerRecord> mKeyGestureHandlerRecords;
+ private final SparseArray<KeyGestureHandlerRecord> mKeyGestureHandlerRecords =
+ new SparseArray<>();
+
+ // Currently supported key gestures mapped to pid that registered the corresponding handler.
+ @GuardedBy("mKeyGestureHandlerRecords")
+ private final SparseIntArray mSupportedKeyGestureToPidMap = new SparseIntArray();
private final ArrayDeque<KeyGestureEvent> mLastHandledEvents = new ArrayDeque<>();
@@ -193,18 +197,6 @@ final class KeyGestureController {
mHandler = new Handler(looper, this::handleMessage);
mIoHandler = new Handler(ioLooper, this::handleIoMessage);
mSystemPid = Process.myPid();
- mKeyGestureHandlerRecords = new TreeMap<>((p1, p2) -> {
- if (Objects.equals(p1, p2)) {
- return 0;
- }
- if (p1 == mSystemPid) {
- return -1;
- } else if (p2 == mSystemPid) {
- return 1;
- } else {
- return Integer.compare(p1, p2);
- }
- });
mKeyCombinationManager = new KeyCombinationManager(mHandler);
mSettingsObserver = new SettingsObserver(mHandler);
mAppLaunchShortcutManager = new AppLaunchShortcutManager(mContext);
@@ -450,8 +442,8 @@ final class KeyGestureController {
public void systemRunning() {
mSettingsObserver.observe();
- mAppLaunchShortcutManager.systemRunning();
- mInputGestureManager.systemRunning();
+ mAppLaunchShortcutManager.init();
+ mInputGestureManager.init(mAppLaunchShortcutManager.getBookmarks());
initKeyGestures();
int userId;
@@ -465,22 +457,24 @@ final class KeyGestureController {
@SuppressLint("MissingPermission")
private void initKeyGestures() {
InputManager im = Objects.requireNonNull(mContext.getSystemService(InputManager.class));
- im.registerKeyGestureEventHandler((event, focusedToken) -> {
- switch (event.getKeyGestureType()) {
- case KeyGestureEvent.KEY_GESTURE_TYPE_ACCESSIBILITY_SHORTCUT_CHORD:
- if (event.getAction() == KeyGestureEvent.ACTION_GESTURE_START) {
- mHandler.removeMessages(MSG_ACCESSIBILITY_SHORTCUT);
- mHandler.sendMessageDelayed(
- mHandler.obtainMessage(MSG_ACCESSIBILITY_SHORTCUT),
- getAccessibilityShortcutTimeout());
+ im.registerKeyGestureEventHandler(
+ List.of(KeyGestureEvent.KEY_GESTURE_TYPE_ACCESSIBILITY_SHORTCUT_CHORD),
+ (event, focusedToken) -> {
+ if (event.getKeyGestureType()
+ == KeyGestureEvent.KEY_GESTURE_TYPE_ACCESSIBILITY_SHORTCUT_CHORD) {
+ if (event.getAction() == KeyGestureEvent.ACTION_GESTURE_START) {
+ mHandler.removeMessages(MSG_ACCESSIBILITY_SHORTCUT);
+ mHandler.sendMessageDelayed(
+ mHandler.obtainMessage(MSG_ACCESSIBILITY_SHORTCUT),
+ getAccessibilityShortcutTimeout());
+ } else {
+ mHandler.removeMessages(MSG_ACCESSIBILITY_SHORTCUT);
+ }
} else {
- mHandler.removeMessages(MSG_ACCESSIBILITY_SHORTCUT);
+ Log.w(TAG, "Received a key gesture " + event
+ + " that was not registered by this handler");
}
- return true;
- default:
- return false;
- }
- });
+ });
}
public boolean interceptKeyBeforeQueueing(KeyEvent event, int policyFlags) {
@@ -590,10 +584,11 @@ final class KeyGestureController {
return true;
}
if (result.appLaunchData() != null) {
- return handleKeyGesture(deviceId, new int[]{keyCode}, metaState,
+ handleKeyGesture(deviceId, new int[]{keyCode}, metaState,
KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION,
- KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
- focusedToken, /* flags = */0, result.appLaunchData());
+ KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId, focusedToken, /* flags = */
+ 0, result.appLaunchData());
+ return true;
}
// Handle system shortcuts
@@ -601,11 +596,11 @@ final class KeyGestureController {
InputGestureData systemShortcut = mInputGestureManager.getSystemShortcutForKeyEvent(
event);
if (systemShortcut != null) {
- return handleKeyGesture(deviceId, new int[]{keyCode}, metaState,
+ handleKeyGesture(deviceId, new int[]{keyCode}, metaState,
systemShortcut.getAction().keyGestureType(),
- KeyGestureEvent.ACTION_GESTURE_COMPLETE,
- displayId, focusedToken, /* flags = */0,
- systemShortcut.getAction().appLaunchData());
+ KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
+ focusedToken, /* flags = */0, systemShortcut.getAction().appLaunchData());
+ return true;
}
}
@@ -687,11 +682,11 @@ final class KeyGestureController {
return true;
case KeyEvent.KEYCODE_SEARCH:
if (firstDown && mSearchKeyBehavior == SEARCH_KEY_BEHAVIOR_TARGET_ACTIVITY) {
- return handleKeyGesture(deviceId, new int[]{keyCode}, /* modifierState = */0,
+ handleKeyGesture(deviceId, new int[]{keyCode}, /* modifierState = */0,
KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_SEARCH,
KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
focusedToken, /* flags = */0, /* appLaunchData = */null);
-
+ return true;
}
break;
case KeyEvent.KEYCODE_SETTINGS:
@@ -782,11 +777,12 @@ final class KeyGestureController {
if (KeyEvent.metaStateHasModifiers(
shiftlessModifiers, KeyEvent.META_ALT_ON)) {
mPendingHideRecentSwitcher = true;
- return handleKeyGesture(deviceId, new int[]{keyCode},
+ handleKeyGesture(deviceId, new int[]{keyCode},
KeyEvent.META_ALT_ON,
KeyGestureEvent.KEY_GESTURE_TYPE_RECENT_APPS_SWITCHER,
KeyGestureEvent.ACTION_GESTURE_START, displayId,
focusedToken, /* flags = */0, /* appLaunchData = */null);
+ return true;
}
}
}
@@ -803,21 +799,23 @@ final class KeyGestureController {
} else {
if (mPendingHideRecentSwitcher) {
mPendingHideRecentSwitcher = false;
- return handleKeyGesture(deviceId, new int[]{KeyEvent.KEYCODE_TAB},
+ handleKeyGesture(deviceId, new int[]{KeyEvent.KEYCODE_TAB},
KeyEvent.META_ALT_ON,
KeyGestureEvent.KEY_GESTURE_TYPE_RECENT_APPS_SWITCHER,
KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
focusedToken, /* flags = */0, /* appLaunchData = */null);
+ return true;
}
// Toggle Caps Lock on META-ALT.
if (mPendingCapsLockToggle) {
mPendingCapsLockToggle = false;
- return handleKeyGesture(deviceId, new int[]{KeyEvent.KEYCODE_META_LEFT,
+ handleKeyGesture(deviceId, new int[]{KeyEvent.KEYCODE_META_LEFT,
KeyEvent.KEYCODE_ALT_LEFT}, /* modifierState = */0,
KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_CAPS_LOCK,
KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
focusedToken, /* flags = */0, /* appLaunchData = */null);
+ return true;
}
}
break;
@@ -885,11 +883,11 @@ final class KeyGestureController {
if (customGesture == null) {
return false;
}
- return handleKeyGesture(deviceId, new int[]{keyCode}, metaState,
+ handleKeyGesture(deviceId, new int[]{keyCode}, metaState,
customGesture.getAction().keyGestureType(),
- KeyGestureEvent.ACTION_GESTURE_COMPLETE,
- displayId, focusedToken, /* flags = */0,
- customGesture.getAction().appLaunchData());
+ KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId, focusedToken,
+ /* flags = */0, customGesture.getAction().appLaunchData());
+ return true;
}
return false;
}
@@ -908,7 +906,7 @@ final class KeyGestureController {
// Handle keyboard layout switching. (CTRL + SPACE)
if (KeyEvent.metaStateHasModifiers(metaState & ~KeyEvent.META_SHIFT_MASK,
KeyEvent.META_CTRL_ON)) {
- return handleKeyGesture(deviceId, new int[]{keyCode},
+ handleKeyGesture(deviceId, new int[]{keyCode},
KeyEvent.META_CTRL_ON | (event.isShiftPressed()
? KeyEvent.META_SHIFT_ON : 0),
KeyGestureEvent.KEY_GESTURE_TYPE_LANGUAGE_SWITCH,
@@ -921,7 +919,7 @@ final class KeyGestureController {
if (down && KeyEvent.metaStateHasModifiers(metaState,
KeyEvent.META_CTRL_ON | KeyEvent.META_ALT_ON)) {
// Intercept the Accessibility keychord (CTRL + ALT + Z) for keyboard users.
- return handleKeyGesture(deviceId, new int[]{keyCode},
+ handleKeyGesture(deviceId, new int[]{keyCode},
KeyEvent.META_CTRL_ON | KeyEvent.META_ALT_ON,
KeyGestureEvent.KEY_GESTURE_TYPE_ACCESSIBILITY_SHORTCUT,
KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
@@ -930,7 +928,7 @@ final class KeyGestureController {
break;
case KeyEvent.KEYCODE_SYSRQ:
if (down && repeatCount == 0) {
- return handleKeyGesture(deviceId, new int[]{keyCode}, /* modifierState = */0,
+ handleKeyGesture(deviceId, new int[]{keyCode}, /* modifierState = */0,
KeyGestureEvent.KEY_GESTURE_TYPE_TAKE_SCREENSHOT,
KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
focusedToken, /* flags = */0, /* appLaunchData = */null);
@@ -938,7 +936,7 @@ final class KeyGestureController {
break;
case KeyEvent.KEYCODE_ESCAPE:
if (down && KeyEvent.metaStateHasNoModifiers(metaState) && repeatCount == 0) {
- return handleKeyGesture(deviceId, new int[]{keyCode}, /* modifierState = */0,
+ handleKeyGesture(deviceId, new int[]{keyCode}, /* modifierState = */0,
KeyGestureEvent.KEY_GESTURE_TYPE_CLOSE_ALL_DIALOGS,
KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
focusedToken, /* flags = */0, /* appLaunchData = */null);
@@ -964,29 +962,31 @@ final class KeyGestureController {
}
@VisibleForTesting
- boolean handleKeyGesture(int deviceId, int[] keycodes, int modifierState,
+ void handleKeyGesture(int deviceId, int[] keycodes, int modifierState,
@KeyGestureEvent.KeyGestureType int gestureType, int action, int displayId,
@Nullable IBinder focusedToken, int flags, @Nullable AppLaunchData appLaunchData) {
- return handleKeyGesture(createKeyGestureEvent(deviceId, keycodes,
- modifierState, gestureType, action, displayId, flags, appLaunchData), focusedToken);
+ handleKeyGesture(
+ createKeyGestureEvent(deviceId, keycodes, modifierState, gestureType, action,
+ displayId, flags, appLaunchData), focusedToken);
}
- private boolean handleKeyGesture(AidlKeyGestureEvent event, @Nullable IBinder focusedToken) {
+ private void handleKeyGesture(AidlKeyGestureEvent event, @Nullable IBinder focusedToken) {
if (mVisibleBackgroundUsersEnabled && event.displayId != DEFAULT_DISPLAY
&& shouldIgnoreGestureEventForVisibleBackgroundUser(event.gestureType,
event.displayId)) {
- return false;
+ return;
}
synchronized (mKeyGestureHandlerRecords) {
- for (KeyGestureHandlerRecord handler : mKeyGestureHandlerRecords.values()) {
- if (handler.handleKeyGesture(event, focusedToken)) {
- Message msg = Message.obtain(mHandler, MSG_NOTIFY_KEY_GESTURE_EVENT, event);
- mHandler.sendMessage(msg);
- return true;
- }
+ int index = mSupportedKeyGestureToPidMap.indexOfKey(event.gestureType);
+ if (index < 0) {
+ Log.i(TAG, "Key gesture: " + event.gestureType + " is not supported");
+ return;
}
+ int pid = mSupportedKeyGestureToPidMap.valueAt(index);
+ mKeyGestureHandlerRecords.get(pid).handleKeyGesture(event, focusedToken);
+ Message msg = Message.obtain(mHandler, MSG_NOTIFY_KEY_GESTURE_EVENT, event);
+ mHandler.sendMessage(msg);
}
- return false;
}
private boolean shouldIgnoreGestureEventForVisibleBackgroundUser(
@@ -1285,12 +1285,23 @@ final class KeyGestureController {
/** Register the key gesture event handler for a process. */
@BinderThread
- public void registerKeyGestureHandler(IKeyGestureHandler handler, int pid) {
+ public void registerKeyGestureHandler(int[] keyGesturesToHandle, IKeyGestureHandler handler,
+ int pid) {
synchronized (mKeyGestureHandlerRecords) {
if (mKeyGestureHandlerRecords.get(pid) != null) {
throw new IllegalStateException("The calling process has already registered "
+ "a KeyGestureHandler.");
}
+ if (keyGesturesToHandle.length == 0) {
+ throw new IllegalArgumentException("No key gestures provided for pid = " + pid);
+ }
+ for (int gestureType : keyGesturesToHandle) {
+ if (mSupportedKeyGestureToPidMap.indexOfKey(gestureType) >= 0) {
+ throw new IllegalArgumentException(
+ "Key gesture " + gestureType + " is already registered by pid = "
+ + mSupportedKeyGestureToPidMap.get(gestureType));
+ }
+ }
KeyGestureHandlerRecord record = new KeyGestureHandlerRecord(pid, handler);
try {
handler.asBinder().linkToDeath(record, 0);
@@ -1298,6 +1309,9 @@ final class KeyGestureController {
throw new RuntimeException(ex);
}
mKeyGestureHandlerRecords.put(pid, record);
+ for (int gestureType : keyGesturesToHandle) {
+ mSupportedKeyGestureToPidMap.put(gestureType, pid);
+ }
}
}
@@ -1315,7 +1329,7 @@ final class KeyGestureController {
+ "KeyGestureHandler.");
}
record.mKeyGestureHandler.asBinder().unlinkToDeath(record, 0);
- mKeyGestureHandlerRecords.remove(pid);
+ onKeyGestureHandlerRemoved(pid);
}
}
@@ -1328,9 +1342,14 @@ final class KeyGestureController {
return mAppLaunchShortcutManager.getBookmarks();
}
- private void onKeyGestureHandlerDied(int pid) {
+ private void onKeyGestureHandlerRemoved(int pid) {
synchronized (mKeyGestureHandlerRecords) {
mKeyGestureHandlerRecords.remove(pid);
+ for (int i = mSupportedKeyGestureToPidMap.size() - 1; i >= 0; i--) {
+ if (mSupportedKeyGestureToPidMap.valueAt(i) == pid) {
+ mSupportedKeyGestureToPidMap.removeAt(i);
+ }
+ }
}
}
@@ -1369,18 +1388,17 @@ final class KeyGestureController {
if (DEBUG) {
Slog.d(TAG, "Key gesture event handler for pid " + mPid + " died.");
}
- onKeyGestureHandlerDied(mPid);
+ onKeyGestureHandlerRemoved(mPid);
}
- public boolean handleKeyGesture(AidlKeyGestureEvent event, IBinder focusedToken) {
+ public void handleKeyGesture(AidlKeyGestureEvent event, IBinder focusedToken) {
try {
- return mKeyGestureHandler.handleKeyGesture(event, focusedToken);
+ mKeyGestureHandler.handleKeyGesture(event, focusedToken);
} catch (RemoteException ex) {
Slog.w(TAG, "Failed to send key gesture to process " + mPid
+ ", assuming it died.", ex);
binderDied();
}
- return false;
}
}
@@ -1479,18 +1497,21 @@ final class KeyGestureController {
}
}
ipw.println("}");
- ipw.print("mKeyGestureHandlerRecords = {");
synchronized (mKeyGestureHandlerRecords) {
- int i = mKeyGestureHandlerRecords.size() - 1;
- for (int processId : mKeyGestureHandlerRecords.keySet()) {
- ipw.print(processId);
- if (i > 0) {
+ ipw.print("mKeyGestureHandlerRecords = {");
+ int size = mKeyGestureHandlerRecords.size();
+ for (int i = 0; i < size; i++) {
+ int pid = mKeyGestureHandlerRecords.keyAt(i);
+ ipw.print(pid);
+ if (i < size - 1) {
ipw.print(", ");
}
- i--;
}
+ ipw.println("}");
+ ipw.println("mSupportedKeyGestures = " + Arrays.toString(
+ mSupportedKeyGestureToPidMap.copyKeys()));
}
- ipw.println("}");
+
ipw.decreaseIndent();
ipw.println("Last handled KeyGestureEvents: ");
ipw.increaseIndent();
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index fde9165a84c6..2066dbc87a0d 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -1826,8 +1826,14 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl.
@NonNull UserData userData) {
final int userId = userData.mUserId;
if (userData.mCurClient == client) {
- hideCurrentInputLocked(userData.mImeBindingState.mFocusedWindow, 0 /* flags */,
- SoftInputShowHideReason.HIDE_REMOVE_CLIENT, userId);
+ if (Flags.refactorInsetsController()) {
+ final var statsToken = createStatsTokenForFocusedClient(false /* show */,
+ SoftInputShowHideReason.HIDE_REMOVE_CLIENT, userId);
+ setImeVisibilityOnFocusedWindowClient(false, userData, statsToken);
+ } else {
+ hideCurrentInputLocked(userData.mImeBindingState.mFocusedWindow, 0 /* flags */,
+ SoftInputShowHideReason.HIDE_REMOVE_CLIENT, userId);
+ }
if (userData.mBoundToMethod) {
userData.mBoundToMethod = false;
final var userBindingController = userData.mBindingController;
@@ -2097,8 +2103,14 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl.
}
if (visibilityStateComputer.getImePolicy().isImeHiddenByDisplayPolicy()) {
- hideCurrentInputLocked(userData.mImeBindingState.mFocusedWindow, 0 /* flags */,
- SoftInputShowHideReason.HIDE_DISPLAY_IME_POLICY_HIDE, userId);
+ if (Flags.refactorInsetsController()) {
+ final var statsToken = createStatsTokenForFocusedClient(false /* show */,
+ SoftInputShowHideReason.HIDE_DISPLAY_IME_POLICY_HIDE, userId);
+ setImeVisibilityOnFocusedWindowClient(false, userData, statsToken);
+ } else {
+ hideCurrentInputLocked(userData.mImeBindingState.mFocusedWindow, 0 /* flags */,
+ SoftInputShowHideReason.HIDE_DISPLAY_IME_POLICY_HIDE, userId);
+ }
return InputBindResult.NO_IME;
}
@@ -3855,8 +3867,17 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl.
Slog.w(TAG, "If you need to impersonate a foreground user/profile from"
+ " a background user, use EditorInfo.targetInputMethodUser with"
+ " INTERACT_ACROSS_USERS_FULL permission.");
- hideCurrentInputLocked(userData.mImeBindingState.mFocusedWindow,
- 0 /* flags */, SoftInputShowHideReason.HIDE_INVALID_USER, userId);
+
+ if (Flags.refactorInsetsController()) {
+ final var statsToken = createStatsTokenForFocusedClient(
+ false /* show */, SoftInputShowHideReason.HIDE_INVALID_USER,
+ userId);
+ setImeVisibilityOnFocusedWindowClient(false, userData, statsToken);
+ } else {
+ hideCurrentInputLocked(userData.mImeBindingState.mFocusedWindow,
+ 0 /* flags */, SoftInputShowHideReason.HIDE_INVALID_USER,
+ userId);
+ }
return InputBindResult.INVALID_USER;
}
@@ -4993,7 +5014,6 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl.
setImeVisibilityOnFocusedWindowClient(false, userData,
null /* TODO(b/353463205) check statsToken */);
} else {
-
hideCurrentInputLocked(userData.mImeBindingState.mFocusedWindow,
0 /* flags */, reason, userId);
}
@@ -6688,8 +6708,9 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl.
final InputMethodSettings settings = InputMethodSettingsRepository.get(userId);
final var userData = getUserData(userId);
if (Flags.refactorInsetsController()) {
- setImeVisibilityOnFocusedWindowClient(false, userData,
- null /* TODO(b329229469) initialize statsToken here? */);
+ final var statsToken = createStatsTokenForFocusedClient(false /* show */,
+ SoftInputShowHideReason.HIDE_RESET_SHELL_COMMAND, userId);
+ setImeVisibilityOnFocusedWindowClient(false, userData, statsToken);
} else {
hideCurrentInputLocked(userData.mImeBindingState.mFocusedWindow,
0 /* flags */,
diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubEndpointBroker.java b/services/core/java/com/android/server/location/contexthub/ContextHubEndpointBroker.java
index 6db62c8397f3..bbf7732c9596 100644
--- a/services/core/java/com/android/server/location/contexthub/ContextHubEndpointBroker.java
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubEndpointBroker.java
@@ -33,6 +33,7 @@ import android.hardware.contexthub.MessageDeliveryStatus;
import android.hardware.contexthub.Reason;
import android.hardware.location.ContextHubTransaction;
import android.hardware.location.IContextHubTransactionCallback;
+import android.hardware.location.NanoAppState;
import android.os.Binder;
import android.os.IBinder;
import android.os.PowerManager;
@@ -48,6 +49,7 @@ import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
+import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
@@ -182,8 +184,11 @@ public class ContextHubEndpointBroker extends IContextHubEndpoint.Stub
long expiryMillis = RELIABLE_MESSAGE_DUPLICATE_DETECTION_TIMEOUT.toMillis();
if (nowMillis >= nextEntry.getValue() + expiryMillis) {
iterator.remove();
+ } else {
+ // Safe to break since LinkedHashMap is insertion-ordered, so the next entry
+ // will have a later timestamp and will not be expired.
+ break;
}
- break;
}
return mRxMessageHistoryMap.containsKey(message.getMessageSequenceNumber());
@@ -276,6 +281,7 @@ public class ContextHubEndpointBroker extends IContextHubEndpoint.Stub
int sessionId = mEndpointManager.reserveSessionId();
EndpointInfo halEndpointInfo = ContextHubServiceUtil.convertHalEndpointInfo(destination);
+ Log.d(TAG, "openSession: sessionId=" + sessionId);
synchronized (mOpenSessionLock) {
try {
@@ -301,7 +307,9 @@ public class ContextHubEndpointBroker extends IContextHubEndpoint.Stub
throw new IllegalArgumentException(
"Unknown session ID in closeSession: id=" + sessionId);
}
- halCloseEndpointSession(sessionId, ContextHubServiceUtil.toHalReason(reason));
+ Log.d(TAG, "closeSession: sessionId=" + sessionId + " reason=" + reason);
+ mEndpointManager.halCloseEndpointSession(
+ sessionId, ContextHubServiceUtil.toHalReason(reason));
}
@Override
@@ -312,7 +320,7 @@ public class ContextHubEndpointBroker extends IContextHubEndpoint.Stub
// Iterate in reverse since cleanupSessionResources will remove the entry
for (int i = mSessionMap.size() - 1; i >= 0; i--) {
int id = mSessionMap.keyAt(i);
- halCloseEndpointSessionNoThrow(id, Reason.ENDPOINT_GONE);
+ mEndpointManager.halCloseEndpointSessionNoThrow(id, Reason.ENDPOINT_GONE);
cleanupSessionResources(id);
}
}
@@ -372,12 +380,43 @@ public class ContextHubEndpointBroker extends IContextHubEndpoint.Stub
try {
mHubInterface.sendMessageToEndpoint(sessionId, halMessage);
} catch (RemoteException e) {
- Log.w(TAG, "Exception while sending message on session " + sessionId, e);
+ Log.e(
+ TAG,
+ "Exception while sending message on session "
+ + sessionId
+ + ", closing session",
+ e);
+ notifySessionClosedToBoth(sessionId, Reason.UNSPECIFIED);
}
} else {
+ IContextHubTransactionCallback wrappedCallback =
+ new IContextHubTransactionCallback.Stub() {
+ @Override
+ public void onQueryResponse(int result, List<NanoAppState> appStates)
+ throws RemoteException {
+ Log.w(TAG, "Unexpected onQueryResponse callback");
+ }
+
+ @Override
+ public void onTransactionComplete(int result) throws RemoteException {
+ callback.onTransactionComplete(result);
+ if (result != ContextHubTransaction.RESULT_SUCCESS) {
+ Log.e(
+ TAG,
+ "Failed to send reliable message "
+ + message
+ + ", closing session");
+ notifySessionClosedToBoth(sessionId, Reason.UNSPECIFIED);
+ }
+ }
+ };
ContextHubServiceTransaction transaction =
mTransactionManager.createSessionMessageTransaction(
- mHubInterface, sessionId, halMessage, mPackageName, callback);
+ mHubInterface,
+ sessionId,
+ halMessage,
+ mPackageName,
+ wrappedCallback);
try {
mTransactionManager.addTransaction(transaction);
info.setReliableMessagePending(transaction.getMessageSequenceNumber());
@@ -444,9 +483,7 @@ public class ContextHubEndpointBroker extends IContextHubEndpoint.Stub
int id = mSessionMap.keyAt(i);
HubEndpointInfo target = mSessionMap.get(id).getRemoteEndpointInfo();
if (!hasEndpointPermissions(target)) {
- halCloseEndpointSessionNoThrow(id, Reason.PERMISSION_DENIED);
- onCloseEndpointSession(id, Reason.PERMISSION_DENIED);
- // Resource cleanup is done in onCloseEndpointSession
+ notifySessionClosedToBoth(id, Reason.PERMISSION_DENIED);
}
}
}
@@ -503,17 +540,7 @@ public class ContextHubEndpointBroker extends IContextHubEndpoint.Stub
mContextHubEndpointCallback.asBinder().linkToDeath(this, 0 /* flags */);
}
- /* package */ void onEndpointSessionOpenRequest(
- int sessionId, HubEndpointInfo initiator, String serviceDescriptor) {
- Optional<Byte> error =
- onEndpointSessionOpenRequestInternal(sessionId, initiator, serviceDescriptor);
- if (error.isPresent()) {
- halCloseEndpointSessionNoThrow(sessionId, error.get());
- onCloseEndpointSession(sessionId, error.get());
- // Resource cleanup is done in onCloseEndpointSession
- }
- }
-
+ /** Handle close endpoint callback to the client side */
/* package */ void onCloseEndpointSession(int sessionId, byte reason) {
if (!cleanupSessionResources(sessionId)) {
Log.w(TAG, "Unknown session ID in onCloseEndpointSession: id=" + sessionId);
@@ -540,8 +567,17 @@ public class ContextHubEndpointBroker extends IContextHubEndpoint.Stub
/* package */ void onMessageReceived(int sessionId, HubMessage message) {
byte errorCode = onMessageReceivedInternal(sessionId, message);
- if (errorCode != ErrorCode.OK && message.isResponseRequired()) {
- sendMessageDeliveryStatus(sessionId, message.getMessageSequenceNumber(), errorCode);
+ if (errorCode != ErrorCode.OK) {
+ Log.e(TAG, "Failed to send message to endpoint: " + message + ", closing session");
+ if (message.isResponseRequired()) {
+ sendMessageDeliveryStatus(sessionId, message.getMessageSequenceNumber(), errorCode);
+ } else {
+ notifySessionClosedToBoth(
+ sessionId,
+ (errorCode == ErrorCode.PERMISSION_DENIED)
+ ? Reason.PERMISSION_DENIED
+ : Reason.UNSPECIFIED);
+ }
}
}
@@ -585,7 +621,7 @@ public class ContextHubEndpointBroker extends IContextHubEndpoint.Stub
}
}
- private Optional<Byte> onEndpointSessionOpenRequestInternal(
+ /* package */ Optional<Byte> onEndpointSessionOpenRequest(
int sessionId, HubEndpointInfo initiator, String serviceDescriptor) {
if (!hasEndpointPermissions(initiator)) {
Log.e(
@@ -594,15 +630,41 @@ public class ContextHubEndpointBroker extends IContextHubEndpoint.Stub
+ initiator
+ " doesn't have permission for "
+ mEndpointInfo);
- return Optional.of(Reason.PERMISSION_DENIED);
+ byte reason = Reason.PERMISSION_DENIED;
+ onCloseEndpointSession(sessionId, reason);
+ return Optional.of(reason);
}
+ // Check & handle error cases for duplicated session id.
+ final boolean existingSession;
+ final boolean existingSessionActive;
synchronized (mOpenSessionLock) {
if (hasSessionId(sessionId)) {
- Log.e(TAG, "Existing session in onEndpointSessionOpenRequest: id=" + sessionId);
- return Optional.of(Reason.UNSPECIFIED);
+ existingSession = true;
+ existingSessionActive = mSessionMap.get(sessionId).isActive();
+ Log.w(
+ TAG,
+ "onEndpointSessionOpenRequest: "
+ + "Existing session ID: "
+ + sessionId
+ + ", isActive: "
+ + existingSessionActive);
+ } else {
+ existingSession = false;
+ existingSessionActive = false;
+ mSessionMap.put(sessionId, new Session(initiator, true));
}
- mSessionMap.put(sessionId, new Session(initiator, true));
+ }
+
+ if (existingSession) {
+ if (existingSessionActive) {
+ // Existing session is already active, call onSessionOpenComplete.
+ openSessionRequestComplete(sessionId);
+ return Optional.empty();
+ }
+ // Reject the session open request for now. Consider invalidating previous pending
+ // session open request based on timeout.
+ return Optional.of(Reason.OPEN_ENDPOINT_SESSION_REQUEST_REJECTED);
}
boolean success =
@@ -610,7 +672,11 @@ public class ContextHubEndpointBroker extends IContextHubEndpoint.Stub
(consumer) ->
consumer.onSessionOpenRequest(
sessionId, initiator, serviceDescriptor));
- return success ? Optional.empty() : Optional.of(Reason.UNSPECIFIED);
+ byte reason = Reason.UNSPECIFIED;
+ if (!success) {
+ onCloseEndpointSession(sessionId, reason);
+ }
+ return success ? Optional.empty() : Optional.of(reason);
}
private byte onMessageReceivedInternal(int sessionId, HubMessage message) {
@@ -657,29 +723,6 @@ public class ContextHubEndpointBroker extends IContextHubEndpoint.Stub
}
/**
- * Calls the HAL closeEndpointSession API.
- *
- * @param sessionId The session ID to close
- * @param halReason The HAL reason
- */
- private void halCloseEndpointSession(int sessionId, byte halReason) throws RemoteException {
- try {
- mHubInterface.closeEndpointSession(sessionId, halReason);
- } catch (RemoteException | IllegalArgumentException | UnsupportedOperationException e) {
- throw e;
- }
- }
-
- /** Same as halCloseEndpointSession but does not throw the exception */
- private void halCloseEndpointSessionNoThrow(int sessionId, byte halReason) {
- try {
- halCloseEndpointSession(sessionId, halReason);
- } catch (RemoteException | IllegalArgumentException | UnsupportedOperationException e) {
- Log.e(TAG, "Exception while calling HAL closeEndpointSession", e);
- }
- }
-
- /**
* Cleans up resources related to a session with the provided ID.
*
* @param sessionId The session ID to clean up resources for
@@ -801,4 +844,16 @@ public class ContextHubEndpointBroker extends IContextHubEndpoint.Stub
+ "-0x"
+ Long.toHexString(endpoint.getIdentifier().getEndpoint()));
}
+
+ /**
+ * Notifies to both the HAL and the app that a session has been closed.
+ *
+ * @param sessionId The ID of the session that was closed
+ * @param halReason The HAL reason for closing the session
+ */
+ private void notifySessionClosedToBoth(int sessionId, byte halReason) {
+ Log.d(TAG, "notifySessionClosedToBoth: sessionId=" + sessionId + ", reason=" + halReason);
+ mEndpointManager.halCloseEndpointSessionNoThrow(sessionId, halReason);
+ onCloseEndpointSession(sessionId, halReason);
+ }
}
diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubEndpointManager.java b/services/core/java/com/android/server/location/contexthub/ContextHubEndpointManager.java
index 8ab581e1fb7a..e1561599517d 100644
--- a/services/core/java/com/android/server/location/contexthub/ContextHubEndpointManager.java
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubEndpointManager.java
@@ -29,6 +29,7 @@ import android.hardware.contexthub.IContextHubEndpoint;
import android.hardware.contexthub.IContextHubEndpointCallback;
import android.hardware.contexthub.IEndpointCommunication;
import android.hardware.contexthub.MessageDeliveryStatus;
+import android.hardware.contexthub.Reason;
import android.os.RemoteException;
import android.os.ServiceSpecificException;
import android.util.Log;
@@ -42,6 +43,7 @@ import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
+import java.util.Optional;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Consumer;
@@ -316,6 +318,11 @@ import java.util.function.Consumer;
}
}
+ /** Returns if a sessionId can be allocated for the service hub. */
+ private boolean isSessionIdAllocatedForService(int sessionId) {
+ return sessionId > mMaxSessionId || sessionId < mMinSessionId;
+ }
+
/**
* Unregisters an endpoint given its ID.
*
@@ -337,8 +344,7 @@ import java.util.function.Consumer;
}
}
- @Override
- public void onEndpointSessionOpenRequest(
+ private Optional<Byte> onEndpointSessionOpenRequestInternal(
int sessionId,
HubEndpointInfo.HubEndpointIdentifier destination,
HubEndpointInfo.HubEndpointIdentifier initiator,
@@ -348,7 +354,7 @@ import java.util.function.Consumer;
TAG,
"onEndpointSessionOpenRequest: invalid destination hub ID: "
+ destination.getHub());
- return;
+ return Optional.of(Reason.ENDPOINT_INVALID);
}
ContextHubEndpointBroker broker = mEndpointMap.get(destination.getEndpoint());
if (broker == null) {
@@ -356,7 +362,7 @@ import java.util.function.Consumer;
TAG,
"onEndpointSessionOpenRequest: unknown destination endpoint ID: "
+ destination.getEndpoint());
- return;
+ return Optional.of(Reason.ENDPOINT_INVALID);
}
HubEndpointInfo initiatorInfo = mHubInfoRegistry.getEndpointInfo(initiator);
if (initiatorInfo == null) {
@@ -364,9 +370,29 @@ import java.util.function.Consumer;
TAG,
"onEndpointSessionOpenRequest: unknown initiator endpoint ID: "
+ initiator.getEndpoint());
- return;
+ return Optional.of(Reason.ENDPOINT_INVALID);
+ }
+ if (!isSessionIdAllocatedForService(sessionId)) {
+ Log.e(
+ TAG,
+ "onEndpointSessionOpenRequest: invalid session ID, rejected:"
+ + " sessionId="
+ + sessionId);
+ return Optional.of(Reason.OPEN_ENDPOINT_SESSION_REQUEST_REJECTED);
}
- broker.onEndpointSessionOpenRequest(sessionId, initiatorInfo, serviceDescriptor);
+ return broker.onEndpointSessionOpenRequest(sessionId, initiatorInfo, serviceDescriptor);
+ }
+
+ @Override
+ public void onEndpointSessionOpenRequest(
+ int sessionId,
+ HubEndpointInfo.HubEndpointIdentifier destination,
+ HubEndpointInfo.HubEndpointIdentifier initiator,
+ String serviceDescriptor) {
+ Optional<Byte> errorOptional =
+ onEndpointSessionOpenRequestInternal(
+ sessionId, destination, initiator, serviceDescriptor);
+ errorOptional.ifPresent((error) -> halCloseEndpointSessionNoThrow(sessionId, error));
}
@Override
@@ -418,6 +444,30 @@ import java.util.function.Consumer;
}
}
+ /**
+ * Calls the HAL closeEndpointSession API.
+ *
+ * @param sessionId The session ID to close
+ * @param halReason The HAL reason
+ */
+ /* package */ void halCloseEndpointSession(int sessionId, byte halReason)
+ throws RemoteException {
+ try {
+ mHubInterface.closeEndpointSession(sessionId, halReason);
+ } catch (RemoteException | IllegalArgumentException | UnsupportedOperationException e) {
+ throw e;
+ }
+ }
+
+ /** Same as halCloseEndpointSession but does not throw the exception */
+ /* package */ void halCloseEndpointSessionNoThrow(int sessionId, byte halReason) {
+ try {
+ halCloseEndpointSession(sessionId, halReason);
+ } catch (RemoteException | IllegalArgumentException | UnsupportedOperationException e) {
+ Log.e(TAG, "Exception while calling HAL closeEndpointSession", e);
+ }
+ }
+
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
diff --git a/services/core/java/com/android/server/location/gnss/hal/GnssNative.java b/services/core/java/com/android/server/location/gnss/hal/GnssNative.java
index 7b4c56334868..7fd400ec8ac9 100644
--- a/services/core/java/com/android/server/location/gnss/hal/GnssNative.java
+++ b/services/core/java/com/android/server/location/gnss/hal/GnssNative.java
@@ -518,7 +518,6 @@ public class GnssNative {
if (!Flags.gnssAssistanceInterfaceJni()) {
return;
}
- Preconditions.checkState(!mRegistered);
Preconditions.checkState(mGnssAssistanceCallbacks == null);
mGnssAssistanceCallbacks = Objects.requireNonNull(callbacks);
}
diff --git a/services/core/java/com/android/server/locksettings/Android.bp b/services/core/java/com/android/server/locksettings/Android.bp
deleted file mode 100644
index 53f1ac668e49..000000000000
--- a/services/core/java/com/android/server/locksettings/Android.bp
+++ /dev/null
@@ -1,11 +0,0 @@
-aconfig_declarations {
- name: "locksettings_flags",
- package: "com.android.server.locksettings",
- container: "system",
- srcs: ["*.aconfig"],
-}
-
-java_aconfig_library {
- name: "locksettings_flags_lib",
- aconfig_declarations: "locksettings_flags",
-}
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index a8190269450a..07d1a65850e3 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -1917,7 +1917,7 @@ public class LockSettingsService extends ILockSettings.Stub {
* Set a new LSKF for the given user/profile. Only succeeds if the synthetic password for the
* user is protected by the given {@param savedCredential}.
* <p>
- * When {@link android.security.Flags#clearStrongAuthOnAddPrimaryCredential()} is enabled and
+ * When {@link android.security.Flags#clearStrongAuthOnAddingPrimaryCredential()} is enabled and
* setting a new credential where there was none, updates the strong auth state for
* {@param userId} to <tt>STRONG_AUTH_NOT_REQUIRED</tt>.
*
@@ -1969,7 +1969,7 @@ public class LockSettingsService extends ILockSettings.Stub {
onSyntheticPasswordUnlocked(userId, sp);
setLockCredentialWithSpLocked(credential, sp, userId);
- if (android.security.Flags.clearStrongAuthOnAddPrimaryCredential()
+ if (android.security.Flags.clearStrongAuthOnAddingPrimaryCredential()
&& savedCredential.isNone() && !credential.isNone()) {
// Clear the strong auth value, since the LSKF has just been entered and set,
// but only when the previous credential was None.
diff --git a/services/core/java/com/android/server/locksettings/RebootEscrowManager.java b/services/core/java/com/android/server/locksettings/RebootEscrowManager.java
index 820c0efcc1cf..4b704d01c3b9 100644
--- a/services/core/java/com/android/server/locksettings/RebootEscrowManager.java
+++ b/services/core/java/com/android/server/locksettings/RebootEscrowManager.java
@@ -428,15 +428,11 @@ class RebootEscrowManager {
/** Wrapper function to set error code serialized through handler, */
private void setLoadEscrowDataErrorCode(@RebootEscrowErrorCode int value, Handler handler) {
- if (Flags.waitForInternetRor()) {
- mInjector.post(
- handler,
- () -> {
- mLoadEscrowDataErrorCode = value;
- });
- } else {
- mLoadEscrowDataErrorCode = value;
- }
+ mInjector.post(
+ handler,
+ () -> {
+ mLoadEscrowDataErrorCode = value;
+ });
}
/** Wrapper function to compare and set error code serialized through handler. */
@@ -511,23 +507,17 @@ class RebootEscrowManager {
mWakeLock.acquire(mInjector.getWakeLockTimeoutMillis());
}
- if (Flags.waitForInternetRor()) {
- // Timeout to stop retrying same as the wake lock timeout.
- mInjector.postDelayed(
- retryHandler,
- () -> {
- mRebootEscrowTimedOut = true;
- },
- mInjector.getLoadEscrowTimeoutMillis());
-
- mInjector.post(
- retryHandler,
- () -> loadRebootEscrowDataOnInternet(retryHandler, users, rebootEscrowUsers));
- return;
- }
+ // Timeout to stop retrying same as the wake lock timeout.
+ mInjector.postDelayed(
+ retryHandler,
+ () -> {
+ mRebootEscrowTimedOut = true;
+ },
+ mInjector.getLoadEscrowTimeoutMillis());
- mInjector.post(retryHandler, () -> loadRebootEscrowDataWithRetry(
- retryHandler, 0, users, rebootEscrowUsers));
+ mInjector.post(
+ retryHandler,
+ () -> loadRebootEscrowDataOnInternet(retryHandler, users, rebootEscrowUsers));
}
void scheduleLoadRebootEscrowDataOrFail(
@@ -548,27 +538,13 @@ class RebootEscrowManager {
return;
}
- if (Flags.waitForInternetRor()) {
- if (mRebootEscrowTimedOut) {
- Slog.w(TAG, "Failed to load reboot escrow data within timeout");
- compareAndSetLoadEscrowDataErrorCode(
- ERROR_NONE, ERROR_TIMEOUT_EXHAUSTED, retryHandler);
- } else {
- Slog.w(
- TAG,
- "Failed to load reboot escrow data after " + attemptNumber + " attempts");
- compareAndSetLoadEscrowDataErrorCode(
- ERROR_NONE, ERROR_RETRY_COUNT_EXHAUSTED, retryHandler);
- }
- onGetRebootEscrowKeyFailed(users, attemptNumber, retryHandler);
- return;
- }
-
- Slog.w(TAG, "Failed to load reboot escrow data after " + attemptNumber + " attempts");
- if (mInjector.serverBasedResumeOnReboot() && !mInjector.isNetworkConnected()) {
- mLoadEscrowDataErrorCode = ERROR_NO_NETWORK;
+ if (mRebootEscrowTimedOut) {
+ Slog.w(TAG, "Failed to load reboot escrow data within timeout");
+ compareAndSetLoadEscrowDataErrorCode(ERROR_NONE, ERROR_TIMEOUT_EXHAUSTED, retryHandler);
} else {
- mLoadEscrowDataErrorCode = ERROR_RETRY_COUNT_EXHAUSTED;
+ Slog.w(TAG, "Failed to load reboot escrow data after " + attemptNumber + " attempts");
+ compareAndSetLoadEscrowDataErrorCode(
+ ERROR_NONE, ERROR_RETRY_COUNT_EXHAUSTED, retryHandler);
}
onGetRebootEscrowKeyFailed(users, attemptNumber, retryHandler);
}
diff --git a/services/core/java/com/android/server/locksettings/flags.aconfig b/services/core/java/com/android/server/locksettings/flags.aconfig
deleted file mode 100644
index 6818de91c98e..000000000000
--- a/services/core/java/com/android/server/locksettings/flags.aconfig
+++ /dev/null
@@ -1,9 +0,0 @@
-package: "com.android.server.locksettings"
-container: "system"
-
-flag {
- name: "wait_for_internet_ror"
- namespace: "sudo"
- description: "Feature flag to wait for internet connectivity before calling resume on reboot server."
- bug: "231660348"
-} \ No newline at end of file
diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java
index 58cf29b59961..c174451e8f5b 100644
--- a/services/core/java/com/android/server/media/MediaSessionService.java
+++ b/services/core/java/com/android/server/media/MediaSessionService.java
@@ -192,9 +192,15 @@ public class MediaSessionService extends SystemService implements Monitor {
private final Map<Integer, Set<MediaSessionRecordImpl>> mUserEngagedSessionsForFgs =
new HashMap<>();
- /* Maps uid with all media notifications associated to it */
+ /**
+ * Maps UIDs to their associated media notifications: UID -> (Notification ID ->
+ * {@link android.service.notification.StatusBarNotification}).
+ * Each UID maps to a collection of notifications, identified by their
+ * {@link android.service.notification.StatusBarNotification#getId()}.
+ */
@GuardedBy("mLock")
- private final Map<Integer, Set<StatusBarNotification>> mMediaNotifications = new HashMap<>();
+ private final Map<Integer, Map<String, StatusBarNotification>> mMediaNotifications =
+ new HashMap<>();
// The FullUserRecord of the current users. (i.e. The foreground user that isn't a profile)
// It's always not null after the MediaSessionService is started.
@@ -737,7 +743,8 @@ public class MediaSessionService extends SystemService implements Monitor {
}
synchronized (mLock) {
int uid = mediaSessionRecord.getUid();
- for (StatusBarNotification sbn : mMediaNotifications.getOrDefault(uid, Set.of())) {
+ for (StatusBarNotification sbn : mMediaNotifications.getOrDefault(uid,
+ Map.of()).values()) {
if (mediaSessionRecord.isLinkedToNotification(sbn.getNotification())) {
setFgsActiveLocked(mediaSessionRecord, sbn);
return;
@@ -771,7 +778,7 @@ public class MediaSessionService extends SystemService implements Monitor {
int uid, MediaSessionRecordImpl record) {
synchronized (mLock) {
for (StatusBarNotification sbn :
- mMediaNotifications.getOrDefault(uid, Set.of())) {
+ mMediaNotifications.getOrDefault(uid, Map.of()).values()) {
if (record.isLinkedToNotification(sbn.getNotification())) {
return sbn;
}
@@ -794,7 +801,8 @@ public class MediaSessionService extends SystemService implements Monitor {
for (MediaSessionRecordImpl record :
mUserEngagedSessionsForFgs.getOrDefault(uid, Set.of())) {
for (StatusBarNotification sbn :
- mMediaNotifications.getOrDefault(uid, Set.of())) {
+ mMediaNotifications.getOrDefault(uid, Map.of()).values()) {
+ //
if (record.isLinkedToNotification(sbn.getNotification())) {
// A user engaged session linked with a media notification is found.
// We shouldn't call stop FGS in this case.
@@ -3262,8 +3270,12 @@ public class MediaSessionService extends SystemService implements Monitor {
return;
}
synchronized (mLock) {
- mMediaNotifications.putIfAbsent(uid, new HashSet<>());
- mMediaNotifications.get(uid).add(sbn);
+ Map<String, StatusBarNotification> notifications = mMediaNotifications.get(uid);
+ if (notifications == null) {
+ notifications = new HashMap<>();
+ mMediaNotifications.put(uid, notifications);
+ }
+ notifications.put(sbn.getKey(), sbn);
MediaSessionRecordImpl userEngagedRecord =
getUserEngagedMediaSessionRecordForNotification(uid, postedNotification);
if (userEngagedRecord != null) {
@@ -3287,10 +3299,10 @@ public class MediaSessionService extends SystemService implements Monitor {
return;
}
synchronized (mLock) {
- Set<StatusBarNotification> uidMediaNotifications = mMediaNotifications.get(uid);
- if (uidMediaNotifications != null) {
- uidMediaNotifications.remove(sbn);
- if (uidMediaNotifications.isEmpty()) {
+ Map<String, StatusBarNotification> notifications = mMediaNotifications.get(uid);
+ if (notifications != null) {
+ notifications.remove(sbn.getKey());
+ if (notifications.isEmpty()) {
mMediaNotifications.remove(uid);
}
}
diff --git a/services/core/java/com/android/server/media/projection/Android.bp b/services/core/java/com/android/server/media/projection/Android.bp
new file mode 100644
index 000000000000..114be7d20d5b
--- /dev/null
+++ b/services/core/java/com/android/server/media/projection/Android.bp
@@ -0,0 +1,21 @@
+//
+// Copyright (C) 2025 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.
+
+team {
+ name: "trendy_team_media_projection",
+
+ // go/trendy/manage/engineers/6362947212640256
+ trendy_team_id: "6362947212640256",
+}
diff --git a/services/core/java/com/android/server/media/projection/TEST_MAPPING b/services/core/java/com/android/server/media/projection/TEST_MAPPING
index b33097c50002..7061044aaeee 100644
--- a/services/core/java/com/android/server/media/projection/TEST_MAPPING
+++ b/services/core/java/com/android/server/media/projection/TEST_MAPPING
@@ -2,6 +2,34 @@
"presubmit": [
{
"name": "MediaProjectionTests"
+ },
+ {
+ "name": "CtsMediaProjectionTestCases"
+ },
+ {
+ "name": "CtsMediaProjectionSDK33TestCases"
+ },
+ {
+ "name": "CtsMediaProjectionSDK34TestCases"
+ },
+ {
+ "name": "CtsMediaAudioTestCases",
+ "options": [
+ {
+ "include-filter": "android.media.audio.cts.RemoteSubmixTest"
+ },
+ {
+ "include-filter": "android.media.audio.cts.AudioPlaybackCaptureTest"
+ }
+ ]
+ },
+ {
+ "name": "CtsStatsdAtomHostTestCases",
+ "options": [
+ {
+ "include-filter": "android.cts.statsdatom.media.projection.MediaProjectionAtomsTests"
+ }
+ ]
}
]
}
diff --git a/services/core/java/com/android/server/media/quality/MediaQualityService.java b/services/core/java/com/android/server/media/quality/MediaQualityService.java
index 9a09807d52d7..f4fe8e120ba1 100644
--- a/services/core/java/com/android/server/media/quality/MediaQualityService.java
+++ b/services/core/java/com/android/server/media/quality/MediaQualityService.java
@@ -444,7 +444,7 @@ public class MediaQualityService extends SystemService {
public SoundProfile createSoundProfile(SoundProfile sp, int userId) {
if ((sp.getPackageName() != null && !sp.getPackageName().isEmpty()
&& !incomingPackageEqualsCallingUidPackage(sp.getPackageName()))
- && !hasGlobalPictureQualityServicePermission()) {
+ && !hasGlobalSoundQualityServicePermission()) {
mMqManagerNotifier.notifyOnSoundProfileError(null, SoundProfile.ERROR_NO_PERMISSION,
Binder.getCallingUid(), Binder.getCallingPid());
}
diff --git a/services/core/java/com/android/server/media/quality/MediaQualityUtils.java b/services/core/java/com/android/server/media/quality/MediaQualityUtils.java
index cf8b703a2641..05aac5587c2c 100644
--- a/services/core/java/com/android/server/media/quality/MediaQualityUtils.java
+++ b/services/core/java/com/android/server/media/quality/MediaQualityUtils.java
@@ -18,13 +18,20 @@ package com.android.server.media.quality;
import android.content.ContentValues;
import android.database.Cursor;
+import android.hardware.tv.mediaquality.ColorRange;
+import android.hardware.tv.mediaquality.ColorSpace;
+import android.hardware.tv.mediaquality.ColorTemperature;
import android.hardware.tv.mediaquality.DolbyAudioProcessing;
import android.hardware.tv.mediaquality.DtsVirtualX;
+import android.hardware.tv.mediaquality.Gamma;
import android.hardware.tv.mediaquality.ParameterDefaultValue;
import android.hardware.tv.mediaquality.ParameterName;
import android.hardware.tv.mediaquality.ParameterRange;
import android.hardware.tv.mediaquality.PictureParameter;
+import android.hardware.tv.mediaquality.PictureQualityEventType;
+import android.hardware.tv.mediaquality.QualityLevel;
import android.hardware.tv.mediaquality.SoundParameter;
+import android.media.quality.MediaQualityContract;
import android.media.quality.MediaQualityContract.BaseParameters;
import android.media.quality.MediaQualityContract.PictureQuality;
import android.media.quality.MediaQualityContract.SoundQuality;
@@ -371,7 +378,7 @@ public final class MediaQualityUtils {
}
List<PictureParameter> pictureParams = new ArrayList<>();
if (params.containsKey(PictureQuality.PARAMETER_BRIGHTNESS)) {
- pictureParams.add(PictureParameter.brightness(params.getLong(
+ pictureParams.add(PictureParameter.brightness((float) params.getDouble(
PictureQuality.PARAMETER_BRIGHTNESS)));
params.remove(PictureQuality.PARAMETER_BRIGHTNESS);
}
@@ -441,28 +448,46 @@ public final class MediaQualityUtils {
params.remove(PictureQuality.PARAMETER_COLOR_TUNER_BLUE_GAIN);
}
if (params.containsKey(PictureQuality.PARAMETER_NOISE_REDUCTION)) {
- pictureParams.add(PictureParameter.noiseReduction(
- (byte) params.getInt(PictureQuality.PARAMETER_NOISE_REDUCTION)));
+ String noiseReductionString = params.getString(
+ PictureQuality.PARAMETER_NOISE_REDUCTION);
+ if (noiseReductionString != null) {
+ byte noiseReductionByte = mapQualityLevel(noiseReductionString);
+ pictureParams.add(PictureParameter.noiseReduction(noiseReductionByte));
+ }
params.remove(PictureQuality.PARAMETER_NOISE_REDUCTION);
}
if (params.containsKey(PictureQuality.PARAMETER_MPEG_NOISE_REDUCTION)) {
- pictureParams.add(PictureParameter.mpegNoiseReduction(
- (byte) params.getInt(PictureQuality.PARAMETER_MPEG_NOISE_REDUCTION)));
+ String mpegNoiseReductionString = params.getString(
+ PictureQuality.PARAMETER_MPEG_NOISE_REDUCTION);
+ if (mpegNoiseReductionString != null) {
+ byte mpegNoiseReductionByte = mapQualityLevel(mpegNoiseReductionString);
+ pictureParams.add(PictureParameter.mpegNoiseReduction(mpegNoiseReductionByte));
+ }
params.remove(PictureQuality.PARAMETER_MPEG_NOISE_REDUCTION);
}
if (params.containsKey(PictureQuality.PARAMETER_FLESH_TONE)) {
- pictureParams.add(PictureParameter.fleshTone(
- (byte) params.getInt(PictureQuality.PARAMETER_FLESH_TONE)));
+ String fleshToneString = params.getString(PictureQuality.PARAMETER_FLESH_TONE);
+ if (fleshToneString != null) {
+ byte fleshToneByte = mapQualityLevel(fleshToneString);
+ pictureParams.add(PictureParameter.fleshTone(fleshToneByte));
+ }
params.remove(PictureQuality.PARAMETER_FLESH_TONE);
}
if (params.containsKey(PictureQuality.PARAMETER_DECONTOUR)) {
- pictureParams.add(PictureParameter.deContour(
- (byte) params.getInt(PictureQuality.PARAMETER_DECONTOUR)));
+ String decontourString = params.getString(PictureQuality.PARAMETER_DECONTOUR);
+ if (decontourString != null) {
+ byte decontourByte = mapQualityLevel(decontourString);
+ pictureParams.add(PictureParameter.deContour(decontourByte));
+ }
params.remove(PictureQuality.PARAMETER_DECONTOUR);
}
if (params.containsKey(PictureQuality.PARAMETER_DYNAMIC_LUMA_CONTROL)) {
- pictureParams.add(PictureParameter.dynamicLumaControl(
- (byte) params.getInt(PictureQuality.PARAMETER_DYNAMIC_LUMA_CONTROL)));
+ String dynamicLunaControlString = params.getString(
+ PictureQuality.PARAMETER_DYNAMIC_LUMA_CONTROL);
+ if (dynamicLunaControlString != null) {
+ byte dynamicLunaControlByte = mapQualityLevel(dynamicLunaControlString);
+ pictureParams.add(PictureParameter.dynamicLumaControl(dynamicLunaControlByte));
+ }
params.remove(PictureQuality.PARAMETER_DYNAMIC_LUMA_CONTROL);
}
if (params.containsKey(PictureQuality.PARAMETER_FILM_MODE)) {
@@ -481,9 +506,48 @@ public final class MediaQualityUtils {
params.remove(PictureQuality.PARAMETER_COLOR_TUNE);
}
if (params.containsKey(PictureQuality.PARAMETER_COLOR_TEMPERATURE)) {
- pictureParams.add(PictureParameter.colorTemperature(
- (byte) params.getInt(
- PictureQuality.PARAMETER_COLOR_TEMPERATURE)));
+ String colorTemperatureString = params.getString(
+ PictureQuality.PARAMETER_COLOR_TEMPERATURE);
+ if (colorTemperatureString != null) {
+ byte colorTemperatureByte;
+ switch (colorTemperatureString) {
+ case MediaQualityContract.COLOR_TEMP_USER:
+ colorTemperatureByte = ColorTemperature.USER;
+ break;
+ case MediaQualityContract.COLOR_TEMP_COOL:
+ colorTemperatureByte = ColorTemperature.COOL;
+ break;
+ case MediaQualityContract.COLOR_TEMP_STANDARD:
+ colorTemperatureByte = ColorTemperature.STANDARD;
+ break;
+ case MediaQualityContract.COLOR_TEMP_WARM:
+ colorTemperatureByte = ColorTemperature.WARM;
+ break;
+ case MediaQualityContract.COLOR_TEMP_USER_HDR10PLUS:
+ colorTemperatureByte = ColorTemperature.USER_HDR10PLUS;
+ break;
+ case MediaQualityContract.COLOR_TEMP_COOL_HDR10PLUS:
+ colorTemperatureByte = ColorTemperature.COOL_HDR10PLUS;
+ break;
+ case MediaQualityContract.COLOR_TEMP_STANDARD_HDR10PLUS:
+ colorTemperatureByte = ColorTemperature.STANDARD_HDR10PLUS;
+ break;
+ case MediaQualityContract.COLOR_TEMP_WARM_HDR10PLUS:
+ colorTemperatureByte = ColorTemperature.WARM_HDR10PLUS;
+ break;
+ case MediaQualityContract.COLOR_TEMP_FMMSDR:
+ colorTemperatureByte = ColorTemperature.FMMSDR;
+ break;
+ case MediaQualityContract.COLOR_TEMP_FMMHDR:
+ colorTemperatureByte = ColorTemperature.FMMHDR;
+ break;
+ default:
+ colorTemperatureByte = ColorTemperature.STANDARD;
+ Log.e("PictureParams", "Invalid color_temp string: "
+ + colorTemperatureString);
+ }
+ pictureParams.add(PictureParameter.colorTemperature(colorTemperatureByte));
+ }
params.remove(PictureQuality.PARAMETER_COLOR_TEMPERATURE);
}
if (params.containsKey(PictureQuality.PARAMETER_GLOBAL_DIMMING)) {
@@ -517,8 +581,26 @@ public final class MediaQualityUtils {
params.remove(PictureQuality.PARAMETER_COLOR_TUNER_BLUE_GAIN);
}
if (params.containsKey(PictureQuality.PARAMETER_LEVEL_RANGE)) {
- pictureParams.add(PictureParameter.levelRange(
- (byte) params.getInt(PictureQuality.PARAMETER_LEVEL_RANGE)));
+ String levelRangeString = params.getString(PictureQuality.PARAMETER_LEVEL_RANGE);
+ if (levelRangeString != null) {
+ byte levelRangeByte;
+ switch (levelRangeString) {
+ case "AUTO":
+ levelRangeByte = ColorRange.AUTO;
+ break;
+ case "LIMITED":
+ levelRangeByte = ColorRange.LIMITED;
+ break;
+ case "FULL":
+ levelRangeByte = ColorRange.FULL;
+ break;
+ default:
+ levelRangeByte = ColorRange.AUTO;
+ Log.e("PictureParams", "Invalid color_range string: "
+ + levelRangeString);
+ }
+ pictureParams.add(PictureParameter.levelRange(levelRangeByte));
+ }
params.remove(PictureQuality.PARAMETER_LEVEL_RANGE);
}
if (params.containsKey(PictureQuality.PARAMETER_GAMUT_MAPPING)) {
@@ -547,13 +629,61 @@ public final class MediaQualityUtils {
params.remove(PictureQuality.PARAMETER_CVRR);
}
if (params.containsKey(PictureQuality.PARAMETER_HDMI_RGB_RANGE)) {
- pictureParams.add(PictureParameter.hdmiRgbRange(
- (byte) params.getInt(PictureQuality.PARAMETER_HDMI_RGB_RANGE)));
+ String hdmiRgbRangeString = params.getString(PictureQuality.PARAMETER_HDMI_RGB_RANGE);
+ if (hdmiRgbRangeString != null) {
+ byte hdmiRgbRangeByte;
+ switch (hdmiRgbRangeString) {
+ case "AUTO":
+ hdmiRgbRangeByte = ColorRange.AUTO;
+ break;
+ case "LIMITED":
+ hdmiRgbRangeByte = ColorRange.LIMITED;
+ break;
+ case "FULL":
+ hdmiRgbRangeByte = ColorRange.FULL;
+ break;
+ default:
+ hdmiRgbRangeByte = ColorRange.AUTO;
+ Log.e("PictureParams", "Invalid hdmi_rgb_range string: "
+ + hdmiRgbRangeByte);
+ }
+ pictureParams.add(PictureParameter.hdmiRgbRange(hdmiRgbRangeByte));
+ }
params.remove(PictureQuality.PARAMETER_HDMI_RGB_RANGE);
}
if (params.containsKey(PictureQuality.PARAMETER_COLOR_SPACE)) {
- pictureParams.add(PictureParameter.colorSpace(
- (byte) params.getInt(PictureQuality.PARAMETER_COLOR_SPACE)));
+ String colorSpaceString = params.getString(PictureQuality.PARAMETER_COLOR_SPACE);
+ if (colorSpaceString != null) {
+ byte colorSpaceByte;
+ switch (colorSpaceString) {
+ case "AUTO":
+ colorSpaceByte = ColorSpace.AUTO;
+ break;
+ case "S_RGB_BT_709":
+ colorSpaceByte = ColorSpace.S_RGB_BT_709;
+ break;
+ case "DCI":
+ colorSpaceByte = ColorSpace.DCI;
+ break;
+ case "ADOBE_RGB":
+ colorSpaceByte = ColorSpace.ADOBE_RGB;
+ break;
+ case "BT2020":
+ colorSpaceByte = ColorSpace.BT2020;
+ break;
+ case "ON":
+ colorSpaceByte = ColorSpace.ON;
+ break;
+ case "OFF":
+ colorSpaceByte = ColorSpace.OFF;
+ break;
+ default:
+ colorSpaceByte = ColorSpace.OFF;
+ Log.e("PictureParams", "Invalid color_space string: "
+ + colorSpaceString);
+ }
+ pictureParams.add(PictureParameter.colorSpace(colorSpaceByte));
+ }
params.remove(PictureQuality.PARAMETER_COLOR_SPACE);
}
if (params.containsKey(PictureQuality.PARAMETER_PANEL_INIT_MAX_LUMINCE_NITS)) {
@@ -567,8 +697,25 @@ public final class MediaQualityUtils {
params.remove(PictureQuality.PARAMETER_PANEL_INIT_MAX_LUMINCE_VALID);
}
if (params.containsKey(PictureQuality.PARAMETER_GAMMA)) {
- pictureParams.add(PictureParameter.gamma(
- (byte) params.getInt(PictureQuality.PARAMETER_GAMMA)));
+ String gammaString = params.getString(PictureQuality.PARAMETER_GAMMA);
+ if (gammaString != null) {
+ byte gammaByte;
+ switch (gammaString) {
+ case "DARK":
+ gammaByte = Gamma.DARK;
+ break;
+ case "MIDDLE":
+ gammaByte = Gamma.MIDDLE;
+ break;
+ case "BRIGHT":
+ gammaByte = Gamma.BRIGHT;
+ break;
+ default:
+ gammaByte = Gamma.DARK;
+ Log.e("PictureParams", "Invalid gamma string: " + gammaString);
+ }
+ pictureParams.add(PictureParameter.gamma(gammaByte));
+ }
params.remove(PictureQuality.PARAMETER_GAMMA);
}
if (params.containsKey(PictureQuality.PARAMETER_COLOR_TEMPERATURE_RED_OFFSET)) {
@@ -602,13 +749,19 @@ public final class MediaQualityUtils {
params.remove(PictureQuality.PARAMETER_ELEVEN_POINT_BLUE);
}
if (params.containsKey(PictureQuality.PARAMETER_LOW_BLUE_LIGHT)) {
- pictureParams.add(PictureParameter.lowBlueLight(
- (byte) params.getInt(PictureQuality.PARAMETER_LOW_BLUE_LIGHT)));
+ String lowBlueLightString = params.getString(PictureQuality.PARAMETER_LOW_BLUE_LIGHT);
+ if (lowBlueLightString != null) {
+ byte lowBlueLightByte = mapQualityLevel(lowBlueLightString);
+ pictureParams.add(PictureParameter.lowBlueLight(lowBlueLightByte));
+ }
params.remove(PictureQuality.PARAMETER_LOW_BLUE_LIGHT);
}
if (params.containsKey(PictureQuality.PARAMETER_LD_MODE)) {
- pictureParams.add(PictureParameter.LdMode(
- (byte) params.getInt(PictureQuality.PARAMETER_LD_MODE)));
+ String ldModeString = params.getString(PictureQuality.PARAMETER_LD_MODE);
+ if (ldModeString != null) {
+ byte ldModeByte = mapQualityLevel(ldModeString);
+ pictureParams.add(PictureParameter.LdMode(ldModeByte));
+ }
params.remove(PictureQuality.PARAMETER_LD_MODE);
}
if (params.containsKey(PictureQuality.PARAMETER_OSD_RED_GAIN)) {
@@ -767,8 +920,44 @@ public final class MediaQualityUtils {
params.remove(PictureQuality.PARAMETER_COLOR_TUNER_LUMINANCE_FLESH);
}
if (params.containsKey(PictureQuality.PARAMETER_PICTURE_QUALITY_EVENT_TYPE)) {
- pictureParams.add(PictureParameter.pictureQualityEventType(
- (byte) params.getInt(PictureQuality.PARAMETER_PICTURE_QUALITY_EVENT_TYPE)));
+ String pictureQualityEventTypeString = params.getString(
+ PictureQuality.PARAMETER_PICTURE_QUALITY_EVENT_TYPE);
+ if (pictureQualityEventTypeString != null) {
+ byte pictureQualityEventTypeByte;
+ switch (pictureQualityEventTypeString) {
+ case "NONE":
+ pictureQualityEventTypeByte = PictureQualityEventType.NONE;
+ break;
+ case "BBD_RESULT":
+ pictureQualityEventTypeByte = PictureQualityEventType.BBD_RESULT;
+ break;
+ case "VIDEO_DELAY_CHANGE":
+ pictureQualityEventTypeByte = PictureQualityEventType.VIDEO_DELAY_CHANGE;
+ break;
+ case "CAPTUREPOINT_INFO_CHANGE":
+ pictureQualityEventTypeByte =
+ PictureQualityEventType.CAPTUREPOINT_INFO_CHANGE;
+ break;
+ case "VIDEOPATH_CHANGE":
+ pictureQualityEventTypeByte = PictureQualityEventType.VIDEOPATH_CHANGE;
+ break;
+ case "EXTRA_FRAME_CHANGE":
+ pictureQualityEventTypeByte = PictureQualityEventType.EXTRA_FRAME_CHANGE;
+ break;
+ case "DOLBY_IQ_CHANGE":
+ pictureQualityEventTypeByte = PictureQualityEventType.DOLBY_IQ_CHANGE;
+ break;
+ case "DOLBY_APO_CHANGE":
+ pictureQualityEventTypeByte = PictureQualityEventType.DOLBY_APO_CHANGE;
+ break;
+ default:
+ pictureQualityEventTypeByte = PictureQualityEventType.NONE;
+ Log.e("PictureParams", "Invalid event type string: "
+ + pictureQualityEventTypeString);
+ }
+ pictureParams.add(
+ PictureParameter.pictureQualityEventType(pictureQualityEventTypeByte));
+ }
params.remove(PictureQuality.PARAMETER_PICTURE_QUALITY_EVENT_TYPE);
}
return pictureParams.toArray(new PictureParameter[0]);
@@ -1657,6 +1846,19 @@ public final class MediaQualityUtils {
return colIndex != -1 ? cursor.getString(colIndex) : null;
}
+ private static byte mapQualityLevel(String qualityLevel) {
+ return switch (qualityLevel) {
+ case MediaQualityContract.LEVEL_OFF -> QualityLevel.OFF;
+ case MediaQualityContract.LEVEL_LOW -> QualityLevel.LOW;
+ case MediaQualityContract.LEVEL_MEDIUM -> QualityLevel.MEDIUM;
+ case MediaQualityContract.LEVEL_HIGH -> QualityLevel.HIGH;
+ default -> {
+ Log.e("PictureParams", "Invalid noise_reduction string: " + qualityLevel);
+ yield QualityLevel.OFF;
+ }
+ };
+ }
+
private MediaQualityUtils() {
}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerInternal.java b/services/core/java/com/android/server/notification/NotificationManagerInternal.java
index 52ddb800fa40..695bf612ccc3 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerInternal.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerInternal.java
@@ -82,4 +82,11 @@ public interface NotificationManagerInternal {
byte[] getBackupPayload(int user, BackupRestoreEventLogger logger);
void applyRestore(byte[] payload, int user, BackupRestoreEventLogger logger);
+
+ /**
+ * Notifies NotificationManager that the system decorations should be removed from the display.
+ *
+ * @param displayId display ID
+ */
+ void onDisplayRemoveSystemDecorations(int displayId);
}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 8948bd196789..6ce1746ed3f6 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -6173,10 +6173,15 @@ public class NotificationManagerService extends SystemService {
}
@Override
- public Map<String, AutomaticZenRule> getAutomaticZenRules() {
+ public ParceledListSlice getAutomaticZenRules() {
int callingUid = Binder.getCallingUid();
enforcePolicyAccess(callingUid, "getAutomaticZenRules");
- return mZenModeHelper.getAutomaticZenRules(getCallingZenUser(), callingUid);
+ List<AutomaticZenRule.AzrWithId> ruleList = new ArrayList<>();
+ for (Map.Entry<String, AutomaticZenRule> rule : mZenModeHelper.getAutomaticZenRules(
+ getCallingZenUser(), callingUid).entrySet()) {
+ ruleList.add(new AutomaticZenRule.AzrWithId(rule.getKey(), rule.getValue()));
+ }
+ return new ParceledListSlice<>(ruleList);
}
@Override
@@ -8209,6 +8214,18 @@ public class NotificationManagerService extends SystemService {
// This can also throw IllegalStateException if called too late.
mZenModeHelper.setDeviceEffectsApplier(applier);
}
+
+ @Override
+ public void onDisplayRemoveSystemDecorations(int displayId) {
+ synchronized (mToastQueue) {
+ for (int i = mToastQueue.size() - 1; i >= 0; i--) {
+ final ToastRecord toast = mToastQueue.get(i);
+ if (toast.displayId == displayId) {
+ cancelToastLocked(i);
+ }
+ }
+ }
+ }
};
private static boolean isBigPictureWithBitmapOrIcon(Notification n) {
@@ -14539,23 +14556,23 @@ public class NotificationManagerService extends SystemService {
*/
private class NotificationTrampolineCallback implements BackgroundActivityStartCallback {
@Override
- public boolean isActivityStartAllowed(Collection<IBinder> tokens, int uid,
- String packageName) {
+ public BackgroundActivityStartCallbackResult isActivityStartAllowed(
+ Collection<IBinder> tokens, int uid, String packageName) {
checkArgument(!tokens.isEmpty());
for (IBinder token : tokens) {
if (token != ALLOWLIST_TOKEN) {
// We only block or warn if the start is exclusively due to notification
- return true;
+ return RESULT_TRUE;
}
}
String logcatMessage =
"Indirect notification activity start (trampoline) from " + packageName;
if (blockTrampoline(uid)) {
Slog.e(TAG, logcatMessage + " blocked");
- return false;
+ return RESULT_FALSE;
} else {
Slog.w(TAG, logcatMessage + ", this should be avoided for performance reasons");
- return true;
+ return new BackgroundActivityStartCallbackResult(true, ALLOWLIST_TOKEN);
}
}
diff --git a/services/core/java/com/android/server/om/OverlayManagerService.java b/services/core/java/com/android/server/om/OverlayManagerService.java
index 8d787fe99e3a..651111e431c3 100644
--- a/services/core/java/com/android/server/om/OverlayManagerService.java
+++ b/services/core/java/com/android/server/om/OverlayManagerService.java
@@ -985,8 +985,8 @@ public final class OverlayManagerService extends SystemService {
final String pkgName = request.overlay.getPackageName();
if (callingUid != Process.ROOT_UID && !ArrayUtils.contains(
mPackageManager.getPackagesForUid(callingUid), pkgName)) {
- throw new IllegalArgumentException("UID " + callingUid + " does own package"
- + "name " + pkgName);
+ throw new IllegalArgumentException("UID " + callingUid + " does not own "
+ + "packageName " + pkgName);
}
} else {
// Enforce actor requirements for enabling, disabling, and reordering overlays.
diff --git a/services/core/java/com/android/server/pm/ComputerEngine.java b/services/core/java/com/android/server/pm/ComputerEngine.java
index b441e9dd561d..7e5ada54c953 100644
--- a/services/core/java/com/android/server/pm/ComputerEngine.java
+++ b/services/core/java/com/android/server/pm/ComputerEngine.java
@@ -1994,6 +1994,9 @@ public class ComputerEngine implements Computer {
if (Process.isSdkSandboxUid(uid)) {
uid = getBaseSdkSandboxUid();
}
+ if(isKnownIsolatedComputeApp(uid)) {
+ uid = getIsolatedOwner(uid);
+ }
final int appId = UserHandle.getAppId(uid);
return getPackagesForUidInternalBody(callingUid, userId, appId, isCallerInstantApp);
}
diff --git a/services/core/java/com/android/server/pm/DeletePackageHelper.java b/services/core/java/com/android/server/pm/DeletePackageHelper.java
index 38aa57f785e5..bbee77ce58ad 100644
--- a/services/core/java/com/android/server/pm/DeletePackageHelper.java
+++ b/services/core/java/com/android/server/pm/DeletePackageHelper.java
@@ -425,7 +425,7 @@ final class DeletePackageHelper {
user == null || user.getIdentifier() == USER_ALL;
if ((!deleteSystem || deleteAllUsers) && disabledPs == null) {
Slog.w(TAG, "Attempt to delete unknown system package "
- + ps.getPkg().getPackageName());
+ + ps.getName());
return null;
}
// Confirmed if the system package has been updated
diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java
index acdc79fb9922..e02ec6a9e3b4 100644
--- a/services/core/java/com/android/server/pm/InstallPackageHelper.java
+++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java
@@ -3077,7 +3077,8 @@ final class InstallPackageHelper {
}
if (succeeded) {
- Slog.i(TAG, "installation completed:" + packageName);
+ Slog.i(TAG, "installation completed for package:" + packageName
+ + ". Final code path: " + pkgSetting.getPath().getPath());
if (Flags.aslInApkAppMetadataSource()
&& pkgSetting.getAppMetadataSource() == APP_METADATA_SOURCE_APK) {
diff --git a/services/core/java/com/android/server/pm/InstallRequest.java b/services/core/java/com/android/server/pm/InstallRequest.java
index 3361dbc2df07..72f2068800d2 100644
--- a/services/core/java/com/android/server/pm/InstallRequest.java
+++ b/services/core/java/com/android/server/pm/InstallRequest.java
@@ -869,6 +869,9 @@ final class InstallRequest {
public void setScannedPackageSettingFirstInstallTimeFromReplaced(
@Nullable PackageStateInternal replacedPkgSetting, int[] userId) {
assertScanResultExists();
+ if (replacedPkgSetting == null) {
+ return;
+ }
mScanResult.mPkgSetting.setFirstInstallTimeFromReplaced(replacedPkgSetting, userId);
}
diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java
index 2dd679818ada..5160319c8cf6 100644
--- a/services/core/java/com/android/server/pm/LauncherAppsService.java
+++ b/services/core/java/com/android/server/pm/LauncherAppsService.java
@@ -379,9 +379,10 @@ public class LauncherAppsService extends SystemService {
public List<UserHandle> getUserProfiles() {
int[] userIds;
if (!canAccessHiddenProfile(getCallingUid(), getCallingPid())) {
- userIds = mUm.getProfileIdsExcludingHidden(getCallingUserId(), /* enabled= */ true);
+ userIds = mUserManagerInternal.getProfileIdsExcludingHidden(getCallingUserId(),
+ /* enabled= */ true);
} else {
- userIds = mUm.getEnabledProfileIds(getCallingUserId());
+ userIds = mUserManagerInternal.getProfileIds(getCallingUserId(), true);
}
final List<UserHandle> result = new ArrayList<>(userIds.length);
for (int userId : userIds) {
@@ -398,9 +399,10 @@ public class LauncherAppsService extends SystemService {
int[] userIds;
if (!canAccessHiddenProfile(callingUid, Binder.getCallingPid())) {
- userIds = mUm.getProfileIdsExcludingHidden(getCallingUserId(), /* enabled= */ true);
+ userIds = mUserManagerInternal.getProfileIdsExcludingHidden(getCallingUserId(),
+ /* enabled= */ true);
} else {
- userIds = mUm.getEnabledProfileIds(getCallingUserId());
+ userIds = mUserManagerInternal.getProfileIds(getCallingUserId(), true);
}
final long token = Binder.clearCallingIdentity();
@@ -503,16 +505,11 @@ public class LauncherAppsService extends SystemService {
return true;
}
- long ident = injectClearCallingIdentity();
- try {
- final UserInfo callingUserInfo = mUm.getUserInfo(callingUserId);
- if (callingUserInfo != null && callingUserInfo.isProfile()) {
- Slog.w(TAG, message + " for another profile "
- + targetUserId + " from " + callingUserId + " not allowed");
- return false;
- }
- } finally {
- injectRestoreCallingIdentity(ident);
+ final UserInfo callingUserInfo = mUserManagerInternal.getUserInfo(callingUserId);
+ if (callingUserInfo != null && callingUserInfo.isProfile()) {
+ Slog.w(TAG, message + " for another profile "
+ + targetUserId + " from " + callingUserId + " not allowed");
+ return false;
}
if (isHiddenProfile(UserHandle.of(targetUserId))
@@ -529,9 +526,9 @@ public class LauncherAppsService extends SystemService {
return false;
}
- long identity = injectClearCallingIdentity();
try {
- UserProperties properties = mUm.getUserProperties(targetUser);
+ UserProperties properties = mUserManagerInternal
+ .getUserProperties(targetUser.getIdentifier());
if (properties == null) {
return false;
}
@@ -540,8 +537,6 @@ public class LauncherAppsService extends SystemService {
== UserProperties.PROFILE_API_VISIBILITY_HIDDEN;
} catch (IllegalArgumentException e) {
return false;
- } finally {
- injectRestoreCallingIdentity(identity);
}
}
@@ -686,7 +681,7 @@ public class LauncherAppsService extends SystemService {
final int callingUid = injectBinderCallingUid();
final long ident = injectClearCallingIdentity();
try {
- if (mUm.getUserInfo(user.getIdentifier()).isManagedProfile()) {
+ if (mUserManagerInternal.getUserInfo(user.getIdentifier()).isManagedProfile()) {
// Managed profile should not show hidden apps
return launcherActivities;
}
@@ -1713,7 +1708,7 @@ public class LauncherAppsService extends SystemService {
}
final long identity = Binder.clearCallingIdentity();
try {
- String userType = mUm.getUserInfo(user.getIdentifier()).userType;
+ String userType = mUserManagerInternal.getUserInfo(user.getIdentifier()).userType;
Set<String> preInstalledPackages = mUm.getPreInstallableSystemPackages(userType);
if (preInstalledPackages == null) {
return new ArrayList<>();
diff --git a/services/core/java/com/android/server/pm/UserManagerInternal.java b/services/core/java/com/android/server/pm/UserManagerInternal.java
index f88681dbcaeb..41ce4fa81668 100644
--- a/services/core/java/com/android/server/pm/UserManagerInternal.java
+++ b/services/core/java/com/android/server/pm/UserManagerInternal.java
@@ -18,6 +18,7 @@ package com.android.server.pm;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SpecialUsers.CanBeNULL;
import android.annotation.UserIdInt;
import android.content.Context;
import android.content.pm.LauncherUserInfo;
@@ -367,6 +368,21 @@ public abstract class UserManagerInternal {
public abstract @NonNull int[] getProfileIds(@UserIdInt int userId, boolean enabledOnly);
/**
+ * Returns a list of the users that are associated with the specified user, including the user
+ * itself. This includes the user, its profiles, its parent, and its parent's other profiles,
+ * as applicable.
+ *
+ * <p>Note that this includes only profile types that are not hidden.
+ *
+ * @param userId id of the user to return profiles for
+ * @param enabledOnly whether return only {@link UserInfo#isEnabled() enabled} profiles
+ * @return A non-empty array of ids of profiles associated with the specified user if the user
+ * exists. Otherwise, an empty array.
+ */
+ public abstract @NonNull int[] getProfileIdsExcludingHidden(@UserIdInt int userId,
+ boolean enabledOnly);
+
+ /**
* Checks if the {@code callingUserId} and {@code targetUserId} are same or in same group
* and that the {@code callingUserId} is not a profile and {@code targetUserId} is enabled.
*
@@ -620,11 +636,17 @@ public abstract class UserManagerInternal {
* Returns the user id of the communal profile, or {@link android.os.UserHandle#USER_NULL}
* if there is no such user.
*/
- public abstract @UserIdInt int getCommunalProfileId();
+ public abstract @CanBeNULL @UserIdInt int getCommunalProfileId();
+
+ /**
+ * Returns the user id of the supervising profile, or {@link android.os.UserHandle#USER_NULL} if
+ * there is no such user.
+ */
+ public abstract @CanBeNULL @UserIdInt int getSupervisingProfileId();
/**
- * Checks whether to show a notification for sounds (e.g., alarms, timers, etc.) from
- * background users.
+ * Checks whether to show a notification for sounds (e.g., alarms, timers, etc.) from background
+ * users.
*/
public static boolean shouldShowNotificationForBackgroundUserSounds() {
return Flags.addUiForSoundsFromBackgroundUsers() && Resources.getSystem().getBoolean(
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 233b577e1c61..0ea9af4b9c38 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -1530,6 +1530,20 @@ public class UserManagerService extends IUserManager.Stub {
return UserHandle.USER_NULL;
}
+ /** Returns the currently-designated supervising profile, or USER_NULL if not present. */
+ private @CanBeNULL @UserIdInt int getSupervisingProfileId() {
+ synchronized (mUsersLock) {
+ final int userSize = mUsers.size();
+ for (int i = 0; i < userSize; i++) {
+ final UserInfo user = mUsers.valueAt(i).info;
+ if (user.isSupervisingProfile() && !mRemovingUserIds.get(user.id)) {
+ return user.id;
+ }
+ }
+ }
+ return UserHandle.USER_NULL;
+ }
+
public @NonNull List<UserInfo> getUsers(boolean excludeDying) {
return getUsers(/*excludePartial= */ true, excludeDying, /* excludePreCreated= */
true);
@@ -7724,6 +7738,7 @@ public class UserManagerService extends IUserManager.Stub {
pw.print(" Has profile owner: ");
pw.println(mIsUserManaged.get(userId));
+
pw.println(" Restrictions:");
synchronized (mRestrictionsLock) {
UserRestrictionsUtils.dumpRestrictions(
@@ -7756,6 +7771,9 @@ public class UserManagerService extends IUserManager.Stub {
}
}
+ pw.print(" Can have profile: ");
+ pw.println(userInfo.canHaveProfile());
+
if (userData.userProperties != null) {
userData.userProperties.println(pw, " ");
}
@@ -8031,6 +8049,14 @@ public class UserManagerService extends IUserManager.Stub {
}
@Override
+ public int[] getProfileIdsExcludingHidden(@UserIdInt int userId, boolean enabledOnly) {
+ synchronized (mUsersLock) {
+ return getProfileIdsLU(userId, null /* userType */, enabledOnly, /* excludeHidden */
+ true).toArray();
+ }
+ }
+
+ @Override
public @Nullable LauncherUserInfo getLauncherUserInfo(@UserIdInt int userId) {
UserInfo userInfo;
synchronized (mUsersLock) {
@@ -8344,10 +8370,14 @@ public class UserManagerService extends IUserManager.Stub {
}
@Override
- public @UserIdInt int getCommunalProfileId() {
+ public @CanBeNULL @UserIdInt int getCommunalProfileId() {
return getCommunalProfileIdUnchecked();
}
+ @Override
+ public @CanBeNULL @UserIdInt int getSupervisingProfileId() {
+ return UserManagerService.this.getSupervisingProfileId();
+ }
} // class LocalService
diff --git a/services/core/java/com/android/server/pm/permission/AccessCheckDelegate.java b/services/core/java/com/android/server/pm/permission/AccessCheckDelegate.java
index e989d6875d15..4c14e96e6c98 100644
--- a/services/core/java/com/android/server/pm/permission/AccessCheckDelegate.java
+++ b/services/core/java/com/android/server/pm/permission/AccessCheckDelegate.java
@@ -36,6 +36,7 @@ import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.SparseArray;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.function.DodecFunction;
import com.android.internal.util.function.HexConsumer;
@@ -141,150 +142,191 @@ public interface AccessCheckDelegate extends CheckPermissionDelegate, CheckOpsDe
class AccessCheckDelegateImpl implements AccessCheckDelegate {
public static final String SHELL_PKG = "com.android.shell";
+
+ private final Object mLock = new Object();
+
+ @GuardedBy("mLock")
private int mDelegateAndOwnerUid = INVALID_UID;
@Nullable
+ @GuardedBy("mLock")
private String mDelegatePackage;
@Nullable
+ @GuardedBy("mLock")
private String[] mDelegatePermissions;
+ @GuardedBy("mLock")
boolean mDelegateAllPermissions;
@Nullable
+ @GuardedBy("mLock")
private SparseArray<ArrayMap<String, Integer>> mOverridePermissionStates;
@Override
public void setShellPermissionDelegate(int uid, @NonNull String packageName,
@Nullable String[] permissions) {
- mDelegateAndOwnerUid = uid;
- mDelegatePackage = packageName;
- mDelegatePermissions = permissions;
- mDelegateAllPermissions = permissions == null;
+ synchronized (mLock) {
+ mDelegateAndOwnerUid = uid;
+ mDelegatePackage = packageName;
+ mDelegatePermissions = permissions;
+ mDelegateAllPermissions = permissions == null;
+ }
PackageManager.invalidatePackageInfoCache();
}
@Override
public void removeShellPermissionDelegate() {
- mDelegatePackage = null;
- mDelegatePermissions = null;
- mDelegateAllPermissions = false;
+ synchronized (mLock) {
+ mDelegatePackage = null;
+ mDelegatePermissions = null;
+ mDelegateAllPermissions = false;
+ }
PackageManager.invalidatePackageInfoCache();
}
@Override
public void addOverridePermissionState(int ownerUid, int uid, @NonNull String permission,
int state) {
- if (mOverridePermissionStates == null) {
- mDelegateAndOwnerUid = ownerUid;
- mOverridePermissionStates = new SparseArray<>();
- }
+ synchronized (mLock) {
+ if (mOverridePermissionStates == null) {
+ mDelegateAndOwnerUid = ownerUid;
+ mOverridePermissionStates = new SparseArray<>();
+ }
- int uidIdx = mOverridePermissionStates.indexOfKey(uid);
- ArrayMap<String, Integer> perUidOverrides;
- if (uidIdx < 0) {
- perUidOverrides = new ArrayMap<>();
- mOverridePermissionStates.put(uid, perUidOverrides);
- } else {
- perUidOverrides = mOverridePermissionStates.valueAt(uidIdx);
- }
+ int uidIdx = mOverridePermissionStates.indexOfKey(uid);
+ ArrayMap<String, Integer> perUidOverrides;
+ if (uidIdx < 0) {
+ perUidOverrides = new ArrayMap<>();
+ mOverridePermissionStates.put(uid, perUidOverrides);
+ } else {
+ perUidOverrides = mOverridePermissionStates.valueAt(uidIdx);
+ }
- perUidOverrides.put(permission, state);
+ perUidOverrides.put(permission, state);
+ }
PackageManager.invalidatePackageInfoCache();
}
@Override
public void removeOverridePermissionState(int uid, @NonNull String permission) {
- if (mOverridePermissionStates == null) {
- return;
- }
+ synchronized (mLock) {
+ if (mOverridePermissionStates == null) {
+ return;
+ }
- ArrayMap<String, Integer> perUidOverrides = mOverridePermissionStates.get(uid);
+ ArrayMap<String, Integer> perUidOverrides = mOverridePermissionStates.get(uid);
- if (perUidOverrides == null) {
- return;
- }
+ if (perUidOverrides == null) {
+ return;
+ }
- perUidOverrides.remove(permission);
- PackageManager.invalidatePackageInfoCache();
+ perUidOverrides.remove(permission);
- if (perUidOverrides.isEmpty()) {
- mOverridePermissionStates.remove(uid);
- }
- if (mOverridePermissionStates.size() == 0) {
- mOverridePermissionStates = null;
+ if (perUidOverrides.isEmpty()) {
+ mOverridePermissionStates.remove(uid);
+ }
+ if (mOverridePermissionStates.size() == 0) {
+ mOverridePermissionStates = null;
+ }
}
+ PackageManager.invalidatePackageInfoCache();
}
@Override
public void clearOverridePermissionStates(int uid) {
- if (mOverridePermissionStates == null) {
- return;
- }
+ synchronized (mLock) {
+ if (mOverridePermissionStates == null) {
+ return;
+ }
- mOverridePermissionStates.remove(uid);
- PackageManager.invalidatePackageInfoCache();
+ mOverridePermissionStates.remove(uid);
- if (mOverridePermissionStates.size() == 0) {
- mOverridePermissionStates = null;
+ if (mOverridePermissionStates.size() == 0) {
+ mOverridePermissionStates = null;
+ }
}
+ PackageManager.invalidatePackageInfoCache();
}
@Override
public void clearAllOverridePermissionStates() {
- mOverridePermissionStates = null;
+ synchronized (mLock) {
+ mOverridePermissionStates = null;
+ }
PackageManager.invalidatePackageInfoCache();
}
@Override
public List<String> getDelegatedPermissionNames() {
- return mDelegatePermissions == null ? null : List.of(mDelegatePermissions);
+ synchronized (mLock) {
+ return mDelegatePermissions == null ? null : List.of(mDelegatePermissions);
+ }
}
@Override
public boolean hasShellPermissionDelegate() {
- return mDelegateAllPermissions || mDelegatePermissions != null;
+ synchronized (mLock) {
+ return mDelegateAllPermissions || mDelegatePermissions != null;
+ }
}
@Override
public boolean isDelegatePackage(int uid, @NonNull String packageName) {
- return mDelegateAndOwnerUid == uid && TextUtils.equals(mDelegatePackage, packageName);
+ synchronized (mLock) {
+ return mDelegateAndOwnerUid == uid
+ && TextUtils.equals(mDelegatePackage, packageName);
+ }
}
@Override
public boolean hasOverriddenPermissions() {
- return mOverridePermissionStates != null;
+ synchronized (mLock) {
+ return mOverridePermissionStates != null;
+ }
}
@Override
public boolean isDelegateAndOwnerUid(int uid) {
- return uid == mDelegateAndOwnerUid;
+ synchronized (mLock) {
+ return uid == mDelegateAndOwnerUid;
+ }
}
@Override
public boolean hasDelegateOrOverrides() {
- return hasShellPermissionDelegate() || hasOverriddenPermissions();
+ synchronized (mLock) {
+ return hasShellPermissionDelegate() || hasOverriddenPermissions();
+ }
}
@Override
public int checkPermission(@NonNull String packageName, @NonNull String permissionName,
@NonNull String persistentDeviceId, @UserIdInt int userId,
@NonNull QuadFunction<String, String, String, Integer, Integer> superImpl) {
- if (TextUtils.equals(mDelegatePackage, packageName) && !SHELL_PKG.equals(packageName)) {
- if (isDelegatePermission(permissionName)) {
- final long identity = Binder.clearCallingIdentity();
- try {
- return checkPermission(SHELL_PKG, permissionName, persistentDeviceId,
- userId, superImpl);
- } finally {
- Binder.restoreCallingIdentity(identity);
+ boolean useShellDelegate;
+
+ synchronized (mLock) {
+ useShellDelegate = !SHELL_PKG.equals(packageName)
+ && TextUtils.equals(mDelegatePackage, packageName)
+ && isDelegatePermission(permissionName);
+
+ if (!useShellDelegate && mOverridePermissionStates != null) {
+ int uid = LocalServices.getService(PackageManagerInternal.class)
+ .getPackageUid(packageName, 0, userId);
+ if (uid >= 0) {
+ Map<String, Integer> permissionGrants = mOverridePermissionStates.get(uid);
+ if (permissionGrants != null
+ && permissionGrants.containsKey(permissionName)) {
+ return permissionGrants.get(permissionName);
+ }
}
}
}
- if (mOverridePermissionStates != null) {
- int uid = LocalServices.getService(PackageManagerInternal.class)
- .getPackageUid(packageName, 0, userId);
- if (uid >= 0) {
- Map<String, Integer> permissionGrants = mOverridePermissionStates.get(uid);
- if (permissionGrants != null && permissionGrants.containsKey(permissionName)) {
- return permissionGrants.get(permissionName);
- }
+
+ if (useShellDelegate) {
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ return checkPermission(SHELL_PKG, permissionName, persistentDeviceId, userId,
+ superImpl);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
}
}
return superImpl.apply(packageName, permissionName, persistentDeviceId, userId);
@@ -294,21 +336,27 @@ public interface AccessCheckDelegate extends CheckPermissionDelegate, CheckOpsDe
public int checkUidPermission(int uid, @NonNull String permissionName,
@NonNull String persistentDeviceId,
@NonNull TriFunction<Integer, String, String, Integer> superImpl) {
- if (uid == mDelegateAndOwnerUid && uid != Process.SHELL_UID) {
- if (isDelegatePermission(permissionName)) {
- final long identity = Binder.clearCallingIdentity();
- try {
- return checkUidPermission(Process.SHELL_UID, permissionName,
- persistentDeviceId, superImpl);
- } finally {
- Binder.restoreCallingIdentity(identity);
+ boolean useShellDelegate;
+
+ synchronized (mLock) {
+ useShellDelegate = uid != Process.SHELL_UID && uid == mDelegateAndOwnerUid
+ && isDelegatePermission(permissionName);
+
+ if (!useShellDelegate && mOverridePermissionStates != null) {
+ Map<String, Integer> permissionGrants = mOverridePermissionStates.get(uid);
+ if (permissionGrants != null && permissionGrants.containsKey(permissionName)) {
+ return permissionGrants.get(permissionName);
}
}
}
- if (mOverridePermissionStates != null) {
- Map<String, Integer> permissionGrants = mOverridePermissionStates.get(uid);
- if (permissionGrants != null && permissionGrants.containsKey(permissionName)) {
- return permissionGrants.get(permissionName);
+
+ if (useShellDelegate) {
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ return checkUidPermission(Process.SHELL_UID, permissionName, persistentDeviceId,
+ superImpl);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
}
}
return superImpl.apply(uid, permissionName, persistentDeviceId);
@@ -319,7 +367,13 @@ public interface AccessCheckDelegate extends CheckPermissionDelegate, CheckOpsDe
@Nullable String attributionTag, int virtualDeviceId, boolean raw,
@NonNull HexFunction<Integer, Integer, String, String, Integer, Boolean, Integer>
superImpl) {
- if (uid == mDelegateAndOwnerUid && isDelegateOp(code)) {
+ boolean useShellDelegate;
+
+ synchronized (mLock) {
+ useShellDelegate = uid == mDelegateAndOwnerUid && isDelegateOp(code);
+ }
+
+ if (useShellDelegate) {
final int shellUid = UserHandle.getUid(UserHandle.getUserId(uid),
Process.SHELL_UID);
final long identity = Binder.clearCallingIdentity();
@@ -335,7 +389,13 @@ public interface AccessCheckDelegate extends CheckPermissionDelegate, CheckOpsDe
@Override
public int checkAudioOperation(int code, int usage, int uid, @Nullable String packageName,
@NonNull QuadFunction<Integer, Integer, Integer, String, Integer> superImpl) {
- if (uid == mDelegateAndOwnerUid && isDelegateOp(code)) {
+ boolean useShellDelegate;
+
+ synchronized (mLock) {
+ useShellDelegate = uid == mDelegateAndOwnerUid && isDelegateOp(code);
+ }
+
+ if (useShellDelegate) {
final int shellUid = UserHandle.getUid(UserHandle.getUserId(uid),
Process.SHELL_UID);
final long identity = Binder.clearCallingIdentity();
@@ -354,7 +414,13 @@ public interface AccessCheckDelegate extends CheckPermissionDelegate, CheckOpsDe
@Nullable String message, boolean shouldCollectMessage, int notedCount,
@NonNull NonaFunction<Integer, Integer, String, String, Integer, Boolean, String,
Boolean, Integer, SyncNotedAppOp> superImpl) {
- if (uid == mDelegateAndOwnerUid && isDelegateOp(code)) {
+ boolean useShellDelegate;
+
+ synchronized (mLock) {
+ useShellDelegate = uid == mDelegateAndOwnerUid && isDelegateOp(code);
+ }
+
+ if (useShellDelegate) {
final int shellUid = UserHandle.getUid(UserHandle.getUserId(uid),
Process.SHELL_UID);
final long identity = Binder.clearCallingIdentity();
@@ -375,21 +441,29 @@ public interface AccessCheckDelegate extends CheckPermissionDelegate, CheckOpsDe
@Nullable String message, boolean shouldCollectMessage, boolean skiProxyOperation,
@NonNull HexFunction<Integer, AttributionSource, Boolean, String, Boolean,
Boolean, SyncNotedAppOp> superImpl) {
- if (!isDelegateOp(code)) {
- return superImpl.apply(code, attributionSource, shouldCollectAsyncNotedOp,
- message, shouldCollectMessage, skiProxyOperation);
+ boolean isDelegateOp;
+ int delegateAndOwnerUid;
+
+ synchronized (mLock) {
+ isDelegateOp = isDelegateOp(code);
+ delegateAndOwnerUid = mDelegateAndOwnerUid;
+ }
+
+ if (!isDelegateOp) {
+ return superImpl.apply(code, attributionSource, shouldCollectAsyncNotedOp, message,
+ shouldCollectMessage, skiProxyOperation);
}
final int shellUid = UserHandle.getUid(
UserHandle.getUserId(attributionSource.getUid()), Process.SHELL_UID);
AttributionSource next = attributionSource.getNext();
- if (next != null && next.getUid() == mDelegateAndOwnerUid) {
+ if (next != null && next.getUid() == delegateAndOwnerUid) {
next = new AttributionSource(shellUid, Process.INVALID_PID, SHELL_PKG,
next.getAttributionTag(), next.getToken(), /*renouncedPermissions*/ null,
next.getDeviceId(), next.getNext());
attributionSource = new AttributionSource(attributionSource, next);
}
- if (attributionSource.getUid() == mDelegateAndOwnerUid) {
+ if (attributionSource.getUid() == delegateAndOwnerUid) {
attributionSource = new AttributionSource(shellUid, Process.INVALID_PID, SHELL_PKG,
attributionSource.getAttributionTag(),
attributionSource.getToken(), /*renouncedPermissions*/ null,
@@ -397,9 +471,8 @@ public interface AccessCheckDelegate extends CheckPermissionDelegate, CheckOpsDe
}
final long identity = Binder.clearCallingIdentity();
try {
- return superImpl.apply(code, attributionSource,
- shouldCollectAsyncNotedOp, message, shouldCollectMessage,
- skiProxyOperation);
+ return superImpl.apply(code, attributionSource, shouldCollectAsyncNotedOp, message,
+ shouldCollectMessage, skiProxyOperation);
} finally {
Binder.restoreCallingIdentity(identity);
}
@@ -413,7 +486,13 @@ public interface AccessCheckDelegate extends CheckPermissionDelegate, CheckOpsDe
@AttributionFlags int attributionFlags, int attributionChainId,
@NonNull DodecFunction<IBinder, Integer, Integer, String, String, Integer, Boolean,
Boolean, String, Boolean, Integer, Integer, SyncNotedAppOp> superImpl) {
- if (uid == mDelegateAndOwnerUid && isDelegateOp(code)) {
+ boolean useShellDelegate;
+
+ synchronized (mLock) {
+ useShellDelegate = uid == mDelegateAndOwnerUid && isDelegateOp(code);
+ }
+
+ if (useShellDelegate) {
final int shellUid = UserHandle.getUid(UserHandle.getUserId(uid),
Process.SHELL_UID);
final long identity = Binder.clearCallingIdentity();
@@ -440,7 +519,14 @@ public interface AccessCheckDelegate extends CheckPermissionDelegate, CheckOpsDe
@NonNull UndecFunction<IBinder, Integer, AttributionSource, Boolean,
Boolean, String, Boolean, Boolean, Integer, Integer, Integer,
SyncNotedAppOp> superImpl) {
- if (attributionSource.getUid() == mDelegateAndOwnerUid && isDelegateOp(code)) {
+ boolean useShellDelegate;
+
+ synchronized (mLock) {
+ useShellDelegate = attributionSource.getUid() == mDelegateAndOwnerUid
+ && isDelegateOp(code);
+ }
+
+ if (useShellDelegate) {
final int shellUid = UserHandle.getUid(UserHandle.getUserId(
attributionSource.getUid()), Process.SHELL_UID);
final long identity = Binder.clearCallingIdentity();
@@ -467,7 +553,14 @@ public interface AccessCheckDelegate extends CheckPermissionDelegate, CheckOpsDe
@NonNull AttributionSource attributionSource, boolean skipProxyOperation,
@NonNull QuadFunction<IBinder, Integer, AttributionSource, Boolean,
Void> superImpl) {
- if (attributionSource.getUid() == mDelegateAndOwnerUid && isDelegateOp(code)) {
+ boolean useShellDelegate;
+
+ synchronized (mLock) {
+ useShellDelegate = attributionSource.getUid() == mDelegateAndOwnerUid
+ && isDelegateOp(code);
+ }
+
+ if (useShellDelegate) {
final int shellUid = UserHandle.getUid(UserHandle.getUserId(
attributionSource.getUid()), Process.SHELL_UID);
final long identity = Binder.clearCallingIdentity();
@@ -490,7 +583,13 @@ public interface AccessCheckDelegate extends CheckPermissionDelegate, CheckOpsDe
public void finishOperation(IBinder clientId, int code, int uid, String packageName,
String attributionTag, int virtualDeviceId, @NonNull HexConsumer<IBinder, Integer,
Integer, String, String, Integer> superImpl) {
- if (uid == mDelegateAndOwnerUid && isDelegateOp(code)) {
+ boolean useShellDelegate;
+
+ synchronized (mLock) {
+ useShellDelegate = uid == mDelegateAndOwnerUid && isDelegateOp(code);
+ }
+
+ if (useShellDelegate) {
final int shellUid =
UserHandle.getUid(UserHandle.getUserId(uid), Process.SHELL_UID);
final long identity = Binder.clearCallingIdentity();
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 3230e891db55..8cf0481b1dc3 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -86,6 +86,7 @@ import static android.view.contentprotection.flags.Flags.createAccessibilityOver
import static com.android.hardware.input.Flags.enableNew25q2Keycodes;
import static com.android.hardware.input.Flags.enableTalkbackAndMagnifierKeyGestures;
import static com.android.hardware.input.Flags.enableVoiceAccessKeyGestures;
+import static com.android.hardware.input.Flags.fixSearchModifierFallbacks;
import static com.android.hardware.input.Flags.inputManagerLifecycleSupport;
import static com.android.hardware.input.Flags.keyboardA11yShortcutControl;
import static com.android.hardware.input.Flags.modifierShortcutDump;
@@ -115,7 +116,6 @@ import static com.android.server.wm.WindowManagerPolicyProto.SCREEN_ON_FULLY;
import static com.android.server.wm.WindowManagerPolicyProto.WINDOW_MANAGER_DRAW_COMPLETE;
import android.accessibilityservice.AccessibilityService;
-import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SuppressLint;
import android.app.ActivityManager;
@@ -268,6 +268,7 @@ import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.PrintWriter;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
@@ -311,6 +312,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
static final int SHORT_PRESS_POWER_LOCK_OR_SLEEP = 6;
static final int SHORT_PRESS_POWER_DREAM_OR_SLEEP = 7;
static final int SHORT_PRESS_POWER_HUB_OR_DREAM_OR_SLEEP = 8;
+ static final int SHORT_PRESS_POWER_DREAM_OR_AWAKE_OR_SLEEP = 9;
// must match: config_LongPressOnPowerBehavior in config.xml
// The config value can be overridden using Settings.Global.POWER_BUTTON_LONG_PRESS
@@ -1234,8 +1236,10 @@ public class PhoneWindowManager implements WindowManagerPolicy {
break;
}
case SHORT_PRESS_POWER_DREAM_OR_SLEEP: {
- attemptToDreamFromShortPowerButtonPress(
- true,
+ attemptToDreamOrAwakeFromShortPowerButtonPress(
+ /* isScreenOn */ true,
+ /* awakeWhenDream */ false,
+ /* noDreamAction */
() -> sleepDefaultDisplayFromPowerButton(eventTime, 0));
break;
}
@@ -1269,13 +1273,22 @@ public class PhoneWindowManager implements WindowManagerPolicy {
lockNow(options);
} else {
// If the hub cannot be run, attempt to dream instead.
- attemptToDreamFromShortPowerButtonPress(
+ attemptToDreamOrAwakeFromShortPowerButtonPress(
/* isScreenOn */ true,
+ /* awakeWhenDream */ false,
/* noDreamAction */
() -> sleepDefaultDisplayFromPowerButton(eventTime, 0));
}
break;
}
+ case SHORT_PRESS_POWER_DREAM_OR_AWAKE_OR_SLEEP: {
+ attemptToDreamOrAwakeFromShortPowerButtonPress(
+ /* isScreenOn */ true,
+ /* awakeWhenDream */ true,
+ /* noDreamAction */
+ () -> sleepDefaultDisplayFromPowerButton(eventTime, 0));
+ break;
+ }
}
}
}
@@ -1319,15 +1332,18 @@ public class PhoneWindowManager implements WindowManagerPolicy {
}
/**
- * Attempt to dream from a power button press.
+ * Attempt to dream, awake or sleep from a power button press.
*
* @param isScreenOn Whether the screen is currently on.
+ * @param awakeWhenDream When it's set to {@code true}, awake the device from dreaming.
+ * Otherwise, go to sleep.
* @param noDreamAction The action to perform if dreaming is not possible.
*/
- private void attemptToDreamFromShortPowerButtonPress(
- boolean isScreenOn, Runnable noDreamAction) {
+ private void attemptToDreamOrAwakeFromShortPowerButtonPress(
+ boolean isScreenOn, boolean awakeWhenDream, Runnable noDreamAction) {
if (mShortPressOnPowerBehavior != SHORT_PRESS_POWER_DREAM_OR_SLEEP
- && mShortPressOnPowerBehavior != SHORT_PRESS_POWER_HUB_OR_DREAM_OR_SLEEP) {
+ && mShortPressOnPowerBehavior != SHORT_PRESS_POWER_HUB_OR_DREAM_OR_SLEEP
+ && mShortPressOnPowerBehavior != SHORT_PRESS_POWER_DREAM_OR_AWAKE_OR_SLEEP) {
// If the power button behavior isn't one that should be able to trigger the dream, give
// up.
noDreamAction.run();
@@ -1335,9 +1351,24 @@ public class PhoneWindowManager implements WindowManagerPolicy {
}
final DreamManagerInternal dreamManagerInternal = getDreamManagerInternal();
- if (dreamManagerInternal == null || !dreamManagerInternal.canStartDreaming(isScreenOn)) {
- Slog.d(TAG, "Can't start dreaming when attempting to dream from short power"
- + " press (isScreenOn=" + isScreenOn + ")");
+ if (dreamManagerInternal == null) {
+ Slog.d(TAG,
+ "Can't access dream manager dreaming when attempting to start or stop dream "
+ + "from short power press (isScreenOn="
+ + isScreenOn + ", awakeWhenDream=" + awakeWhenDream + ")");
+ noDreamAction.run();
+ return;
+ }
+
+ if (!dreamManagerInternal.canStartDreaming(isScreenOn)) {
+ if (awakeWhenDream && dreamManagerInternal.isDreaming()) {
+ dreamManagerInternal.stopDream(false /*immediate*/, "short press power" /*reason*/);
+ return;
+ }
+ Slog.d(TAG,
+ "Can't start dreaming and the device is not dreaming when attempting to start "
+ + "or stop dream from short power press (isScreenOn="
+ + isScreenOn + ", awakeWhenDream=" + awakeWhenDream + ")");
noDreamAction.run();
return;
}
@@ -2312,6 +2343,10 @@ public class PhoneWindowManager implements WindowManagerPolicy {
return ActivityManager.getService();
}
+ LockPatternUtils getLockPatternUtils() {
+ return new LockPatternUtils(mContext);
+ }
+
ButtonOverridePermissionChecker getButtonOverridePermissionChecker() {
return new ButtonOverridePermissionChecker();
}
@@ -2360,7 +2395,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
mAccessibilityShortcutController = injector.getAccessibilityShortcutController(
mContext, new Handler(), mCurrentUserId);
mGlobalActionsFactory = injector.getGlobalActionsFactory();
- mLockPatternUtils = new LockPatternUtils(mContext);
+ mLockPatternUtils = injector.getLockPatternUtils();
mLogger = new MetricsLogger();
Resources res = mContext.getResources();
@@ -4147,6 +4182,13 @@ public class PhoneWindowManager implements WindowManagerPolicy {
return true;
}
+ if (fixSearchModifierFallbacks()) {
+ // Pass event as unhandled to give other services, e.g. InputManagerService, the
+ // opportunity to determine if the event can be modified, e.g. generating a fallback for
+ // meta/search events.
+ return false;
+ }
+
// Reserve all the META modifier combos for system behavior
return (metaState & KeyEvent.META_META_ON) != 0;
}
@@ -4240,19 +4282,51 @@ public class PhoneWindowManager implements WindowManagerPolicy {
if (!useKeyGestureEventHandler()) {
return;
}
- mInputManager.registerKeyGestureEventHandler((event, focusedToken) -> {
- boolean handled = PhoneWindowManager.this.handleKeyGestureEvent(event,
- focusedToken);
- if (handled && !event.isCancelled() && Arrays.stream(event.getKeycodes()).anyMatch(
- (keycode) -> keycode == KeyEvent.KEYCODE_POWER)) {
- mPowerKeyHandled = true;
- }
- return handled;
- });
+ List<Integer> supportedGestures = new ArrayList<>(List.of(
+ KeyGestureEvent.KEY_GESTURE_TYPE_RECENT_APPS,
+ KeyGestureEvent.KEY_GESTURE_TYPE_APP_SWITCH,
+ KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_ASSISTANT,
+ KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_VOICE_ASSISTANT,
+ KeyGestureEvent.KEY_GESTURE_TYPE_HOME,
+ KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_SYSTEM_SETTINGS,
+ KeyGestureEvent.KEY_GESTURE_TYPE_LOCK_SCREEN,
+ KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_NOTIFICATION_PANEL,
+ KeyGestureEvent.KEY_GESTURE_TYPE_TAKE_SCREENSHOT,
+ KeyGestureEvent.KEY_GESTURE_TYPE_TRIGGER_BUG_REPORT,
+ KeyGestureEvent.KEY_GESTURE_TYPE_BACK,
+ KeyGestureEvent.KEY_GESTURE_TYPE_MULTI_WINDOW_NAVIGATION,
+ KeyGestureEvent.KEY_GESTURE_TYPE_DESKTOP_MODE,
+ KeyGestureEvent.KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION_LEFT,
+ KeyGestureEvent.KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION_RIGHT,
+ KeyGestureEvent.KEY_GESTURE_TYPE_OPEN_SHORTCUT_HELPER,
+ KeyGestureEvent.KEY_GESTURE_TYPE_BRIGHTNESS_UP,
+ KeyGestureEvent.KEY_GESTURE_TYPE_BRIGHTNESS_DOWN,
+ KeyGestureEvent.KEY_GESTURE_TYPE_RECENT_APPS_SWITCHER,
+ KeyGestureEvent.KEY_GESTURE_TYPE_ALL_APPS,
+ KeyGestureEvent.KEY_GESTURE_TYPE_ACCESSIBILITY_ALL_APPS,
+ KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_SEARCH,
+ KeyGestureEvent.KEY_GESTURE_TYPE_LANGUAGE_SWITCH,
+ KeyGestureEvent.KEY_GESTURE_TYPE_ACCESSIBILITY_SHORTCUT,
+ KeyGestureEvent.KEY_GESTURE_TYPE_CLOSE_ALL_DIALOGS,
+ KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION,
+ KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_DO_NOT_DISTURB,
+ KeyGestureEvent.KEY_GESTURE_TYPE_SCREENSHOT_CHORD,
+ KeyGestureEvent.KEY_GESTURE_TYPE_RINGER_TOGGLE_CHORD,
+ KeyGestureEvent.KEY_GESTURE_TYPE_GLOBAL_ACTIONS,
+ KeyGestureEvent.KEY_GESTURE_TYPE_TV_TRIGGER_BUG_REPORT
+ ));
+ if (enableTalkbackAndMagnifierKeyGestures()) {
+ supportedGestures.add(KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_TALKBACK);
+ }
+ if (enableVoiceAccessKeyGestures()) {
+ supportedGestures.add(KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_VOICE_ACCESS);
+ }
+ mInputManager.registerKeyGestureEventHandler(supportedGestures,
+ PhoneWindowManager.this::handleKeyGestureEvent);
}
@VisibleForTesting
- boolean handleKeyGestureEvent(KeyGestureEvent event, IBinder focusedToken) {
+ void handleKeyGestureEvent(KeyGestureEvent event, IBinder focusedToken) {
boolean start = event.getAction() == KeyGestureEvent.ACTION_GESTURE_START;
boolean complete = event.getAction() == KeyGestureEvent.ACTION_GESTURE_COMPLETE
&& !event.isCancelled();
@@ -4262,12 +4336,16 @@ public class PhoneWindowManager implements WindowManagerPolicy {
int modifierState = event.getModifierState();
boolean keyguardOn = keyguardOn();
boolean canLaunchApp = isUserSetupComplete() && !keyguardOn;
+ if (!event.isCancelled() && Arrays.stream(event.getKeycodes()).anyMatch(
+ (keycode) -> keycode == KeyEvent.KEYCODE_POWER)) {
+ mPowerKeyHandled = true;
+ }
switch (gestureType) {
case KeyGestureEvent.KEY_GESTURE_TYPE_RECENT_APPS:
if (complete) {
showRecentApps(false);
}
- return true;
+ break;
case KeyGestureEvent.KEY_GESTURE_TYPE_APP_SWITCH:
if (!keyguardOn) {
if (start) {
@@ -4276,7 +4354,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
toggleRecentApps();
}
}
- return true;
+ break;
case KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_ASSISTANT:
case KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_VOICE_ASSISTANT:
if (complete && canLaunchApp) {
@@ -4284,33 +4362,33 @@ public class PhoneWindowManager implements WindowManagerPolicy {
deviceId, SystemClock.uptimeMillis(),
AssistUtils.INVOCATION_TYPE_UNKNOWN);
}
- return true;
+ break;
case KeyGestureEvent.KEY_GESTURE_TYPE_HOME:
if (complete) {
// Post to main thread to avoid blocking input pipeline.
mHandler.post(() -> handleShortPressOnHome(displayId));
}
- return true;
+ break;
case KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_SYSTEM_SETTINGS:
if (complete && canLaunchApp) {
showSystemSettings();
}
- return true;
+ break;
case KeyGestureEvent.KEY_GESTURE_TYPE_LOCK_SCREEN:
if (complete) {
lockNow(null /* options */);
}
- return true;
+ break;
case KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_NOTIFICATION_PANEL:
if (complete) {
toggleNotificationPanel();
}
- return true;
+ break;
case KeyGestureEvent.KEY_GESTURE_TYPE_TAKE_SCREENSHOT:
if (complete) {
interceptScreenshotChord(SCREENSHOT_KEY_OTHER, 0 /*pressDelay*/);
}
- return true;
+ break;
case KeyGestureEvent.KEY_GESTURE_TYPE_TRIGGER_BUG_REPORT:
if (complete && mEnableBugReportKeyboardShortcut) {
try {
@@ -4321,12 +4399,12 @@ public class PhoneWindowManager implements WindowManagerPolicy {
Slog.d(TAG, "Error taking bugreport", e);
}
}
- return true;
+ break;
case KeyGestureEvent.KEY_GESTURE_TYPE_BACK:
if (complete) {
injectBackGesture(SystemClock.uptimeMillis());
}
- return true;
+ break;
case KeyGestureEvent.KEY_GESTURE_TYPE_MULTI_WINDOW_NAVIGATION:
if (complete) {
StatusBarManagerInternal statusbar = getStatusBarManagerInternal();
@@ -4335,7 +4413,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
getTargetDisplayIdForKeyGestureEvent(event));
}
}
- return true;
+ break;
case KeyGestureEvent.KEY_GESTURE_TYPE_DESKTOP_MODE:
if (complete) {
StatusBarManagerInternal statusbar = getStatusBarManagerInternal();
@@ -4344,24 +4422,24 @@ public class PhoneWindowManager implements WindowManagerPolicy {
getTargetDisplayIdForKeyGestureEvent(event));
}
}
- return true;
+ break;
case KeyGestureEvent.KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION_LEFT:
if (complete) {
moveFocusedTaskToStageSplit(getTargetDisplayIdForKeyGestureEvent(event),
true /* leftOrTop */);
}
- return true;
+ break;
case KeyGestureEvent.KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION_RIGHT:
if (complete) {
moveFocusedTaskToStageSplit(getTargetDisplayIdForKeyGestureEvent(event),
false /* leftOrTop */);
}
- return true;
+ break;
case KeyGestureEvent.KEY_GESTURE_TYPE_OPEN_SHORTCUT_HELPER:
if (complete) {
toggleKeyboardShortcutsMenu(deviceId);
}
- return true;
+ break;
case KeyGestureEvent.KEY_GESTURE_TYPE_BRIGHTNESS_UP:
case KeyGestureEvent.KEY_GESTURE_TYPE_BRIGHTNESS_DOWN:
if (complete) {
@@ -4369,32 +4447,32 @@ public class PhoneWindowManager implements WindowManagerPolicy {
gestureType == KeyGestureEvent.KEY_GESTURE_TYPE_BRIGHTNESS_UP ? 1 : -1;
changeDisplayBrightnessValue(displayId, direction);
}
- return true;
+ break;
case KeyGestureEvent.KEY_GESTURE_TYPE_RECENT_APPS_SWITCHER:
if (start) {
showRecentApps(true);
} else {
hideRecentApps(true, false);
}
- return true;
+ break;
case KeyGestureEvent.KEY_GESTURE_TYPE_ALL_APPS:
case KeyGestureEvent.KEY_GESTURE_TYPE_ACCESSIBILITY_ALL_APPS:
if (complete && isKeyEventForCurrentUser(event.getDisplayId(),
event.getKeycodes()[0], "launchAllAppsViaA11y")) {
launchAllAppsAction();
}
- return true;
+ break;
case KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_SEARCH:
if (complete && canLaunchApp) {
launchTargetSearchActivity();
}
- return true;
+ break;
case KeyGestureEvent.KEY_GESTURE_TYPE_LANGUAGE_SWITCH:
if (complete) {
int direction = (modifierState & KeyEvent.META_SHIFT_MASK) != 0 ? -1 : 1;
sendSwitchKeyboardLayout(displayId, focusedToken, direction);
}
- return true;
+ break;
case KeyGestureEvent.KEY_GESTURE_TYPE_SCREENSHOT_CHORD:
if (start) {
// Screenshot chord is pressed: Wait for long press delay before taking
@@ -4404,14 +4482,14 @@ public class PhoneWindowManager implements WindowManagerPolicy {
} else {
cancelPendingScreenshotChordAction();
}
- return true;
+ break;
case KeyGestureEvent.KEY_GESTURE_TYPE_RINGER_TOGGLE_CHORD:
if (start) {
interceptRingerToggleChord();
} else {
cancelPendingRingerToggleChordAction();
}
- return true;
+ break;
case KeyGestureEvent.KEY_GESTURE_TYPE_GLOBAL_ACTIONS:
if (start) {
performHapticFeedback(
@@ -4421,40 +4499,34 @@ public class PhoneWindowManager implements WindowManagerPolicy {
} else {
cancelGlobalActionsAction();
}
- return true;
+ break;
case KeyGestureEvent.KEY_GESTURE_TYPE_TV_TRIGGER_BUG_REPORT:
if (start) {
interceptBugreportGestureTv();
} else {
cancelBugreportGestureTv();
}
- return true;
+ break;
case KeyGestureEvent.KEY_GESTURE_TYPE_ACCESSIBILITY_SHORTCUT:
if (complete && mAccessibilityShortcutController.isAccessibilityShortcutAvailable(
isKeyguardLocked())) {
mHandler.sendMessage(mHandler.obtainMessage(MSG_ACCESSIBILITY_SHORTCUT));
}
- return true;
+ break;
case KeyGestureEvent.KEY_GESTURE_TYPE_CLOSE_ALL_DIALOGS:
if (complete) {
mContext.closeSystemDialogs();
}
- return true;
+ break;
case KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_TALKBACK:
- if (enableTalkbackAndMagnifierKeyGestures()) {
- if (complete) {
- mTalkbackShortcutController.toggleTalkback(mCurrentUserId,
- TalkbackShortcutController.ShortcutSource.KEYBOARD);
- }
- return true;
+ if (complete) {
+ mTalkbackShortcutController.toggleTalkback(mCurrentUserId,
+ TalkbackShortcutController.ShortcutSource.KEYBOARD);
}
break;
case KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_VOICE_ACCESS:
- if (enableVoiceAccessKeyGestures()) {
- if (complete) {
- mVoiceAccessShortcutController.toggleVoiceAccess(mCurrentUserId);
- }
- return true;
+ if (complete) {
+ mVoiceAccessShortcutController.toggleVoiceAccess(mCurrentUserId);
}
break;
case KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION:
@@ -4463,7 +4535,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
&& mModifierShortcutManager.launchApplication(data)) {
dismissKeyboardShortcutsMenu();
}
- return true;
+ break;
case KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_DO_NOT_DISTURB:
NotificationManager nm = getNotificationService();
if (nm != null) {
@@ -4472,9 +4544,12 @@ public class PhoneWindowManager implements WindowManagerPolicy {
: Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS, null,
"Key gesture DND", true);
}
- return true;
+ break;
+ default:
+ Log.w(TAG, "Received a key gesture " + event
+ + " that was not registered by this handler");
+ break;
}
- return false;
}
private void changeDisplayBrightnessValue(int displayId, int direction) {
@@ -4558,6 +4633,9 @@ public class PhoneWindowManager implements WindowManagerPolicy {
@SuppressLint("MissingPermission")
private void injectBackGesture(long downtime) {
+ if (mActivityTaskManagerInternal.requestBackGesture()) {
+ return;
+ }
// Create and inject down event
KeyEvent downEvent = new KeyEvent(downtime, downtime, KeyEvent.ACTION_DOWN,
KeyEvent.KEYCODE_BACK, 0 /* repeat */, 0 /* metaState */,
@@ -5966,7 +6044,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
long whenNanos, int policyFlags) {
if ((policyFlags & FLAG_WAKE) != 0) {
if (mWindowWakeUpPolicy.wakeUpFromMotion(displayId, whenNanos / 1000000, source,
- action == MotionEvent.ACTION_DOWN)) {
+ action == MotionEvent.ACTION_DOWN, mDeviceGoingToSleep)) {
// Woke up. Pass motion events to user.
return ACTION_PASS_TO_USER;
}
@@ -5981,7 +6059,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
// wake up in this case.
if (isTheaterModeEnabled() && (policyFlags & FLAG_WAKE) != 0) {
if (mWindowWakeUpPolicy.wakeUpFromMotion(displayId, whenNanos / 1000000, source,
- action == MotionEvent.ACTION_DOWN)) {
+ action == MotionEvent.ACTION_DOWN, mDeviceGoingToSleep)) {
// Woke up. Pass motion events to user.
return ACTION_PASS_TO_USER;
}
diff --git a/services/core/java/com/android/server/policy/WindowWakeUpPolicy.java b/services/core/java/com/android/server/policy/WindowWakeUpPolicy.java
index 04dbd1fea5d6..0b5ec6e4f495 100644
--- a/services/core/java/com/android/server/policy/WindowWakeUpPolicy.java
+++ b/services/core/java/com/android/server/policy/WindowWakeUpPolicy.java
@@ -149,16 +149,22 @@ class WindowWakeUpPolicy {
* @param displayId the id of the display to wake.
* @param eventTime the timestamp of the event in {@link SystemClock#uptimeMillis()}.
* @param isDown {@code true} if the event's action is {@link MotionEvent#ACTION_DOWN}.
+ * @param deviceGoingToSleep {@code true} if the device is in the middle of going to sleep. This
+ * will be {@code false} if the device is currently fully awake or is fully asleep
+ * (i.e. not trying to go to sleep)
* @return {@code true} if the policy allows the requested wake up and the request has been
* executed; {@code false} otherwise.
*/
- boolean wakeUpFromMotion(int displayId, long eventTime, int source, boolean isDown) {
+ boolean wakeUpFromMotion(
+ int displayId, long eventTime, int source, boolean isDown,
+ boolean deviceGoingToSleep) {
if (!canWakeUp(mAllowTheaterModeWakeFromMotion)) {
if (DEBUG) Slog.d(TAG, "Unable to wake up from motion.");
return false;
}
if (mInputWakeUpDelegate != null
- && mInputWakeUpDelegate.wakeUpFromMotion(eventTime, source, isDown)) {
+ && mInputWakeUpDelegate.wakeUpFromMotion(
+ eventTime, source, isDown, deviceGoingToSleep)) {
return true;
}
if (perDisplayWakeByTouch()) {
diff --git a/services/core/java/com/android/server/policy/WindowWakeUpPolicyInternal.java b/services/core/java/com/android/server/policy/WindowWakeUpPolicyInternal.java
index 66a003577e9a..962b5a7010ea 100644
--- a/services/core/java/com/android/server/policy/WindowWakeUpPolicyInternal.java
+++ b/services/core/java/com/android/server/policy/WindowWakeUpPolicyInternal.java
@@ -52,10 +52,14 @@ public interface WindowWakeUpPolicyInternal {
* @param eventTime the timestamp of the event in {@link SystemClock#uptimeMillis()}.
* @param source the {@link android.view.InputDevice} source that caused the event.
* @param isDown {@code true} if the event's action is {@link MotionEvent#ACTION_DOWN}.
+ * @param deviceGoingToSleep {@code true} if the device is in the middle of going to sleep.
+ * This will be {@code false} if the device is currently fully awake or is fully
+ * asleep (i.e. not trying to go to sleep)
* @return {@code true} if the delegate handled the wake up. {@code false} if the delegate
* decided not to handle the wake up. The policy will execute the wake up in this case.
*/
- boolean wakeUpFromMotion(long eventTime, int source, boolean isDown);
+ boolean wakeUpFromMotion(
+ long eventTime, int source, boolean isDown, boolean deviceGoingToSleep);
}
/**
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index 3eac4b54cd2b..bef505867a14 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -130,12 +130,12 @@ import com.android.internal.util.LatencyTracker;
import com.android.internal.util.Preconditions;
import com.android.server.EventLogTags;
import com.android.server.LockGuard;
+import com.android.server.PackageWatchdog;
import com.android.server.ServiceThread;
import com.android.server.SystemService;
import com.android.server.UiThread;
import com.android.server.Watchdog;
import com.android.server.am.BatteryStatsService;
-import com.android.server.crashrecovery.CrashRecoveryHelper;
import com.android.server.display.feature.DeviceConfigParameterProvider;
import com.android.server.lights.LightsManager;
import com.android.server.lights.LogicalLight;
@@ -4113,7 +4113,7 @@ public final class PowerManagerService extends SystemService
}
}
if (mHandler == null || !mSystemReady) {
- if (CrashRecoveryHelper.isRecoveryTriggeredReboot()) {
+ if (PackageWatchdog.isRecoveryTriggeredReboot()) {
// If we're stuck in a really low-level reboot loop, and a
// rescue party is trying to prompt the user for a factory data
// reset, we must GET TO DA CHOPPA!
diff --git a/services/core/java/com/android/server/power/ShutdownThread.java b/services/core/java/com/android/server/power/ShutdownThread.java
index d209ea90f3ca..4ae1b4e119fa 100644
--- a/services/core/java/com/android/server/power/ShutdownThread.java
+++ b/services/core/java/com/android/server/power/ShutdownThread.java
@@ -59,7 +59,7 @@ import android.view.SurfaceControl;
import android.view.WindowManager;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.server.crashrecovery.CrashRecoveryHelper;
+import com.android.server.PackageWatchdog;
import com.android.server.LocalServices;
import com.android.server.statusbar.StatusBarManagerInternal;
@@ -339,7 +339,7 @@ public final class ShutdownThread extends Thread {
com.android.internal.R.string.reboot_to_update_reboot));
}
} else if (mReason != null && mReason.equals(PowerManager.REBOOT_RECOVERY)) {
- if (CrashRecoveryHelper.isRecoveryTriggeredReboot()) {
+ if (PackageWatchdog.isRecoveryTriggeredReboot()) {
// We're not actually doing a factory reset yet; we're rebooting
// to ask the user if they'd like to reset, so give them a less
// scary dialog message.
diff --git a/services/core/java/com/android/server/power/ThermalManagerService.java b/services/core/java/com/android/server/power/ThermalManagerService.java
index 5ee9b7d09fdd..46c497d04f9e 100644
--- a/services/core/java/com/android/server/power/ThermalManagerService.java
+++ b/services/core/java/com/android/server/power/ThermalManagerService.java
@@ -176,7 +176,9 @@ public class ThermalManagerService extends SystemService {
try {
final HeadroomCallbackData data;
synchronized (mTemperatureWatcher.mSamples) {
- Slog.d(TAG, "Updating skin threshold: " + threshold);
+ if (DEBUG) {
+ Slog.d(TAG, "Updating skin threshold: " + threshold);
+ }
mTemperatureWatcher.updateTemperatureThresholdLocked(threshold, true);
data = mTemperatureWatcher.getHeadroomCallbackDataLocked();
}
@@ -454,7 +456,9 @@ public class ThermalManagerService extends SystemService {
&& temperature.getType() == Temperature.TYPE_SKIN) {
final HeadroomCallbackData data;
synchronized (mTemperatureWatcher.mSamples) {
- Slog.d(TAG, "Updating new temperature: " + temperature);
+ if (DEBUG) {
+ Slog.d(TAG, "Updating new temperature: " + temperature);
+ }
mTemperatureWatcher.updateTemperatureSampleLocked(System.currentTimeMillis(),
temperature);
mTemperatureWatcher.mCachedHeadrooms.clear();
@@ -1878,6 +1882,7 @@ public class ThermalManagerService extends SystemService {
@VisibleForTesting
long mInactivityThresholdMillis = INACTIVITY_THRESHOLD_MILLIS;
+ @GuardedBy("mSamples")
private final Handler mHandler = BackgroundThread.getHandler();
/**
@@ -1900,6 +1905,9 @@ public class ThermalManagerService extends SystemService {
@GuardedBy("mSamples")
private long mLastForecastCallTimeMillis = 0;
+ private final Runnable mGetAndUpdateTemperatureSamplesRunnable =
+ this::getAndUpdateTemperatureSamples;
+
void getAndUpdateThresholds() {
List<TemperatureThreshold> thresholds =
mHalWrapper.getTemperatureThresholds(true, Temperature.TYPE_SKIN);
@@ -1930,7 +1938,9 @@ public class ThermalManagerService extends SystemService {
return;
}
if (override) {
- Slog.d(TAG, "Headroom cache cleared on threshold update " + threshold);
+ if (DEBUG) {
+ Slog.d(TAG, "Headroom cache cleared on threshold update " + threshold);
+ }
mCachedHeadrooms.clear();
Arrays.fill(mHeadroomThresholds, Float.NaN);
}
@@ -1962,7 +1972,7 @@ public class ThermalManagerService extends SystemService {
< mInactivityThresholdMillis) {
// Trigger this again after a second as long as forecast has been called more
// recently than the inactivity timeout
- mHandler.postDelayed(this::getAndUpdateTemperatureSamples, 1000);
+ mHandler.postDelayed(mGetAndUpdateTemperatureSamplesRunnable, 1000);
} else {
// Otherwise, we've been idle for at least 10 seconds, so we should
// shut down
@@ -1974,6 +1984,9 @@ public class ThermalManagerService extends SystemService {
long now = SystemClock.elapsedRealtime();
final List<Temperature> temperatures = mHalWrapper.getCurrentTemperatures(true,
Temperature.TYPE_SKIN);
+ if (DEBUG) {
+ Slog.d(TAG, "Thermal HAL getCurrentTemperatures result: " + temperatures);
+ }
for (Temperature temperature : temperatures) {
updateTemperatureSampleLocked(now, temperature);
}
@@ -2080,10 +2093,16 @@ public class ThermalManagerService extends SystemService {
}
synchronized (mSamples) {
mLastForecastCallTimeMillis = SystemClock.elapsedRealtime();
- if (mSamples.isEmpty()) {
+ if (!mHandler.hasCallbacks(mGetAndUpdateTemperatureSamplesRunnable)) {
+ if (DEBUG) {
+ Slog.d(TAG, "No temperature update callback, scheduling one");
+ }
getAndUpdateTemperatureSamples();
+ } else {
+ if (DEBUG) {
+ Slog.d(TAG, "Temperature update callback already exists");
+ }
}
-
// If somehow things take much longer than expected or there are no temperatures
// to sample, return early
if (mSamples.isEmpty()) {
@@ -2103,8 +2122,11 @@ public class ThermalManagerService extends SystemService {
Binder.getCallingUid(),
FrameworkStatsLog.THERMAL_HEADROOM_CALLED__API_STATUS__SUCCESS,
headroom, forecastSeconds);
- Slog.d(TAG, "Headroom forecast in " + forecastSeconds + "s served from cache: "
- + headroom);
+ if (DEBUG) {
+ Slog.d(TAG,
+ "Headroom forecast in " + forecastSeconds + "s served from cache: "
+ + headroom);
+ }
return headroom;
}
@@ -2133,7 +2155,10 @@ public class ThermalManagerService extends SystemService {
Binder.getCallingUid(),
FrameworkStatsLog.THERMAL_HEADROOM_CALLED__API_STATUS__SUCCESS,
headroom, 0);
- Slog.d(TAG, "Headroom forecast in 0s served from cache: " + headroom);
+ if (DEBUG) {
+ Slog.d(TAG,
+ "Headroom forecast in 0s served from cache: " + headroom);
+ }
return headroom;
}
// Don't try to forecast, just use the latest one we have
@@ -2182,7 +2207,9 @@ public class ThermalManagerService extends SystemService {
getForecast(DEFAULT_FORECAST_SECONDS),
DEFAULT_FORECAST_SECONDS,
Arrays.copyOf(mHeadroomThresholds, mHeadroomThresholds.length));
- Slog.d(TAG, "New headroom callback data: " + data);
+ if (DEBUG) {
+ Slog.d(TAG, "New headroom callback data: " + data);
+ }
return data;
}
diff --git a/services/core/java/com/android/server/power/stats/BatteryExternalStatsWorker.java b/services/core/java/com/android/server/power/stats/BatteryExternalStatsWorker.java
index f90da644c0ce..a9896e96a08f 100644
--- a/services/core/java/com/android/server/power/stats/BatteryExternalStatsWorker.java
+++ b/services/core/java/com/android/server/power/stats/BatteryExternalStatsWorker.java
@@ -272,15 +272,6 @@ public class BatteryExternalStatsWorker implements BatteryStatsImpl.ExternalStat
mHandler.removeMessages(SYNC_WAKELOCK_CHANGE);
}
- @Override
- public void scheduleSyncDueToBatteryLevelChange(long delayMillis) {
- synchronized (BatteryExternalStatsWorker.this) {
- scheduleDelayedSyncLocked(SYNC_BATTERY_LEVEL_CHANGE,
- () -> scheduleSync("battery-level", UPDATE_ALL),
- delayMillis);
- }
- }
-
@GuardedBy("this")
private void cancelSyncDueToBatteryLevelChangeLocked() {
mHandler.removeMessages(SYNC_BATTERY_LEVEL_CHANGE);
diff --git a/services/core/java/com/android/server/power/stats/BatteryHistoryStepDetailsProvider.java b/services/core/java/com/android/server/power/stats/BatteryHistoryStepDetailsProvider.java
new file mode 100644
index 000000000000..cd7612523b3e
--- /dev/null
+++ b/services/core/java/com/android/server/power/stats/BatteryHistoryStepDetailsProvider.java
@@ -0,0 +1,276 @@
+/*
+ * Copyright (C) 2025 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.server.power.stats;
+
+import android.hardware.power.stats.PowerEntity;
+import android.hardware.power.stats.State;
+import android.hardware.power.stats.StateResidency;
+import android.hardware.power.stats.StateResidencyResult;
+import android.os.BatteryStats;
+import android.power.PowerStatsInternal;
+import android.util.Slog;
+import android.util.SparseArray;
+
+import com.android.server.LocalServices;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+
+class BatteryHistoryStepDetailsProvider {
+ public static final String TAG = "BatteryHistoryStepDetails";
+ private static final boolean DEBUG = false;
+
+ private static final int POWER_STATS_QUERY_TIMEOUT_MILLIS = 2000;
+ private static final int MAX_LOW_POWER_STATS_SIZE = 32768;
+
+ private final BatteryStatsImpl mBatteryStats;
+
+ private final BatteryStats.HistoryStepDetails mDetails = new BatteryStats.HistoryStepDetails();
+
+ private boolean mHasHistoryStepDetails;
+
+ /**
+ * Total time (in milliseconds) spent executing in user code.
+ */
+ private long mLastStepCpuUserTimeMs;
+ private long mCurStepCpuUserTimeMs;
+ /**
+ * Total time (in milliseconds) spent executing in kernel code.
+ */
+ private long mLastStepCpuSystemTimeMs;
+ private long mCurStepCpuSystemTimeMs;
+ /**
+ * Times from /proc/stat (but measured in milliseconds).
+ */
+ private long mLastStepStatUserTimeMs;
+ private long mLastStepStatSystemTimeMs;
+ private long mLastStepStatIOWaitTimeMs;
+ private long mLastStepStatIrqTimeMs;
+ private long mLastStepStatSoftIrqTimeMs;
+ private long mLastStepStatIdleTimeMs;
+ private long mCurStepStatUserTimeMs;
+ private long mCurStepStatSystemTimeMs;
+ private long mCurStepStatIOWaitTimeMs;
+ private long mCurStepStatIrqTimeMs;
+ private long mCurStepStatSoftIrqTimeMs;
+ private long mCurStepStatIdleTimeMs;
+
+ private PowerStatsInternal mPowerStatsInternal;
+ private final Map<Integer, String> mEntityNames = new HashMap<>();
+ private final Map<Integer, Map<Integer, String>> mStateNames = new HashMap<>();
+
+ BatteryHistoryStepDetailsProvider(BatteryStatsImpl batteryStats) {
+ mBatteryStats = batteryStats;
+ }
+
+ void onSystemReady() {
+ mPowerStatsInternal = LocalServices.getService(PowerStatsInternal.class);
+ if (mPowerStatsInternal != null) {
+ populatePowerEntityMaps();
+ }
+ }
+
+ void requestUpdate() {
+ mBatteryStats.mHandler.post(this::update);
+ }
+
+ void update() {
+ mHasHistoryStepDetails = false;
+ mBatteryStats.updateCpuDetails();
+ calculateHistoryStepDetails();
+ updateStateResidency();
+ mBatteryStats.getHistory().recordHistoryStepDetails(mDetails,
+ mBatteryStats.mClock.elapsedRealtime(),
+ mBatteryStats.mClock.uptimeMillis());
+ }
+
+ private void calculateHistoryStepDetails() {
+ if (!mHasHistoryStepDetails) {
+ return;
+ }
+
+ if (DEBUG) {
+ Slog.d(TAG, "Step stats last: user=" + mLastStepCpuUserTimeMs + " sys="
+ + mLastStepStatSystemTimeMs + " io=" + mLastStepStatIOWaitTimeMs
+ + " irq=" + mLastStepStatIrqTimeMs + " sirq="
+ + mLastStepStatSoftIrqTimeMs + " idle=" + mLastStepStatIdleTimeMs);
+ Slog.d(TAG, "Step stats cur: user=" + mCurStepCpuUserTimeMs + " sys="
+ + mCurStepStatSystemTimeMs + " io=" + mCurStepStatIOWaitTimeMs
+ + " irq=" + mCurStepStatIrqTimeMs + " sirq="
+ + mCurStepStatSoftIrqTimeMs + " idle=" + mCurStepStatIdleTimeMs);
+ }
+ mDetails.userTime = (int) (mCurStepCpuUserTimeMs - mLastStepCpuUserTimeMs);
+ mDetails.systemTime = (int) (mCurStepCpuSystemTimeMs - mLastStepCpuSystemTimeMs);
+ mDetails.statUserTime = (int) (mCurStepStatUserTimeMs - mLastStepStatUserTimeMs);
+ mDetails.statSystemTime =
+ (int) (mCurStepStatSystemTimeMs - mLastStepStatSystemTimeMs);
+ mDetails.statIOWaitTime =
+ (int) (mCurStepStatIOWaitTimeMs - mLastStepStatIOWaitTimeMs);
+ mDetails.statIrqTime = (int) (mCurStepStatIrqTimeMs - mLastStepStatIrqTimeMs);
+ mDetails.statSoftIrqTime =
+ (int) (mCurStepStatSoftIrqTimeMs - mLastStepStatSoftIrqTimeMs);
+ mDetails.statIdlTime = (int) (mCurStepStatIdleTimeMs - mLastStepStatIdleTimeMs);
+ mDetails.appCpuUid1 = mDetails.appCpuUid2 = mDetails.appCpuUid3 = -1;
+ mDetails.appCpuUTime1 = mDetails.appCpuUTime2 = mDetails.appCpuUTime3 = 0;
+ mDetails.appCpuSTime1 = mDetails.appCpuSTime2 = mDetails.appCpuSTime3 = 0;
+ SparseArray<? extends BatteryStats.Uid> uidStats = mBatteryStats.getUidStats();
+ final int uidCount = uidStats.size();
+ for (int i = 0; i < uidCount; i++) {
+ final BatteryStatsImpl.Uid uid = (BatteryStatsImpl.Uid) uidStats.valueAt(i);
+ final int totalUTimeMs =
+ (int) (uid.mCurStepUserTimeMs - uid.mLastStepUserTimeMs);
+ final int totalSTimeMs =
+ (int) (uid.mCurStepSystemTimeMs - uid.mLastStepSystemTimeMs);
+ final int totalTimeMs = totalUTimeMs + totalSTimeMs;
+ uid.mLastStepUserTimeMs = uid.mCurStepUserTimeMs;
+ uid.mLastStepSystemTimeMs = uid.mCurStepSystemTimeMs;
+ if (totalTimeMs <= (mDetails.appCpuUTime3 + mDetails.appCpuSTime3)) {
+ continue;
+ }
+ if (totalTimeMs <= (mDetails.appCpuUTime2 + mDetails.appCpuSTime2)) {
+ mDetails.appCpuUid3 = uid.mUid;
+ mDetails.appCpuUTime3 = totalUTimeMs;
+ mDetails.appCpuSTime3 = totalSTimeMs;
+ } else {
+ mDetails.appCpuUid3 = mDetails.appCpuUid2;
+ mDetails.appCpuUTime3 = mDetails.appCpuUTime2;
+ mDetails.appCpuSTime3 = mDetails.appCpuSTime2;
+ if (totalTimeMs <= (mDetails.appCpuUTime1 + mDetails.appCpuSTime1)) {
+ mDetails.appCpuUid2 = uid.mUid;
+ mDetails.appCpuUTime2 = totalUTimeMs;
+ mDetails.appCpuSTime2 = totalSTimeMs;
+ } else {
+ mDetails.appCpuUid2 = mDetails.appCpuUid1;
+ mDetails.appCpuUTime2 = mDetails.appCpuUTime1;
+ mDetails.appCpuSTime2 = mDetails.appCpuSTime1;
+ mDetails.appCpuUid1 = uid.mUid;
+ mDetails.appCpuUTime1 = totalUTimeMs;
+ mDetails.appCpuSTime1 = totalSTimeMs;
+ }
+ }
+ }
+ mLastStepCpuUserTimeMs = mCurStepCpuUserTimeMs;
+ mLastStepCpuSystemTimeMs = mCurStepCpuSystemTimeMs;
+ mLastStepStatUserTimeMs = mCurStepStatUserTimeMs;
+ mLastStepStatSystemTimeMs = mCurStepStatSystemTimeMs;
+ mLastStepStatIOWaitTimeMs = mCurStepStatIOWaitTimeMs;
+ mLastStepStatIrqTimeMs = mCurStepStatIrqTimeMs;
+ mLastStepStatSoftIrqTimeMs = mCurStepStatSoftIrqTimeMs;
+ mLastStepStatIdleTimeMs = mCurStepStatIdleTimeMs;
+ }
+
+ public void addCpuStats(int totalUTimeMs, int totalSTimeMs, int statUserTimeMs,
+ int statSystemTimeMs, int statIOWaitTimeMs, int statIrqTimeMs,
+ int statSoftIrqTimeMs, int statIdleTimeMs) {
+ if (DEBUG) {
+ Slog.d(TAG, "Adding cpu: tuser=" + totalUTimeMs + " tsys=" + totalSTimeMs
+ + " user=" + statUserTimeMs + " sys=" + statSystemTimeMs
+ + " io=" + statIOWaitTimeMs + " irq=" + statIrqTimeMs
+ + " sirq=" + statSoftIrqTimeMs + " idle=" + statIdleTimeMs);
+ }
+ mCurStepCpuUserTimeMs += totalUTimeMs;
+ mCurStepCpuSystemTimeMs += totalSTimeMs;
+ mCurStepStatUserTimeMs += statUserTimeMs;
+ mCurStepStatSystemTimeMs += statSystemTimeMs;
+ mCurStepStatIOWaitTimeMs += statIOWaitTimeMs;
+ mCurStepStatIrqTimeMs += statIrqTimeMs;
+ mCurStepStatSoftIrqTimeMs += statSoftIrqTimeMs;
+ mCurStepStatIdleTimeMs += statIdleTimeMs;
+ }
+
+ public void finishAddingCpuLocked() {
+ mHasHistoryStepDetails = true;
+ }
+
+ public void reset() {
+ mHasHistoryStepDetails = false;
+ mLastStepCpuUserTimeMs = mCurStepCpuUserTimeMs = 0;
+ mLastStepCpuSystemTimeMs = mCurStepCpuSystemTimeMs = 0;
+ mLastStepStatUserTimeMs = mCurStepStatUserTimeMs = 0;
+ mLastStepStatSystemTimeMs = mCurStepStatSystemTimeMs = 0;
+ mLastStepStatIOWaitTimeMs = mCurStepStatIOWaitTimeMs = 0;
+ mLastStepStatIrqTimeMs = mCurStepStatIrqTimeMs = 0;
+ mLastStepStatSoftIrqTimeMs = mCurStepStatSoftIrqTimeMs = 0;
+ mLastStepStatIdleTimeMs = mCurStepStatIdleTimeMs = 0;
+ }
+
+ private void updateStateResidency() {
+ mDetails.statSubsystemPowerState = null;
+
+ if (mPowerStatsInternal == null || mEntityNames.isEmpty() || mStateNames.isEmpty()) {
+ return;
+ }
+
+ final StateResidencyResult[] results;
+ try {
+ results = mPowerStatsInternal.getStateResidencyAsync(new int[0])
+ .get(POWER_STATS_QUERY_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
+ } catch (Exception e) {
+ Slog.e(TAG, "Failed to getStateResidencyAsync", e);
+ return;
+ }
+
+ if (results == null || results.length == 0) {
+ return;
+ }
+
+ StringBuilder builder = new StringBuilder("SubsystemPowerState");
+ for (int i = 0; i < results.length; i++) {
+ final StateResidencyResult result = results[i];
+ int length = builder.length();
+ builder.append(" subsystem_").append(i);
+ builder.append(" name=").append(mEntityNames.get(result.id));
+
+ for (int j = 0; j < result.stateResidencyData.length; j++) {
+ final StateResidency stateResidency = result.stateResidencyData[j];
+ builder.append(" state_").append(j);
+ builder.append(" name=").append(mStateNames.get(result.id).get(
+ stateResidency.id));
+ builder.append(" time=").append(stateResidency.totalTimeInStateMs);
+ builder.append(" count=").append(stateResidency.totalStateEntryCount);
+ builder.append(" last entry=").append(stateResidency.lastEntryTimestampMs);
+ }
+
+ if (builder.length() > MAX_LOW_POWER_STATS_SIZE) {
+ Slog.e(TAG, "updateStateResidency: buffer not enough");
+ builder.setLength(length);
+ break;
+ }
+ }
+
+ mDetails.statSubsystemPowerState = builder.toString();
+ }
+
+ private void populatePowerEntityMaps() {
+ PowerEntity[] entities = mPowerStatsInternal.getPowerEntityInfo();
+ if (entities == null) {
+ return;
+ }
+
+ for (final PowerEntity entity : entities) {
+ Map<Integer, String> states = new HashMap<>();
+ for (int j = 0; j < entity.states.length; j++) {
+ final State state = entity.states[j];
+ states.put(state.id, state.name);
+ }
+
+ mEntityNames.put(entity.id, entity.name);
+ mStateNames.put(entity.id, states);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java b/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
index 2cf6b7efcb48..0af50805d756 100644
--- a/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
+++ b/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
@@ -117,7 +117,6 @@ import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.os.BackgroundThread;
import com.android.internal.os.BatteryStatsHistory;
-import com.android.internal.os.BatteryStatsHistory.HistoryStepDetailsCalculator;
import com.android.internal.os.BatteryStatsHistoryIterator;
import com.android.internal.os.BinderCallsStats;
import com.android.internal.os.BinderTransactionNameResolver;
@@ -653,7 +652,6 @@ public class BatteryStatsImpl extends BatteryStats {
public interface PlatformIdleStateCallback {
public void fillLowPowerStats(RpmStats rpmStats);
- public String getSubsystemLowPowerStats();
}
/** interface to update rail information for power monitor */
@@ -1065,10 +1063,6 @@ public class BatteryStatsImpl extends BatteryStats {
*/
void cancelCpuSyncDueToWakelockChange();
- /**
- * Schedules a sync caused by the battery level change
- */
- void scheduleSyncDueToBatteryLevelChange(long delayMillis);
/** Schedule removal of UIDs corresponding to a removed user */
void scheduleCleanupDueToRemovedUser(int userId);
/** Schedule a sync because of a process state change */
@@ -1131,8 +1125,8 @@ public class BatteryStatsImpl extends BatteryStats {
private boolean mShuttingDown;
private final HistoryEventTracker mActiveEvents = new HistoryEventTracker();
- private final HistoryStepDetailsCalculatorImpl mStepDetailsCalculator =
- new HistoryStepDetailsCalculatorImpl();
+ private final BatteryHistoryStepDetailsProvider mStepDetailsProvider =
+ new BatteryHistoryStepDetailsProvider(this);
private boolean mHaveBatteryLevel = false;
private boolean mBatteryPluggedIn;
@@ -4553,184 +4547,6 @@ public class BatteryStatsImpl extends BatteryStats {
return kmt;
}
- private class HistoryStepDetailsCalculatorImpl implements HistoryStepDetailsCalculator {
- private final HistoryStepDetails mDetails = new HistoryStepDetails();
-
- private boolean mHasHistoryStepDetails;
- private boolean mUpdateRequested;
-
- /**
- * Total time (in milliseconds) spent executing in user code.
- */
- private long mLastStepCpuUserTimeMs;
- private long mCurStepCpuUserTimeMs;
- /**
- * Total time (in milliseconds) spent executing in kernel code.
- */
- private long mLastStepCpuSystemTimeMs;
- private long mCurStepCpuSystemTimeMs;
- /**
- * Times from /proc/stat (but measured in milliseconds).
- */
- private long mLastStepStatUserTimeMs;
- private long mLastStepStatSystemTimeMs;
- private long mLastStepStatIOWaitTimeMs;
- private long mLastStepStatIrqTimeMs;
- private long mLastStepStatSoftIrqTimeMs;
- private long mLastStepStatIdleTimeMs;
- private long mCurStepStatUserTimeMs;
- private long mCurStepStatSystemTimeMs;
- private long mCurStepStatIOWaitTimeMs;
- private long mCurStepStatIrqTimeMs;
- private long mCurStepStatSoftIrqTimeMs;
- private long mCurStepStatIdleTimeMs;
-
- @Override
- public HistoryStepDetails getHistoryStepDetails() {
- if (!mUpdateRequested) {
- mUpdateRequested = true;
- // Perform a CPU update right after we do this collection, so we have started
- // collecting good data for the next step.
- requestImmediateCpuUpdate();
-
- if (mPlatformIdleStateCallback != null) {
- mDetails.statSubsystemPowerState =
- mPlatformIdleStateCallback.getSubsystemLowPowerStats();
- if (DEBUG) {
- Slog.i(TAG,
- "WRITE SubsystemPowerState:" + mDetails.statSubsystemPowerState);
- }
- }
- }
-
- if (!mHasHistoryStepDetails) {
- // We are not generating a delta, so all we need to do is reset the stats
- // we will later be doing a delta from.
- final int uidCount = mUidStats.size();
- for (int i = 0; i < uidCount; i++) {
- final BatteryStatsImpl.Uid uid = mUidStats.valueAt(i);
- uid.mLastStepUserTimeMs = uid.mCurStepUserTimeMs;
- uid.mLastStepSystemTimeMs = uid.mCurStepSystemTimeMs;
- }
- mLastStepCpuUserTimeMs = mCurStepCpuUserTimeMs;
- mLastStepCpuSystemTimeMs = mCurStepCpuSystemTimeMs;
- mLastStepStatUserTimeMs = mCurStepStatUserTimeMs;
- mLastStepStatSystemTimeMs = mCurStepStatSystemTimeMs;
- mLastStepStatIOWaitTimeMs = mCurStepStatIOWaitTimeMs;
- mLastStepStatIrqTimeMs = mCurStepStatIrqTimeMs;
- mLastStepStatSoftIrqTimeMs = mCurStepStatSoftIrqTimeMs;
- mLastStepStatIdleTimeMs = mCurStepStatIdleTimeMs;
- return null;
- } else {
- if (DEBUG) {
- Slog.d(TAG, "Step stats last: user=" + mLastStepCpuUserTimeMs + " sys="
- + mLastStepStatSystemTimeMs + " io=" + mLastStepStatIOWaitTimeMs
- + " irq=" + mLastStepStatIrqTimeMs + " sirq="
- + mLastStepStatSoftIrqTimeMs + " idle=" + mLastStepStatIdleTimeMs);
- Slog.d(TAG, "Step stats cur: user=" + mCurStepCpuUserTimeMs + " sys="
- + mCurStepStatSystemTimeMs + " io=" + mCurStepStatIOWaitTimeMs
- + " irq=" + mCurStepStatIrqTimeMs + " sirq="
- + mCurStepStatSoftIrqTimeMs + " idle=" + mCurStepStatIdleTimeMs);
- }
- mDetails.userTime = (int) (mCurStepCpuUserTimeMs - mLastStepCpuUserTimeMs);
- mDetails.systemTime = (int) (mCurStepCpuSystemTimeMs - mLastStepCpuSystemTimeMs);
- mDetails.statUserTime = (int) (mCurStepStatUserTimeMs - mLastStepStatUserTimeMs);
- mDetails.statSystemTime =
- (int) (mCurStepStatSystemTimeMs - mLastStepStatSystemTimeMs);
- mDetails.statIOWaitTime =
- (int) (mCurStepStatIOWaitTimeMs - mLastStepStatIOWaitTimeMs);
- mDetails.statIrqTime = (int) (mCurStepStatIrqTimeMs - mLastStepStatIrqTimeMs);
- mDetails.statSoftIrqTime =
- (int) (mCurStepStatSoftIrqTimeMs - mLastStepStatSoftIrqTimeMs);
- mDetails.statIdlTime = (int) (mCurStepStatIdleTimeMs - mLastStepStatIdleTimeMs);
- mDetails.appCpuUid1 = mDetails.appCpuUid2 = mDetails.appCpuUid3 = -1;
- mDetails.appCpuUTime1 = mDetails.appCpuUTime2 = mDetails.appCpuUTime3 = 0;
- mDetails.appCpuSTime1 = mDetails.appCpuSTime2 = mDetails.appCpuSTime3 = 0;
- final int uidCount = mUidStats.size();
- for (int i = 0; i < uidCount; i++) {
- final BatteryStatsImpl.Uid uid = mUidStats.valueAt(i);
- final int totalUTimeMs =
- (int) (uid.mCurStepUserTimeMs - uid.mLastStepUserTimeMs);
- final int totalSTimeMs =
- (int) (uid.mCurStepSystemTimeMs - uid.mLastStepSystemTimeMs);
- final int totalTimeMs = totalUTimeMs + totalSTimeMs;
- uid.mLastStepUserTimeMs = uid.mCurStepUserTimeMs;
- uid.mLastStepSystemTimeMs = uid.mCurStepSystemTimeMs;
- if (totalTimeMs <= (mDetails.appCpuUTime3 + mDetails.appCpuSTime3)) {
- continue;
- }
- if (totalTimeMs <= (mDetails.appCpuUTime2 + mDetails.appCpuSTime2)) {
- mDetails.appCpuUid3 = uid.mUid;
- mDetails.appCpuUTime3 = totalUTimeMs;
- mDetails.appCpuSTime3 = totalSTimeMs;
- } else {
- mDetails.appCpuUid3 = mDetails.appCpuUid2;
- mDetails.appCpuUTime3 = mDetails.appCpuUTime2;
- mDetails.appCpuSTime3 = mDetails.appCpuSTime2;
- if (totalTimeMs <= (mDetails.appCpuUTime1 + mDetails.appCpuSTime1)) {
- mDetails.appCpuUid2 = uid.mUid;
- mDetails.appCpuUTime2 = totalUTimeMs;
- mDetails.appCpuSTime2 = totalSTimeMs;
- } else {
- mDetails.appCpuUid2 = mDetails.appCpuUid1;
- mDetails.appCpuUTime2 = mDetails.appCpuUTime1;
- mDetails.appCpuSTime2 = mDetails.appCpuSTime1;
- mDetails.appCpuUid1 = uid.mUid;
- mDetails.appCpuUTime1 = totalUTimeMs;
- mDetails.appCpuSTime1 = totalSTimeMs;
- }
- }
- }
- mLastStepCpuUserTimeMs = mCurStepCpuUserTimeMs;
- mLastStepCpuSystemTimeMs = mCurStepCpuSystemTimeMs;
- mLastStepStatUserTimeMs = mCurStepStatUserTimeMs;
- mLastStepStatSystemTimeMs = mCurStepStatSystemTimeMs;
- mLastStepStatIOWaitTimeMs = mCurStepStatIOWaitTimeMs;
- mLastStepStatIrqTimeMs = mCurStepStatIrqTimeMs;
- mLastStepStatSoftIrqTimeMs = mCurStepStatSoftIrqTimeMs;
- mLastStepStatIdleTimeMs = mCurStepStatIdleTimeMs;
- return mDetails;
- }
- }
-
- public void addCpuStats(int totalUTimeMs, int totalSTimeMs, int statUserTimeMs,
- int statSystemTimeMs, int statIOWaitTimeMs, int statIrqTimeMs,
- int statSoftIrqTimeMs, int statIdleTimeMs) {
- if (DEBUG) {
- Slog.d(TAG, "Adding cpu: tuser=" + totalUTimeMs + " tsys=" + totalSTimeMs
- + " user=" + statUserTimeMs + " sys=" + statSystemTimeMs
- + " io=" + statIOWaitTimeMs + " irq=" + statIrqTimeMs
- + " sirq=" + statSoftIrqTimeMs + " idle=" + statIdleTimeMs);
- }
- mCurStepCpuUserTimeMs += totalUTimeMs;
- mCurStepCpuSystemTimeMs += totalSTimeMs;
- mCurStepStatUserTimeMs += statUserTimeMs;
- mCurStepStatSystemTimeMs += statSystemTimeMs;
- mCurStepStatIOWaitTimeMs += statIOWaitTimeMs;
- mCurStepStatIrqTimeMs += statIrqTimeMs;
- mCurStepStatSoftIrqTimeMs += statSoftIrqTimeMs;
- mCurStepStatIdleTimeMs += statIdleTimeMs;
- }
-
- public void finishAddingCpuLocked() {
- mHasHistoryStepDetails = true;
- mUpdateRequested = false;
- }
-
- @Override
- public void clear() {
- mHasHistoryStepDetails = false;
- mLastStepCpuUserTimeMs = mCurStepCpuUserTimeMs = 0;
- mLastStepCpuSystemTimeMs = mCurStepCpuSystemTimeMs = 0;
- mLastStepStatUserTimeMs = mCurStepStatUserTimeMs = 0;
- mLastStepStatSystemTimeMs = mCurStepStatSystemTimeMs = 0;
- mLastStepStatIOWaitTimeMs = mCurStepStatIOWaitTimeMs = 0;
- mLastStepStatIrqTimeMs = mCurStepStatIrqTimeMs = 0;
- mLastStepStatSoftIrqTimeMs = mCurStepStatSoftIrqTimeMs = 0;
- mLastStepStatIdleTimeMs = mCurStepStatIdleTimeMs = 0;
- }
- }
-
@GuardedBy("this")
@Override
public void commitCurrentHistoryBatchLocked() {
@@ -5557,7 +5373,7 @@ public class BatteryStatsImpl extends BatteryStats {
public void addCpuStatsLocked(int totalUTimeMs, int totalSTimeMs, int statUserTimeMs,
int statSystemTimeMs, int statIOWaitTimeMs, int statIrqTimeMs,
int statSoftIrqTimeMs, int statIdleTimeMs) {
- mStepDetailsCalculator.addCpuStats(totalUTimeMs, totalSTimeMs, statUserTimeMs,
+ mStepDetailsProvider.addCpuStats(totalUTimeMs, totalSTimeMs, statUserTimeMs,
statSystemTimeMs, statIOWaitTimeMs, statIrqTimeMs,
statSoftIrqTimeMs, statIdleTimeMs);
}
@@ -5567,7 +5383,7 @@ public class BatteryStatsImpl extends BatteryStats {
*/
@GuardedBy("this")
public void finishAddingCpuStatsLocked() {
- mStepDetailsCalculator.finishAddingCpuLocked();
+ mStepDetailsProvider.finishAddingCpuLocked();
}
public void noteProcessDiedLocked(int uid, int pid) {
@@ -11515,8 +11331,7 @@ public class BatteryStatsImpl extends BatteryStats {
mBatteryHistoryDirectory = batteryHistoryDirectory;
mHistory = new BatteryStatsHistory(null /* historyBuffer */, mConstants.MAX_HISTORY_BUFFER,
- mBatteryHistoryDirectory, mStepDetailsCalculator, mClock, mMonotonicClock,
- traceDelegate, eventLogger);
+ mBatteryHistoryDirectory, mClock, mMonotonicClock, traceDelegate, eventLogger);
mCpuPowerStatsCollector = new CpuPowerStatsCollector(mPowerStatsCollectorInjector);
mCpuPowerStatsCollector.addConsumer(this::recordPowerStats);
@@ -11723,6 +11538,12 @@ public class BatteryStatsImpl extends BatteryStats {
mCallback = cb;
}
+ void updateCpuDetails() {
+ if (mCallback != null) {
+ mCallback.batteryNeedsCpuUpdate();
+ }
+ }
+
public void setRadioScanningTimeoutLocked(long timeoutUs) {
if (mPhoneSignalScanningTimer != null) {
mPhoneSignalScanningTimer.setTimeout(timeoutUs);
@@ -12343,6 +12164,8 @@ public class BatteryStatsImpl extends BatteryStats {
mWakeupReasonStats.clear();
}
+ mStepDetailsProvider.reset();
+
if (mTmpRailStats != null) {
mTmpRailStats.reset();
}
@@ -14948,6 +14771,8 @@ public class BatteryStatsImpl extends BatteryStats {
mCpuUidFreqTimeReader.onSystemReady();
}
+ mStepDetailsProvider.onSystemReady();
+
mPowerStatsCollectorInjector.setContext(context);
mCpuPowerStatsCollector.setEnabled(
@@ -15270,6 +15095,7 @@ public class BatteryStatsImpl extends BatteryStats {
reportChangesToStatsLog(status, plugType, level);
+ boolean requestStepDetails = false;
final boolean onBattery = isOnBattery(plugType, status);
if (!mHaveBatteryLevel) {
mHaveBatteryLevel = true;
@@ -15289,6 +15115,7 @@ public class BatteryStatsImpl extends BatteryStats {
mMaxChargeStepLevel = mMinDischargeStepLevel =
mLastChargeStepLevel = mLastDischargeStepLevel = level;
+ requestStepDetails = true;
} else if (mBatteryLevel != level || mOnBattery != onBattery) {
recordDailyStatsIfNeededLocked(level >= 100 && onBattery, currentTimeMs);
}
@@ -15332,16 +15159,13 @@ public class BatteryStatsImpl extends BatteryStats {
}
mBatteryChargeUah = chargeUah;
setOnBatteryLocked(elapsedRealtimeMs, uptimeMs, onBattery, oldStatus, level, chargeUah);
+ requestStepDetails = true;
} else {
boolean changed = false;
if (mBatteryLevel != level) {
mBatteryLevel = level;
changed = true;
-
- // TODO(adamlesinski): Schedule the creation of a HistoryStepDetails record
- // which will pull external stats.
- mExternalSync.scheduleSyncDueToBatteryLevelChange(
- mConstants.BATTERY_LEVEL_COLLECTION_DELAY_MS);
+ requestStepDetails = true;
}
if (mBatteryStatus != status) {
mBatteryStatus = status;
@@ -15462,6 +15286,9 @@ public class BatteryStatsImpl extends BatteryStats {
if (mAccumulateBatteryUsageStats) {
mBatteryUsageStatsProvider.accumulateBatteryUsageStatsAsync(this, mHandler);
}
+ if (requestStepDetails) {
+ mStepDetailsProvider.requestUpdate();
+ }
}
public static boolean isOnBattery(int plugType, int status) {
diff --git a/services/core/java/com/android/server/security/advancedprotection/AdvancedProtectionService.java b/services/core/java/com/android/server/security/advancedprotection/AdvancedProtectionService.java
index 6872ca9e46ee..3ece07c84080 100644
--- a/services/core/java/com/android/server/security/advancedprotection/AdvancedProtectionService.java
+++ b/services/core/java/com/android/server/security/advancedprotection/AdvancedProtectionService.java
@@ -17,11 +17,14 @@
package com.android.server.security.advancedprotection;
import static android.provider.Settings.Secure.ADVANCED_PROTECTION_MODE;
+import static android.provider.Settings.Secure.AAPM_USB_DATA_PROTECTION;
+import static com.android.internal.util.ConcurrentUtils.DIRECT_EXECUTOR;
import android.Manifest;
import android.annotation.EnforcePermission;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.app.StatsManager;
import android.content.Context;
import android.content.SharedPreferences;
import android.os.Binder;
@@ -45,6 +48,7 @@ import android.security.advancedprotection.IAdvancedProtectionService;
import android.security.advancedprotection.AdvancedProtectionProtoEnums;
import android.util.ArrayMap;
import android.util.Slog;
+import android.util.StatsEvent;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.DumpUtils;
@@ -59,12 +63,14 @@ import com.android.server.security.advancedprotection.features.DisallowCellular2
import com.android.server.security.advancedprotection.features.DisallowInstallUnknownSourcesAdvancedProtectionHook;
import com.android.server.security.advancedprotection.features.MemoryTaggingExtensionHook;
import com.android.server.security.advancedprotection.features.UsbDataAdvancedProtectionHook;
+import com.android.server.security.advancedprotection.features.DisallowWepAdvancedProtectionProvider;
import java.io.File;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
+import java.util.Set;
/** @hide */
public class AdvancedProtectionService extends IAdvancedProtectionService.Stub {
@@ -125,13 +131,27 @@ public class AdvancedProtectionService extends IAdvancedProtectionService.Stub
Slog.e(TAG, "Failed to initialize DisallowCellular2g", e);
}
}
- if (android.security.Flags.aapmFeatureUsbDataProtection()) {
+ if (android.security.Flags.aapmFeatureUsbDataProtection()
+ // Usb data protection is enabled by default
+ && mStore.retrieveInt(AAPM_USB_DATA_PROTECTION, AdvancedProtectionStore.ON)
+ == AdvancedProtectionStore.ON) {
try {
mHooks.add(new UsbDataAdvancedProtectionHook(mContext, enabled));
} catch (Exception e) {
Slog.e(TAG, "Failed to initialize UsbDataAdvancedProtection", e);
}
- }
+ }
+
+ mProviders.add(new DisallowWepAdvancedProtectionProvider());
+ }
+
+ private void initLogging() {
+ StatsManager statsManager = mContext.getSystemService(StatsManager.class);
+ statsManager.setPullAtomCallback(
+ FrameworkStatsLog.ADVANCED_PROTECTION_STATE_INFO,
+ null, // use default PullAtomMetadata values
+ DIRECT_EXECUTOR,
+ new AdvancedProtectionStatePullAtomCallback());
}
// Only for tests
@@ -168,7 +188,7 @@ public class AdvancedProtectionService extends IAdvancedProtectionService.Stub
// Without permission check
private boolean isAdvancedProtectionEnabledInternal() {
- return mStore.retrieve();
+ return mStore.retrieveAdvancedProtectionModeEnabled();
}
@Override
@@ -202,7 +222,7 @@ public class AdvancedProtectionService extends IAdvancedProtectionService.Stub
try {
synchronized (mCallbacks) {
if (enabled != isAdvancedProtectionEnabledInternal()) {
- mStore.store(enabled);
+ mStore.storeAdvancedProtectionModeEnabled(enabled);
sendModeChanged(enabled);
logAdvancedProtectionEnabled(enabled);
}
@@ -212,6 +232,34 @@ public class AdvancedProtectionService extends IAdvancedProtectionService.Stub
}
}
+ public void setUsbDataProtectionEnabled(boolean enabled) {
+ int value = enabled ? AdvancedProtectionStore.ON
+ : AdvancedProtectionStore.OFF;
+ setAdvancedProtectionSubSettingInt(AAPM_USB_DATA_PROTECTION, value);
+ }
+
+ private void setAdvancedProtectionSubSettingInt(String key, int value) {
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ synchronized (mCallbacks) {
+ mStore.storeInt(key, value);
+ Slog.i(TAG, "Advanced protection: subsetting" + key + " is " + value);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ public boolean isUsbDataProtectionEnabled() {
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ return mStore.retrieveInt(AAPM_USB_DATA_PROTECTION, AdvancedProtectionStore.ON)
+ == AdvancedProtectionStore.ON;
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
@Override
@EnforcePermission(Manifest.permission.MANAGE_ADVANCED_PROTECTION_MODE)
public void logDialogShown(@FeatureId int featureId, @SupportDialogType int type,
@@ -303,7 +351,7 @@ public class AdvancedProtectionService extends IAdvancedProtectionService.Stub
getAdvancedProtectionFeatures_enforcePermission();
List<AdvancedProtectionFeature> features = new ArrayList<>();
for (int i = 0; i < mProviders.size(); i++) {
- features.addAll(mProviders.get(i).getFeatures());
+ features.addAll(mProviders.get(i).getFeatures(mContext));
}
for (int i = 0; i < mHooks.size(); i++) {
@@ -341,7 +389,7 @@ public class AdvancedProtectionService extends IAdvancedProtectionService.Stub
writer.println(" Providers: ");
mProviders.stream().forEach(provider -> {
writer.println(" " + provider.getClass().getSimpleName());
- provider.getFeatures().stream().forEach(feature -> {
+ provider.getFeatures(mContext).stream().forEach(feature -> {
writer.println(" " + feature.getClass().getSimpleName());
});
});
@@ -396,6 +444,7 @@ public class AdvancedProtectionService extends IAdvancedProtectionService.Stub
Slog.i(TAG, "Advanced protection is enabled");
}
mService.initFeatures(enabled);
+ mService.initLogging();
}
}
}
@@ -403,8 +452,8 @@ public class AdvancedProtectionService extends IAdvancedProtectionService.Stub
@VisibleForTesting
static class AdvancedProtectionStore {
private final Context mContext;
- private static final int APM_ON = 1;
- private static final int APM_OFF = 0;
+ static final int ON = 1;
+ static final int OFF = 0;
private final UserManagerInternal mUserManager;
AdvancedProtectionStore(@NonNull Context context) {
@@ -412,15 +461,26 @@ public class AdvancedProtectionService extends IAdvancedProtectionService.Stub
mUserManager = LocalServices.getService(UserManagerInternal.class);
}
- void store(boolean enabled) {
+ void storeAdvancedProtectionModeEnabled(boolean enabled) {
+ Settings.Secure.putIntForUser(mContext.getContentResolver(),
+ ADVANCED_PROTECTION_MODE, enabled ? ON : OFF,
+ mUserManager.getMainUserId());
+ }
+
+ boolean retrieveAdvancedProtectionModeEnabled() {
+ return Settings.Secure.getIntForUser(mContext.getContentResolver(),
+ ADVANCED_PROTECTION_MODE, OFF, mUserManager.getMainUserId()) == ON;
+ }
+
+ void storeInt(String key, int value) {
Settings.Secure.putIntForUser(mContext.getContentResolver(),
- ADVANCED_PROTECTION_MODE, enabled ? APM_ON : APM_OFF,
+ key, value,
mUserManager.getMainUserId());
}
- boolean retrieve() {
+ int retrieveInt(String key, int defaultValue) {
return Settings.Secure.getIntForUser(mContext.getContentResolver(),
- ADVANCED_PROTECTION_MODE, APM_OFF, mUserManager.getMainUserId()) == APM_ON;
+ key, defaultValue, mUserManager.getMainUserId());
}
}
@@ -497,4 +557,22 @@ public class AdvancedProtectionService extends IAdvancedProtectionService.Stub
}
}
}
+
+ private class AdvancedProtectionStatePullAtomCallback
+ implements StatsManager.StatsPullAtomCallback {
+
+ @Override
+ public int onPullAtom(int atomTag, List<StatsEvent> data) {
+ if (atomTag != FrameworkStatsLog.ADVANCED_PROTECTION_STATE_INFO) {
+ return StatsManager.PULL_SKIP;
+ }
+
+ data.add(
+ FrameworkStatsLog.buildStatsEvent(
+ FrameworkStatsLog.ADVANCED_PROTECTION_STATE_INFO,
+ /*enabled*/ isAdvancedProtectionEnabledInternal(),
+ /*hours_since_enabled*/ hoursSinceLastChange()));
+ return StatsManager.PULL_SUCCESS;
+ }
+ }
}
diff --git a/services/core/java/com/android/server/security/advancedprotection/AdvancedProtectionShellCommand.java b/services/core/java/com/android/server/security/advancedprotection/AdvancedProtectionShellCommand.java
index 42505ad2de3f..ae17a459010b 100644
--- a/services/core/java/com/android/server/security/advancedprotection/AdvancedProtectionShellCommand.java
+++ b/services/core/java/com/android/server/security/advancedprotection/AdvancedProtectionShellCommand.java
@@ -45,6 +45,10 @@ class AdvancedProtectionShellCommand extends ShellCommand {
return setProtectionEnabled();
case "is-protection-enabled":
return isProtectionEnabled(pw);
+ case "set-usb-data-protection-enabled":
+ return setUsbDataProtectedEnabled();
+ case "is-usb-data-protection-enabled":
+ return isUsbDataProtectedEnabled(pw);
}
} catch (RemoteException e) {
pw.println("Remote exception: " + e);
@@ -64,6 +68,10 @@ class AdvancedProtectionShellCommand extends ShellCommand {
pw.println(" Print this help text.");
pw.println(" set-protection-enabled [true|false]");
pw.println(" is-protection-enabled");
+ if(android.security.Flags.aapmFeatureUsbDataProtection()) {
+ pw.println(" set-usb-data-protection-enabled [true|false]");
+ pw.println(" is-usb-data-protection-enabled");
+ }
}
@SuppressLint("AndroidFrameworkRequiresPermission")
@@ -79,4 +87,22 @@ class AdvancedProtectionShellCommand extends ShellCommand {
pw.println(protectionMode);
return 0;
}
+
+ @SuppressLint("AndroidFrameworkRequiresPermission")
+ private int setUsbDataProtectedEnabled() throws RemoteException {
+ if(android.security.Flags.aapmFeatureUsbDataProtection()) {
+ String protectionMode = getNextArgRequired();
+ mService.setUsbDataProtectionEnabled(Boolean.parseBoolean(protectionMode));
+ }
+ return 0;
+ }
+
+ @SuppressLint("AndroidFrameworkRequiresPermission")
+ private int isUsbDataProtectedEnabled(@NonNull PrintWriter pw) throws RemoteException {
+ if(android.security.Flags.aapmFeatureUsbDataProtection()) {
+ boolean protectionMode = mService.isUsbDataProtectionEnabled();
+ pw.println(protectionMode);
+ }
+ return 0;
+ }
}
diff --git a/services/core/java/com/android/server/security/advancedprotection/features/AdvancedProtectionProvider.java b/services/core/java/com/android/server/security/advancedprotection/features/AdvancedProtectionProvider.java
index ed451f1e2257..6498cfc97aac 100644
--- a/services/core/java/com/android/server/security/advancedprotection/features/AdvancedProtectionProvider.java
+++ b/services/core/java/com/android/server/security/advancedprotection/features/AdvancedProtectionProvider.java
@@ -16,6 +16,8 @@
package com.android.server.security.advancedprotection.features;
+import android.annotation.NonNull;
+import android.content.Context;
import android.security.advancedprotection.AdvancedProtectionFeature;
import java.util.List;
@@ -23,5 +25,5 @@ import java.util.List;
/** @hide */
public abstract class AdvancedProtectionProvider {
/** The list of features provided */
- public abstract List<AdvancedProtectionFeature> getFeatures();
+ public abstract List<AdvancedProtectionFeature> getFeatures(@NonNull Context context);
}
diff --git a/services/core/java/com/android/server/security/advancedprotection/features/DisallowWepAdvancedProtectionProvider.java b/services/core/java/com/android/server/security/advancedprotection/features/DisallowWepAdvancedProtectionProvider.java
new file mode 100644
index 000000000000..1505f68b699e
--- /dev/null
+++ b/services/core/java/com/android/server/security/advancedprotection/features/DisallowWepAdvancedProtectionProvider.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2025 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.server.security.advancedprotection.features;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.net.wifi.WifiManager;
+import android.security.advancedprotection.AdvancedProtectionFeature;
+
+import java.util.List;
+
+public class DisallowWepAdvancedProtectionProvider extends AdvancedProtectionProvider {
+ public List<AdvancedProtectionFeature> getFeatures(@NonNull Context context) {
+ WifiManager wifiManager = context.getSystemService(WifiManager.class);
+ return wifiManager.getAvailableAdvancedProtectionFeatures();
+ }
+}
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index 798c794edaf5..0f6cc24f1fc9 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -87,6 +87,7 @@ import android.service.quicksettings.TileService;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.IndentingPrintWriter;
+import android.util.IntArray;
import android.util.Pair;
import android.util.Slog;
import android.util.SparseArray;
@@ -102,6 +103,7 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.inputmethod.SoftInputShowHideReason;
import com.android.internal.logging.InstanceId;
import com.android.internal.os.TransferPipe;
+import com.android.internal.statusbar.DisableStates;
import com.android.internal.statusbar.IAddTileResultCallback;
import com.android.internal.statusbar.ISessionListener;
import com.android.internal.statusbar.IStatusBar;
@@ -124,6 +126,7 @@ import com.android.server.policy.GlobalActionsProvider;
import com.android.server.power.ShutdownCheckPoints;
import com.android.server.power.ShutdownThread;
import com.android.server.wm.ActivityTaskManagerInternal;
+import com.android.systemui.shared.Flags;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -1344,48 +1347,76 @@ public class StatusBarManagerService extends IStatusBarService.Stub implements D
return mTracingEnabled;
}
- // TODO(b/117478341): make it aware of multi-display if needed.
+ /**
+ * Disable status bar features. Pass the bitwise-or of the {@code #DISABLE_*} flags.
+ * To re-enable everything, pass {@code #DISABLE_NONE}.
+ *
+ * Warning: Only pass {@code #DISABLE_*} flags into this function, do not use
+ * {@code #DISABLE2_*} flags.
+ */
@Override
public void disable(int what, IBinder token, String pkg) {
disableForUser(what, token, pkg, mCurrentUserId);
}
- // TODO(b/117478341): make it aware of multi-display if needed.
+ /**
+ * Disable status bar features for a given user. Pass the bitwise-or of the
+ * {@code #DISABLE_*} flags. To re-enable everything, pass {@code #DISABLE_NONE}.
+ *
+ * Warning: Only pass {@code #DISABLE_*} flags into this function, do not use
+ * {@code #DISABLE2_*} flags.
+ */
@Override
public void disableForUser(int what, IBinder token, String pkg, int userId) {
enforceStatusBar();
enforceValidCallingUser();
synchronized (mLock) {
- disableLocked(DEFAULT_DISPLAY, userId, what, token, pkg, 1);
+ if (Flags.statusBarConnectedDisplays()) {
+ IntArray displayIds = new IntArray();
+ for (int i = 0; i < mDisplayUiState.size(); i++) {
+ displayIds.add(mDisplayUiState.keyAt(i));
+ }
+ disableAllDisplaysLocked(displayIds, userId, what, token, pkg, /* whichFlag= */ 1);
+ } else {
+ disableLocked(DEFAULT_DISPLAY, userId, what, token, pkg, /* whichFlag= */ 1);
+ }
}
}
- // TODO(b/117478341): make it aware of multi-display if needed.
/**
- * Disable additional status bar features. Pass the bitwise-or of the DISABLE2_* flags.
- * To re-enable everything, pass {@link #DISABLE2_NONE}.
+ * Disable additional status bar features. Pass the bitwise-or of the {@code #DISABLE2_*} flags.
+ * To re-enable everything, pass {@code #DISABLE2_NONE}.
*
- * Warning: Only pass DISABLE2_* flags into this function, do not use DISABLE_* flags.
+ * Warning: Only pass {@code #DISABLE2_*} flags into this function, do not use
+ * {@code #DISABLE_*} flags.
*/
@Override
public void disable2(int what, IBinder token, String pkg) {
disable2ForUser(what, token, pkg, mCurrentUserId);
}
- // TODO(b/117478341): make it aware of multi-display if needed.
/**
- * Disable additional status bar features for a given user. Pass the bitwise-or of the
- * DISABLE2_* flags. To re-enable everything, pass {@link #DISABLE_NONE}.
+ * Disable additional status bar features for a given user. Pass the bitwise-or
+ * of the {@code #DISABLE2_*} flags. To re-enable everything, pass {@code #DISABLE2_NONE}.
*
- * Warning: Only pass DISABLE2_* flags into this function, do not use DISABLE_* flags.
+ * Warning: Only pass {@code #DISABLE2_*} flags into this function, do not use
+ * {@code #DISABLE_*} flags.
*/
@Override
public void disable2ForUser(int what, IBinder token, String pkg, int userId) {
enforceStatusBar();
synchronized (mLock) {
- disableLocked(DEFAULT_DISPLAY, userId, what, token, pkg, 2);
+ if (Flags.statusBarConnectedDisplays()) {
+ IntArray displayIds = new IntArray();
+ for (int i = 0; i < mDisplayUiState.size(); i++) {
+ displayIds.add(mDisplayUiState.keyAt(i));
+ }
+ disableAllDisplaysLocked(displayIds, userId, what, token, pkg, /* whichFlag= */ 2);
+ } else {
+ disableLocked(DEFAULT_DISPLAY, userId, what, token, pkg, /* whichFlag= */ 2);
+ }
}
}
@@ -1414,6 +1445,42 @@ public class StatusBarManagerService extends IStatusBarService.Stub implements D
}
}
+ // This method batches disable state across all displays into a single remote call
+ // (IStatusBar#disableForAllDisplays) for efficiency and calls
+ // NotificationDelegate#onSetDisabled only if any display's disable state changes.
+ private void disableAllDisplaysLocked(IntArray displayIds, int userId, int what, IBinder token,
+ String pkg, int whichFlag) {
+ // It's important that the the callback and the call to mBar get done
+ // in the same order when multiple threads are calling this function
+ // so they are paired correctly. The messages on the handler will be
+ // handled in the order they were enqueued, but will be outside the lock.
+ manageDisableListLocked(userId, what, token, pkg, whichFlag);
+
+ // Ensure state for the current user is applied, even if passed a non-current user.
+ final int net1 = gatherDisableActionsLocked(mCurrentUserId, 1);
+ final int net2 = gatherDisableActionsLocked(mCurrentUserId, 2);
+
+ IStatusBar bar = mBar;
+ Map<Integer, Pair<Integer, Integer>> displaysWithNewDisableStates = new HashMap<>();
+ for (int displayId : displayIds.toArray()) {
+ final UiState state = getUiState(displayId);
+ if (!state.disableEquals(net1, net2)) {
+ state.setDisabled(net1, net2);
+ displaysWithNewDisableStates.put(displayId, new Pair(net1, net2));
+ }
+ }
+ if (bar != null) {
+ try {
+ bar.disableForAllDisplays(new DisableStates(displaysWithNewDisableStates));
+ } catch (RemoteException ex) {
+ Slog.e(TAG, "Unable to disable Status bar.", ex);
+ }
+ }
+ if (!displaysWithNewDisableStates.isEmpty()) {
+ mHandler.post(() -> mNotificationDelegate.onSetDisabled(net1));
+ }
+ }
+
/**
* Get the currently applied disable flags, in the form of one Pair<Integer, Integer>.
*
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperCropper.java b/services/core/java/com/android/server/wallpaper/WallpaperCropper.java
index 424439df3c4b..e22bc7d0719e 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperCropper.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperCropper.java
@@ -863,7 +863,7 @@ public class WallpaperCropper {
double maxDisplayToImageRatio = Math.max((double) displaySize.x / croppedImageBound.width(),
(double) displaySize.y / croppedImageBound.height());
- if (maxDisplayToImageRatio > 1.3) {
+ if (maxDisplayToImageRatio > 1.5) {
return false;
}
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerInternal.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerInternal.java
index f413fe33c3f2..58f34d0404d0 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerInternal.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerInternal.java
@@ -36,7 +36,4 @@ public abstract class WallpaperManagerInternal {
/** Notifies when the screen starts turning on and is not yet visible to the user. */
public abstract void onScreenTurningOn(int displayId);
-
- /** Notifies when the keyguard is going away. Sent right after the bouncer is gone. */
- public abstract void onKeyguardGoingAway();
}
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index e7da33d50b27..274175aa71ba 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -22,6 +22,7 @@ import static android.Manifest.permission.READ_WALLPAPER_INTERNAL;
import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND;
import static android.app.Flags.fixWallpaperChanged;
import static android.app.Flags.liveWallpaperContentHandling;
+import static android.app.Flags.notifyKeyguardEvents;
import static android.app.Flags.removeNextWallpaperComponent;
import static android.app.WallpaperManager.COMMAND_REAPPLY;
import static android.app.WallpaperManager.FLAG_LOCK;
@@ -1709,8 +1710,32 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
mWallpaperDataParser = new WallpaperDataParser(mContext, mWallpaperDisplayHelper,
mWallpaperCropper);
LocalServices.addService(WallpaperManagerInternal.class, new LocalService());
+
+ LocalServices.getService(ActivityTaskManagerInternal.class)
+ .registerScreenObserver(mKeyguardObserver);
+
}
+ private final ActivityTaskManagerInternal.ScreenObserver mKeyguardObserver =
+ new ActivityTaskManagerInternal.ScreenObserver() {
+ @Override
+ public void onKeyguardStateChanged(boolean isShowing) {
+ if (!notifyKeyguardEvents()) {
+ return;
+ }
+ if (isShowing) {
+ notifyKeyguardAppearing();
+ } else {
+ notifyKeyguardGoingAway();
+ }
+ }
+
+ @Override
+ public void onKeyguardGoingAway() {
+ notifyKeyguardGoingAway();
+ }
+ };
+
private final class LocalService extends WallpaperManagerInternal {
@Override
public void onDisplayAddSystemDecorations(int displayId) {
@@ -1733,11 +1758,6 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
public void onScreenTurningOn(int displayId) {
notifyScreenTurningOn(displayId);
}
-
- @Override
- public void onKeyguardGoingAway() {
- notifyKeyguardGoingAway();
- }
}
void initialize() {
@@ -2571,6 +2591,18 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
return mContext.checkCallingOrSelfPermission(permission) == PERMISSION_GRANTED;
}
+ private boolean hasPermission(WallpaperData data, String permission) {
+ try {
+ return PackageManager.PERMISSION_GRANTED == mIPackageManager.checkPermission(
+ permission,
+ data.getComponent().getPackageName(),
+ data.userId);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to check wallpaper service permission", e);
+ return false;
+ }
+ }
+
private boolean hasAppOpPermission(String permission, int callingUid, String callingPackage,
String attributionTag, String message) {
final String op = AppOpsManager.permissionToOp(permission);
@@ -2873,16 +2905,37 @@ public class WallpaperManagerService extends IWallpaperManager.Stub
* Propagate a keyguard going away event to the wallpaper engine.
*/
private void notifyKeyguardGoingAway() {
+ dispatchKeyguardCommand(WallpaperManager.COMMAND_KEYGUARD_GOING_AWAY);
+ }
+
+ /**
+ * Propagate a keyguard appearing event to the wallpaper engine.
+ */
+ private void notifyKeyguardAppearing() {
+ dispatchKeyguardCommand(WallpaperManager.COMMAND_KEYGUARD_APPEARING);
+ }
+
+ /**
+ * Propagate a keyguard-related event to the wallpaper engine.
+ *
+ * When the flag below is enabled, the event will only be dispatched in case the recipient
+ * has {@link android.Manifest.pertmission.SUBSCRIBE_TO_KEYGUARD_LOCKED_STATE} permission.
+ */
+ private void dispatchKeyguardCommand(String command) {
synchronized (mLock) {
for (WallpaperData data : getActiveWallpapers()) {
+ if (notifyKeyguardEvents() && !hasPermission(
+ data, android.Manifest.permission.SUBSCRIBE_TO_KEYGUARD_LOCKED_STATE)) {
+ continue;
+ }
+
data.connection.forEachDisplayConnector(displayConnector -> {
if (displayConnector.mEngine != null) {
try {
displayConnector.mEngine.dispatchWallpaperCommand(
- WallpaperManager.COMMAND_KEYGUARD_GOING_AWAY,
- -1, -1, -1, new Bundle());
+ command, -1, -1, -1, new Bundle());
} catch (RemoteException e) {
- Slog.w(TAG, "Failed to notify that the keyguard is going away", e);
+ Slog.w(TAG, "Failed to dispatch wallpaper command: " + command, e);
}
}
});
diff --git a/services/core/java/com/android/server/wm/AbsAppSnapshotController.java b/services/core/java/com/android/server/wm/AbsAppSnapshotController.java
index a731bf7c64b3..ee173a2a6a35 100644
--- a/services/core/java/com/android/server/wm/AbsAppSnapshotController.java
+++ b/services/core/java/com/android/server/wm/AbsAppSnapshotController.java
@@ -82,6 +82,7 @@ abstract class AbsAppSnapshotController<TYPE extends WindowContainer,
*/
@VisibleForTesting
static final int SNAPSHOT_MODE_NONE = 2;
+ static final float THEME_SNAPSHOT_MIN_Length = 128.0f;
protected final WindowManagerService mService;
protected final float mHighResSnapshotScale;
@@ -130,6 +131,10 @@ abstract class AbsAppSnapshotController<TYPE extends WindowContainer,
mCache = cache;
}
+ void setSnapshotReleaser(Consumer<HardwareBuffer> releaser) {
+ mCache.setSafeSnapshotReleaser(releaser);
+ }
+
void setSnapshotEnabled(boolean enabled) {
mSnapshotEnabled = enabled;
}
@@ -436,14 +441,21 @@ abstract class AbsAppSnapshotController<TYPE extends WindowContainer,
final Rect taskBounds = source.getBounds();
final InsetsState insetsState = mainWindow.getInsetsStateWithVisibilityOverride();
final Rect systemBarInsets = getSystemBarInsets(mainWindow.getFrame(), insetsState);
+ final int taskWidth = taskBounds.width();
+ final int taskHeight = taskBounds.height();
+ float scale = mHighResSnapshotScale;
+ if (Flags.reduceTaskSnapshotMemoryUsage()) {
+ final int minLength = Math.min(taskWidth, taskHeight);
+ if (THEME_SNAPSHOT_MIN_Length < minLength) {
+ scale = Math.min(THEME_SNAPSHOT_MIN_Length / minLength, scale);
+ }
+ }
final SnapshotDrawerUtils.SystemBarBackgroundPainter
decorPainter = new SnapshotDrawerUtils.SystemBarBackgroundPainter(attrs.flags,
attrs.privateFlags, attrs.insetsFlags.appearance, taskDescription,
- mHighResSnapshotScale, mainWindow.getRequestedVisibleTypes());
- final int taskWidth = taskBounds.width();
- final int taskHeight = taskBounds.height();
- final int width = (int) (taskWidth * mHighResSnapshotScale);
- final int height = (int) (taskHeight * mHighResSnapshotScale);
+ scale, mainWindow.getRequestedVisibleTypes());
+ final int width = (int) (taskWidth * scale);
+ final int height = (int) (taskHeight * scale);
final RenderNode node = RenderNode.create("SnapshotController", null);
node.setLeftTopRightBottom(0, 0, width, height);
node.setClipToBounds(false);
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index df00fa195f03..b9ab863a2805 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -158,7 +158,6 @@ import static com.android.server.wm.ActivityRecordProto.FRONT_OF_TASK;
import static com.android.server.wm.ActivityRecordProto.IN_SIZE_COMPAT_MODE;
import static com.android.server.wm.ActivityRecordProto.IS_ANIMATING;
import static com.android.server.wm.ActivityRecordProto.IS_USER_FULLSCREEN_OVERRIDE_ENABLED;
-import static com.android.server.wm.ActivityRecordProto.LAST_ALL_DRAWN;
import static com.android.server.wm.ActivityRecordProto.LAST_DROP_INPUT_MODE;
import static com.android.server.wm.ActivityRecordProto.LAST_SURFACE_SHOWING;
import static com.android.server.wm.ActivityRecordProto.MIN_ASPECT_RATIO;
@@ -723,7 +722,6 @@ final class ActivityRecord extends WindowToken {
private int mNumInterestingWindows;
private int mNumDrawnWindows;
boolean allDrawn;
- private boolean mLastAllDrawn;
/**
* Solely for reporting to ActivityMetricsLogger. Just tracks whether, the last time this
@@ -1148,13 +1146,11 @@ final class ActivityRecord extends WindowToken {
if (mAppStopped) {
pw.print(prefix); pw.print("mAppStopped="); pw.println(mAppStopped);
}
- if (mNumInterestingWindows != 0 || mNumDrawnWindows != 0
- || allDrawn || mLastAllDrawn) {
+ if (mNumInterestingWindows != 0 || mNumDrawnWindows != 0 || allDrawn) {
pw.print(prefix); pw.print("mNumInterestingWindows=");
pw.print(mNumInterestingWindows);
pw.print(" mNumDrawnWindows="); pw.print(mNumDrawnWindows);
pw.print(" allDrawn="); pw.print(allDrawn);
- pw.print(" lastAllDrawn="); pw.print(mLastAllDrawn);
pw.println(")");
}
if (mStartingData != null || firstWindowDrawn) {
@@ -3665,13 +3661,6 @@ final class ActivityRecord extends WindowToken {
if (endTask) {
mAtmService.getLockTaskController().clearLockedTask(task);
- // This activity was in the top focused root task and this is the last
- // activity in that task, give this activity a higher layer so it can stay on
- // top before the closing task transition be executed.
- if (mayAdjustTop) {
- mNeedsZBoost = true;
- mDisplayContent.assignWindowLayers(false /* setLayoutNeeded */);
- }
}
} else if (!isState(PAUSING)) {
if (mVisibleRequested) {
@@ -5155,7 +5144,6 @@ final class ActivityRecord extends WindowToken {
void clearAllDrawn() {
allDrawn = false;
- mLastAllDrawn = false;
}
/**
@@ -5583,6 +5571,13 @@ final class ActivityRecord extends WindowToken {
// called for updating snapshot states.
if (!fromTransition) {
mWmService.mSnapshotController.notifyAppVisibilityChanged(this, visible);
+ if (visible) {
+ // In case the activity becomes visible without transition, the client still expects
+ // to receive Activity#onEnterAnimationComplete.
+ mEnteringAnimation = true;
+ mWmService.mActivityManagerAppTransitionNotifier.onAppTransitionFinishedLocked(
+ token);
+ }
}
}
@@ -6592,35 +6587,6 @@ final class ActivityRecord extends WindowToken {
nowVisible = false;
}
- @Override
- void checkAppWindowsReadyToShow() {
- if (allDrawn == mLastAllDrawn) {
- return;
- }
-
- mLastAllDrawn = allDrawn;
- if (!allDrawn) {
- return;
- }
-
- setAppLayoutChanges(FINISH_LAYOUT_REDO_ANIM, "checkAppWindowsReadyToShow");
-
- // We can now show all of the drawn windows!
- if (canShowWindows()) {
- showAllWindowsLocked();
- }
- }
-
- /**
- * This must be called while inside a transaction.
- */
- void showAllWindowsLocked() {
- forAllWindows(windowState -> {
- if (DEBUG_VISIBILITY) Slog.v(TAG, "performing show on: " + windowState);
- windowState.performShowLocked();
- }, false /* traverseTopToBottom */);
- }
-
void updateReportedVisibilityLocked() {
if (DEBUG_VISIBILITY) Slog.v(TAG, "Update reported visibility: " + this);
final int count = mChildren.size();
@@ -7234,11 +7200,6 @@ final class ActivityRecord extends WindowToken {
}
@Override
- boolean needsZBoost() {
- return mNeedsZBoost || super.needsZBoost();
- }
-
- @Override
public SurfaceControl getAnimationLeashParent() {
// For transitions in the root pinned task (menu activity) we just let them occur as a child
// of the root pinned task.
@@ -9386,7 +9347,6 @@ final class ActivityRecord extends WindowToken {
proto.write(NUM_INTERESTING_WINDOWS, mNumInterestingWindows);
proto.write(NUM_DRAWN_WINDOWS, mNumDrawnWindows);
proto.write(ALL_DRAWN, allDrawn);
- proto.write(LAST_ALL_DRAWN, mLastAllDrawn);
if (mStartingWindow != null) {
mStartingWindow.writeIdentifierToProto(proto, STARTING_WINDOW);
}
diff --git a/services/core/java/com/android/server/wm/ActivitySnapshotCache.java b/services/core/java/com/android/server/wm/ActivitySnapshotCache.java
index ed07afd2eab5..a0e537231071 100644
--- a/services/core/java/com/android/server/wm/ActivitySnapshotCache.java
+++ b/services/core/java/com/android/server/wm/ActivitySnapshotCache.java
@@ -31,6 +31,7 @@ class ActivitySnapshotCache extends SnapshotCache<ActivityRecord> {
void putSnapshot(ActivityRecord ar, TaskSnapshot snapshot) {
final int hasCode = System.identityHashCode(ar);
snapshot.addReference(TaskSnapshot.REFERENCE_CACHE);
+ snapshot.setSafeRelease(mSafeSnapshotReleaser);
synchronized (mLock) {
final CacheEntry entry = mRunningCache.get(hasCode);
if (entry != null) {
diff --git a/services/core/java/com/android/server/wm/ActivityStartInterceptor.java b/services/core/java/com/android/server/wm/ActivityStartInterceptor.java
index 5cc186c40b6c..a19f4388a7c8 100644
--- a/services/core/java/com/android/server/wm/ActivityStartInterceptor.java
+++ b/services/core/java/com/android/server/wm/ActivityStartInterceptor.java
@@ -272,6 +272,12 @@ class ActivityStartInterceptor {
mActivityOptions = interceptResult.getActivityOptions();
mCallingPid = mRealCallingPid;
mCallingUid = mRealCallingUid;
+ // When an activity launch is intercepted, Intent#prepareToLeaveProcess is not called
+ // since the interception happens in the system_server. So if any activity is calling
+ // a trampoline activity, the keys do not get collected. Since all the interceptors
+ // are present in the system_server, add the creator token before launching the
+ // intercepted intent.
+ mService.mAmInternal.addCreatorToken(mIntent, mCallingPackage);
if (interceptResult.isActivityResolved()) {
return true;
}
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
index c243cdc23137..7123a7c1160f 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
@@ -124,8 +124,9 @@ public abstract class ActivityTaskManagerInternal {
public static final String ASSIST_KEY_RECEIVER_EXTRAS = "receiverExtras";
public interface ScreenObserver {
- void onAwakeStateChanged(boolean isAwake);
- void onKeyguardStateChanged(boolean isShowing);
+ default void onAwakeStateChanged(boolean isAwake) {}
+ default void onKeyguardStateChanged(boolean isShowing) {}
+ default void onKeyguardGoingAway() {}
}
/**
@@ -802,4 +803,10 @@ public abstract class ActivityTaskManagerInternal {
/** Returns whether assist data is allowed. */
public abstract boolean isAssistDataAllowed();
+
+ /**
+ * Delegate back gesture request from shell.
+ * Returns true if the back gesture request was successful, false otherwise.
+ */
+ public abstract boolean requestBackGesture();
}
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 9c9656671519..a0c38dd82037 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -281,7 +281,6 @@ import com.android.server.sdksandbox.SdkSandboxManagerLocal;
import com.android.server.statusbar.StatusBarManagerInternal;
import com.android.server.uri.NeededUriGrants;
import com.android.server.uri.UriGrantsManagerInternal;
-import com.android.server.wallpaper.WallpaperManagerInternal;
import com.android.server.wm.utils.WindowStyleCache;
import com.android.wm.shell.Flags;
@@ -373,7 +372,6 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
private ComponentName mSysUiServiceComponent;
private PermissionPolicyInternal mPermissionPolicyInternal;
private StatusBarManagerInternal mStatusBarManagerInternal;
- private WallpaperManagerInternal mWallpaperManagerInternal;
private UserManagerInternal mUserManagerInternal;
@VisibleForTesting
final ActivityTaskManagerInternal mInternal;
@@ -1894,6 +1892,18 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
}
}
+ @Override
+ public void registerBackGestureDelegate(RemoteCallback requestObserver) {
+ mAmInternal.enforceCallingPermission(START_TASKS_FROM_RECENTS,
+ "registerBackGestureDelegate()");
+ final long origId = Binder.clearCallingIdentity();
+ try {
+ mBackNavigationController.registerBackGestureDelegate(requestObserver);
+ } finally {
+ Binder.restoreCallingIdentity(origId);
+ }
+ }
+
/**
* Public API to check if the client is allowed to start an activity on specified display.
*
@@ -3719,10 +3729,12 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
if (isPowerModePreApplied && !foundResumed) {
endPowerMode(POWER_MODE_REASON_START_ACTIVITY);
}
- }
- WallpaperManagerInternal wallpaperManagerInternal = getWallpaperManagerInternal();
- if (wallpaperManagerInternal != null) {
- wallpaperManagerInternal.onKeyguardGoingAway();
+
+ mH.post(() -> {
+ for (int i = mScreenObservers.size() - 1; i >= 0; i--) {
+ mScreenObservers.get(i).onKeyguardGoingAway();
+ }
+ });
}
} finally {
Binder.restoreCallingIdentity(token);
@@ -5573,13 +5585,6 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
return mStatusBarManagerInternal;
}
- WallpaperManagerInternal getWallpaperManagerInternal() {
- if (mWallpaperManagerInternal == null) {
- mWallpaperManagerInternal = LocalServices.getService(WallpaperManagerInternal.class);
- }
- return mWallpaperManagerInternal;
- }
-
UserManagerInternal getUserManagerInternal() {
if (mUserManagerInternal == null) {
mUserManagerInternal = LocalServices.getService(UserManagerInternal.class);
@@ -7578,6 +7583,11 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
public boolean isAssistDataAllowed() {
return ActivityTaskManagerService.this.isAssistDataAllowed();
}
+
+ @Override
+ public boolean requestBackGesture() {
+ return mBackNavigationController.requestBackGesture();
+ }
}
/** Cache the return value for {@link #isPip2ExperimentEnabled()} */
diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
index b7ef1057388c..0e14f83c96f8 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
@@ -2650,9 +2650,13 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks {
*/
void endDeferResume() {
mDeferResumeCount--;
- if (readyToResume() && mLastReportedTopResumedActivity != null
- && mTopResumedActivity != mLastReportedTopResumedActivity) {
- scheduleTopResumedActivityStateLossIfNeeded();
+ if (readyToResume()) {
+ if (mLastReportedTopResumedActivity != null
+ && mTopResumedActivity != mLastReportedTopResumedActivity) {
+ scheduleTopResumedActivityStateLossIfNeeded();
+ } else if (mLastReportedTopResumedActivity == null) {
+ scheduleTopResumedActivityStateIfNeeded();
+ }
}
}
diff --git a/services/core/java/com/android/server/wm/AppCompatCameraOverrides.java b/services/core/java/com/android/server/wm/AppCompatCameraOverrides.java
index cb82f480d73b..d152a1dbe17d 100644
--- a/services/core/java/com/android/server/wm/AppCompatCameraOverrides.java
+++ b/services/core/java/com/android/server/wm/AppCompatCameraOverrides.java
@@ -35,6 +35,7 @@ import static com.android.server.wm.AppCompatUtils.isChangeEnabled;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.window.DesktopModeFlags;
import com.android.server.wm.utils.OptPropFactory;
import com.android.window.flags.Flags;
@@ -177,7 +178,7 @@ class AppCompatCameraOverrides {
* </ul>
*/
boolean shouldApplyFreeformTreatmentForCameraCompat() {
- return Flags.enableCameraCompatForDesktopWindowing()
+ return DesktopModeFlags.ENABLE_CAMERA_COMPAT_SIMULATE_REQUESTED_ORIENTATION.isTrue()
&& (shouldEnableCameraCompatFreeformTreatmentForApp()
|| shouldEnableCameraCompatFreeformTreatmentForAllApps());
}
diff --git a/services/core/java/com/android/server/wm/AppCompatCameraPolicy.java b/services/core/java/com/android/server/wm/AppCompatCameraPolicy.java
index 276c7d2cbaa0..e7a0803df916 100644
--- a/services/core/java/com/android/server/wm/AppCompatCameraPolicy.java
+++ b/services/core/java/com/android/server/wm/AppCompatCameraPolicy.java
@@ -26,9 +26,9 @@ import android.app.CameraCompatTaskInfo;
import android.content.pm.ActivityInfo.ScreenOrientation;
import android.content.res.Configuration;
import android.widget.Toast;
+import android.window.DesktopModeFlags;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.window.flags.Flags;
/**
* Encapsulate policy logic related to app compat display rotation.
@@ -53,7 +53,7 @@ class AppCompatCameraPolicy {
final boolean needsDisplayRotationCompatPolicy =
wmService.mAppCompatConfiguration.isCameraCompatTreatmentEnabledAtBuildTime();
final boolean needsCameraCompatFreeformPolicy =
- Flags.enableCameraCompatForDesktopWindowing()
+ DesktopModeFlags.ENABLE_CAMERA_COMPAT_SIMULATE_REQUESTED_ORIENTATION.isTrue()
&& DesktopModeHelper.canEnterDesktopMode(wmService.mContext);
if (needsDisplayRotationCompatPolicy || needsCameraCompatFreeformPolicy) {
mCameraStateMonitor = new CameraStateMonitor(displayContent, wmService.mH);
diff --git a/services/core/java/com/android/server/wm/AppCompatSafeRegionPolicy.java b/services/core/java/com/android/server/wm/AppCompatSafeRegionPolicy.java
index 959609309da1..cd6a01478eef 100644
--- a/services/core/java/com/android/server/wm/AppCompatSafeRegionPolicy.java
+++ b/services/core/java/com/android/server/wm/AppCompatSafeRegionPolicy.java
@@ -156,6 +156,8 @@ class AppCompatSafeRegionPolicy {
void dump(@NonNull PrintWriter pw, @NonNull String prefix) {
if (mNeedsSafeRegionBounds) {
pw.println(prefix + " mNeedsSafeRegionBounds=true");
+ pw.println(
+ prefix + " latestSafeRegionBoundsOnActivity=" + getLatestSafeRegionBounds());
}
if (isLetterboxedForSafeRegionOnlyAllowed()) {
pw.println(prefix + " isLetterboxForSafeRegionOnlyAllowed=true");
diff --git a/services/core/java/com/android/server/wm/AsyncRotationController.java b/services/core/java/com/android/server/wm/AsyncRotationController.java
index d3fd0e3199a3..f75b17fa1569 100644
--- a/services/core/java/com/android/server/wm/AsyncRotationController.java
+++ b/services/core/java/com/android/server/wm/AsyncRotationController.java
@@ -234,7 +234,7 @@ class AsyncRotationController extends FadeAnimationController implements Consume
}
for (int i = mTargetWindowTokens.size() - 1; i >= 0; i--) {
final Operation op = mTargetWindowTokens.valueAt(i);
- if (op.mIsCompletionPending || op.mAction == Operation.ACTION_SEAMLESS) {
+ if (op.mIsCompletionPending || op.mActions == Operation.ACTION_SEAMLESS) {
// Skip completed target. And seamless windows use the signal from blast sync.
continue;
}
@@ -264,17 +264,18 @@ class AsyncRotationController extends FadeAnimationController implements Consume
op.mDrawTransaction = null;
if (DEBUG) Slog.d(TAG, "finishOp merge transaction " + windowToken.getTopChild());
}
- if (op.mAction == Operation.ACTION_TOGGLE_IME) {
+ if (op.mActions == Operation.ACTION_TOGGLE_IME) {
if (DEBUG) Slog.d(TAG, "finishOp fade-in IME " + windowToken.getTopChild());
fadeWindowToken(true /* show */, windowToken, ANIMATION_TYPE_TOKEN_TRANSFORM,
(type, anim) -> mDisplayContent.getInsetsStateController()
.getImeSourceProvider().reportImeDrawnForOrganizer());
- } else if (op.mAction == Operation.ACTION_FADE) {
+ } else if ((op.mActions & Operation.ACTION_FADE) != 0) {
if (DEBUG) Slog.d(TAG, "finishOp fade-in " + windowToken.getTopChild());
// The previous animation leash will be dropped when preparing fade-in animation, so
// simply apply new animation without restoring the transformation.
fadeWindowToken(true /* show */, windowToken, ANIMATION_TYPE_TOKEN_TRANSFORM);
- } else if (op.isValidSeamless()) {
+ }
+ if (op.isValidSeamless()) {
if (DEBUG) Slog.d(TAG, "finishOp undo seamless " + windowToken.getTopChild());
final SurfaceControl.Transaction t = windowToken.getSyncTransaction();
clearTransform(t, op.mLeash);
@@ -339,7 +340,7 @@ class AsyncRotationController extends FadeAnimationController implements Consume
}
if (mTransitionOp == OP_APP_SWITCH && token.mTransitionController.inTransition()) {
final Operation op = mTargetWindowTokens.get(token);
- if (op != null && op.mAction == Operation.ACTION_FADE) {
+ if (op != null && op.mActions == Operation.ACTION_FADE) {
// Defer showing to onTransitionFinished().
if (DEBUG) Slog.d(TAG, "Defer completion " + token.getTopChild());
return false;
@@ -367,11 +368,12 @@ class AsyncRotationController extends FadeAnimationController implements Consume
for (int i = mTargetWindowTokens.size() - 1; i >= 0; i--) {
final WindowToken windowToken = mTargetWindowTokens.keyAt(i);
final Operation op = mTargetWindowTokens.valueAt(i);
- if (op.mAction == Operation.ACTION_FADE || op.mAction == Operation.ACTION_TOGGLE_IME) {
+ if ((op.mActions & Operation.ACTION_FADE) != 0
+ || op.mActions == Operation.ACTION_TOGGLE_IME) {
fadeWindowToken(false /* show */, windowToken, ANIMATION_TYPE_TOKEN_TRANSFORM);
op.mLeash = windowToken.getAnimationLeash();
if (DEBUG) Slog.d(TAG, "Start fade-out " + windowToken.getTopChild());
- } else if (op.mAction == Operation.ACTION_SEAMLESS) {
+ } else if (op.mActions == Operation.ACTION_SEAMLESS) {
op.mLeash = windowToken.mSurfaceControl;
if (DEBUG) Slog.d(TAG, "Start seamless " + windowToken.getTopChild());
}
@@ -481,13 +483,13 @@ class AsyncRotationController extends FadeAnimationController implements Consume
/** Returns {@code true} if the controller will run fade animations on the window. */
boolean hasFadeOperation(WindowToken token) {
final Operation op = mTargetWindowTokens.get(token);
- return op != null && op.mAction == Operation.ACTION_FADE;
+ return op != null && (op.mActions & Operation.ACTION_FADE) != 0;
}
/** Returns {@code true} if the window is un-rotated to original rotation. */
boolean hasSeamlessOperation(WindowToken token) {
final Operation op = mTargetWindowTokens.get(token);
- return op != null && op.mAction == Operation.ACTION_SEAMLESS;
+ return op != null && (op.mActions & Operation.ACTION_SEAMLESS) != 0;
}
/**
@@ -541,7 +543,7 @@ class AsyncRotationController extends FadeAnimationController implements Consume
final Operation op = mTargetWindowTokens.valueAt(i);
final SurfaceControl leash = op.mLeash;
if (leash == null || !leash.isValid()) continue;
- if (mHasScreenRotationAnimation && op.mAction == Operation.ACTION_FADE) {
+ if (mHasScreenRotationAnimation && op.mActions == Operation.ACTION_FADE) {
// Hide the windows immediately because a screenshot layer should cover the screen.
t.setAlpha(leash, 0f);
if (DEBUG) {
@@ -707,7 +709,7 @@ class AsyncRotationController extends FadeAnimationController implements Consume
* start transaction of rotation transition is applied.
*/
private boolean canDrawBeforeStartTransaction(Operation op) {
- return op.mAction != Operation.ACTION_SEAMLESS;
+ return (op.mActions & Operation.ACTION_SEAMLESS) == 0;
}
void dump(PrintWriter pw, String prefix) {
@@ -723,14 +725,14 @@ class AsyncRotationController extends FadeAnimationController implements Consume
/** The operation to control the rotation appearance associated with window token. */
private static class Operation {
@Retention(RetentionPolicy.SOURCE)
- @IntDef(value = { ACTION_SEAMLESS, ACTION_FADE, ACTION_TOGGLE_IME })
+ @IntDef(flag = true, value = { ACTION_SEAMLESS, ACTION_FADE, ACTION_TOGGLE_IME })
@interface Action {}
static final int ACTION_SEAMLESS = 1;
- static final int ACTION_FADE = 2;
- /** The action to toggle the IME window appearance */
- static final int ACTION_TOGGLE_IME = 3;
- final @Action int mAction;
+ static final int ACTION_FADE = 1 << 1;
+ /** The action to toggle the IME window appearance. It can only be used exclusively. */
+ static final int ACTION_TOGGLE_IME = 1 << 2;
+ final @Action int mActions;
/** The leash of window token. It can be animation leash or the token itself. */
SurfaceControl mLeash;
/** Whether the window is drawn before the transition starts. */
@@ -744,17 +746,17 @@ class AsyncRotationController extends FadeAnimationController implements Consume
*/
SurfaceControl.Transaction mDrawTransaction;
- Operation(@Action int action) {
- mAction = action;
+ Operation(@Action int actions) {
+ mActions = actions;
}
boolean isValidSeamless() {
- return mAction == ACTION_SEAMLESS && mLeash != null && mLeash.isValid();
+ return (mActions & ACTION_SEAMLESS) != 0 && mLeash != null && mLeash.isValid();
}
@Override
public String toString() {
- return "Operation{a=" + mAction + " pending=" + mIsCompletionPending + '}';
+ return "Operation{a=" + mActions + " pending=" + mIsCompletionPending + '}';
}
}
}
diff --git a/services/core/java/com/android/server/wm/BackNavigationController.java b/services/core/java/com/android/server/wm/BackNavigationController.java
index dfe323c43abb..819e4abaa9d3 100644
--- a/services/core/java/com/android/server/wm/BackNavigationController.java
+++ b/services/core/java/com/android/server/wm/BackNavigationController.java
@@ -85,6 +85,7 @@ class BackNavigationController {
private boolean mShowWallpaper;
private Runnable mPendingAnimation;
private final NavigationMonitor mNavigationMonitor = new NavigationMonitor();
+ private RemoteCallback mGestureRequest;
private AnimationHandler mAnimationHandler;
@@ -113,6 +114,35 @@ class BackNavigationController {
mNavigationMonitor.onEmbeddedWindowGestureTransferred(host);
}
+ void registerBackGestureDelegate(@NonNull RemoteCallback requestObserver) {
+ if (!sPredictBackEnable) {
+ return;
+ }
+ synchronized (mWindowManagerService.mGlobalLock) {
+ mGestureRequest = requestObserver;
+ try {
+ requestObserver.getInterface().asBinder().linkToDeath(() -> {
+ synchronized (mWindowManagerService.mGlobalLock) {
+ mGestureRequest = null;
+ }
+ }, 0 /* flags */);
+ } catch (RemoteException r) {
+ Slog.e(TAG, "Failed to link to death");
+ mGestureRequest = null;
+ }
+ }
+ }
+
+ boolean requestBackGesture() {
+ synchronized (mWindowManagerService.mGlobalLock) {
+ if (mGestureRequest == null) {
+ return false;
+ }
+ mGestureRequest.sendResult(null);
+ return true;
+ }
+ }
+
/**
* Set up the necessary leashes and build a {@link BackNavigationInfo} instance for an upcoming
* back gesture animation.
diff --git a/services/core/java/com/android/server/wm/BackgroundActivityStartCallback.java b/services/core/java/com/android/server/wm/BackgroundActivityStartCallback.java
index eec648dae24a..826d5fb1c333 100644
--- a/services/core/java/com/android/server/wm/BackgroundActivityStartCallback.java
+++ b/services/core/java/com/android/server/wm/BackgroundActivityStartCallback.java
@@ -24,6 +24,16 @@ import java.util.Collection;
* Callback to decide activity starts and related operations based on originating tokens.
*/
public interface BackgroundActivityStartCallback {
+ BackgroundActivityStartCallbackResult RESULT_FALSE =
+ new BackgroundActivityStartCallbackResult(false, null);
+ BackgroundActivityStartCallbackResult RESULT_TRUE =
+ new BackgroundActivityStartCallbackResult(true, null);
+
+ record BackgroundActivityStartCallbackResult(
+ boolean allowed,
+ IBinder token
+ ) {}
+
/**
* Returns true if the background activity start originating from {@code tokens} should be
* allowed or not.
@@ -34,7 +44,8 @@ public interface BackgroundActivityStartCallback {
* This will be called holding the WM and local lock, don't do anything costly or invoke AM/WM
* methods here directly.
*/
- boolean isActivityStartAllowed(Collection<IBinder> tokens, int uid, String packageName);
+ BackgroundActivityStartCallbackResult isActivityStartAllowed(Collection<IBinder> tokens,
+ int uid, String packageName);
/**
* Returns whether {@code uid} can send {@link android.content.Intent
diff --git a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
index f50a68cc5389..f8a50b3fda04 100644
--- a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
+++ b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
@@ -51,6 +51,7 @@ import static com.android.window.flags.Flags.balRequireOptInByPendingIntentCreat
import static com.android.window.flags.Flags.balShowToastsBlocked;
import static com.android.window.flags.Flags.balStrictModeGracePeriod;
import static com.android.window.flags.Flags.balStrictModeRo;
+import static com.android.window.flags.Flags.balAdditionalLogging;
import static java.lang.annotation.RetentionPolicy.SOURCE;
import static java.util.Objects.requireNonNull;
@@ -1939,6 +1940,7 @@ public class BackgroundActivityStartController {
}
}
logIfOnlyAllowedBy(finalVerdict, state, BAL_ALLOW_NON_APP_VISIBLE_WINDOW);
+ logIfOnlyAllowedBy(finalVerdict, state, BAL_ALLOW_TOKEN);
if (balImprovedMetrics()) {
if (shouldLogStats(finalVerdict, state)) {
@@ -1998,8 +2000,10 @@ public class BackgroundActivityStartController {
return false;
} else {
// log to determine grace period length distribution
- Slog.wtf(TAG, "Activity start ONLY allowed by " + balCodeToString(balCode) + " "
- + finalVerdict.mMessage + ": " + state);
+ if (balAdditionalLogging()) {
+ Slog.wtf(TAG, "Activity start ONLY allowed by " + balCodeToString(balCode) + " "
+ + finalVerdict.mMessage + ": " + state);
+ }
return true;
}
}
diff --git a/services/core/java/com/android/server/wm/BackgroundLaunchProcessController.java b/services/core/java/com/android/server/wm/BackgroundLaunchProcessController.java
index 31b239421baf..2605310db6f4 100644
--- a/services/core/java/com/android/server/wm/BackgroundLaunchProcessController.java
+++ b/services/core/java/com/android/server/wm/BackgroundLaunchProcessController.java
@@ -128,11 +128,16 @@ class BackgroundLaunchProcessController {
return new BalVerdict(BAL_ALLOW_PERMISSION, /*background*/
"process instrumenting with background activity starts privileges");
}
- // Allow if the flag was explicitly set.
- if (checkConfiguration.checkOtherExemptions && isBackgroundStartAllowedByToken(uid,
- packageName, checkConfiguration.isCheckingForFgsStart)) {
- return new BalVerdict(balImprovedMetrics() ? BAL_ALLOW_TOKEN : BAL_ALLOW_PERMISSION,
- /*background*/ "process allowed by token");
+ // Allow if the token is explicitly allowed.
+ if (checkConfiguration.checkOtherExemptions) {
+ BalVerdict tokenVerdict = isBackgroundStartAllowedByToken(uid,
+ packageName, checkConfiguration.isCheckingForFgsStart);
+ if (tokenVerdict.allows()) {
+ if (!balImprovedMetrics()) {
+ return new BalVerdict(BAL_ALLOW_PERMISSION, tokenVerdict.toString());
+ }
+ return tokenVerdict;
+ }
}
// Allow if the caller is bound by a UID that's currently foreground.
// But still respect the appSwitchState.
@@ -174,42 +179,53 @@ class BackgroundLaunchProcessController {
* isCheckingForFgsStart is false, we ask the callback if the start is allowed for these tokens,
* otherwise if there is no callback we allow.
*/
- private boolean isBackgroundStartAllowedByToken(int uid, String packageName,
+ private BalVerdict isBackgroundStartAllowedByToken(int uid, String packageName,
boolean isCheckingForFgsStart) {
synchronized (this) {
if (mBackgroundStartPrivileges == null
|| mBackgroundStartPrivileges.isEmpty()) {
// no tokens to allow anything
- return false;
+ return BalVerdict.BLOCK;
}
if (isCheckingForFgsStart) {
// check if any token allows foreground service starts
for (int i = mBackgroundStartPrivileges.size(); i-- > 0; ) {
if (mBackgroundStartPrivileges.valueAt(i).allowsBackgroundFgsStarts()) {
- return true;
+ return new BalVerdict(BAL_ALLOW_TOKEN, "process allowed by token");
}
}
- return false;
+ return BalVerdict.BLOCK;
}
if (mBackgroundActivityStartCallback == null) {
// without a callback just check if any token allows background activity starts
for (int i = mBackgroundStartPrivileges.size(); i-- > 0; ) {
if (mBackgroundStartPrivileges.valueAt(i)
.allowsBackgroundActivityStarts()) {
- return true;
+ return new BalVerdict(BAL_ALLOW_TOKEN, "process allowed by token");
}
}
- return false;
+ return BalVerdict.BLOCK;
}
List<IBinder> binderTokens = getOriginatingTokensThatAllowBal();
if (binderTokens.isEmpty()) {
// no tokens to allow anything
- return false;
+ return BalVerdict.BLOCK;
}
// The callback will decide.
- return mBackgroundActivityStartCallback.isActivityStartAllowed(
+ BackgroundActivityStartCallback.BackgroundActivityStartCallbackResult
+ activityStartAllowed = mBackgroundActivityStartCallback.isActivityStartAllowed(
binderTokens, uid, packageName);
+ if (!activityStartAllowed.allowed()) {
+ return BalVerdict.BLOCK;
+ }
+ if (activityStartAllowed.token() == null) {
+ return new BalVerdict(BAL_ALLOW_TOKEN,
+ "process allowed by callback (token ignored) tokens: " + binderTokens);
+ }
+ return new BalVerdict(BAL_ALLOW_TOKEN,
+ "process allowed by callback (token: " + activityStartAllowed.token()
+ + ") tokens: " + binderTokens);
}
}
diff --git a/services/core/java/com/android/server/wm/DesktopAppCompatAspectRatioPolicy.java b/services/core/java/com/android/server/wm/DesktopAppCompatAspectRatioPolicy.java
index fee5566af484..d8087265c1d3 100644
--- a/services/core/java/com/android/server/wm/DesktopAppCompatAspectRatioPolicy.java
+++ b/services/core/java/com/android/server/wm/DesktopAppCompatAspectRatioPolicy.java
@@ -69,11 +69,11 @@ public class DesktopAppCompatAspectRatioPolicy {
* Calculates the final aspect ratio of an launching activity based on the task it will be
* launched in. Takes into account any min or max aspect ratio constraints.
*/
- float calculateAspectRatio(@NonNull Task task) {
+ float calculateAspectRatio(@NonNull Task task, boolean hasOrientationMismatch) {
final float maxAspectRatio = getMaxAspectRatio();
final float minAspectRatio = getMinAspectRatio(task);
float desiredAspectRatio = 0;
- desiredAspectRatio = getDesiredAspectRatio(task);
+ desiredAspectRatio = getDesiredAspectRatio(task, hasOrientationMismatch);
if (maxAspectRatio >= 1 && desiredAspectRatio > maxAspectRatio) {
desiredAspectRatio = maxAspectRatio;
} else if (minAspectRatio >= 1 && desiredAspectRatio < minAspectRatio) {
@@ -87,13 +87,14 @@ public class DesktopAppCompatAspectRatioPolicy {
* any min or max aspect ratio constraints.
*/
@VisibleForTesting
- float getDesiredAspectRatio(@NonNull Task task) {
+ float getDesiredAspectRatio(@NonNull Task task, boolean hasOrientationMismatch) {
final float letterboxAspectRatioOverride = getFixedOrientationLetterboxAspectRatio(task);
// Aspect ratio as suggested by the system. Apps requested mix/max aspect ratio will
// be respected in #calculateAspectRatio.
if (isDefaultMultiWindowLetterboxAspectRatioDesired(task)) {
return DEFAULT_LETTERBOX_ASPECT_RATIO_FOR_MULTI_WINDOW;
- } else if (letterboxAspectRatioOverride > MIN_FIXED_ORIENTATION_LETTERBOX_ASPECT_RATIO) {
+ } else if (hasOrientationMismatch
+ && letterboxAspectRatioOverride > MIN_FIXED_ORIENTATION_LETTERBOX_ASPECT_RATIO) {
return letterboxAspectRatioOverride;
}
return AppCompatUtils.computeAspectRatio(task.getDisplayArea().getBounds());
diff --git a/services/core/java/com/android/server/wm/DesktopModeBoundsCalculator.java b/services/core/java/com/android/server/wm/DesktopModeBoundsCalculator.java
index d9354323ae7c..83ca5f6f83f4 100644
--- a/services/core/java/com/android/server/wm/DesktopModeBoundsCalculator.java
+++ b/services/core/java/com/android/server/wm/DesktopModeBoundsCalculator.java
@@ -17,7 +17,6 @@
package com.android.server.wm;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LOCKED;
-import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
import static android.content.pm.ActivityInfo.isFixedOrientation;
import static android.content.pm.ActivityInfo.isFixedOrientationLandscape;
import static android.content.pm.ActivityInfo.isFixedOrientationPortrait;
@@ -32,8 +31,8 @@ import static com.android.server.wm.LaunchParamsUtil.calculateLayoutBounds;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityOptions;
-import android.content.pm.ActivityInfo.ScreenOrientation;
import android.content.pm.ActivityInfo.WindowLayout;
+import android.content.res.Configuration;
import android.graphics.Rect;
import android.os.SystemProperties;
import android.util.Size;
@@ -152,19 +151,25 @@ public final class DesktopModeBoundsCalculator {
}
final DesktopAppCompatAspectRatioPolicy desktopAppCompatAspectRatioPolicy =
activity.mAppCompatController.getDesktopAspectRatioPolicy();
- float appAspectRatio = desktopAppCompatAspectRatioPolicy.calculateAspectRatio(task);
+ final int stableBoundsOrientation = stableBounds.height() >= stableBounds.width()
+ ? ORIENTATION_PORTRAIT : ORIENTATION_LANDSCAPE;
+ int activityOrientation = getActivityConfigurationOrientation(
+ activity, task, stableBoundsOrientation);
+ // Use orientation mismatch to resolve aspect ratio to match fixed orientation letterboxing
+ // policy in {@link ActivityRecord.resolveFixedOrientationConfiguration}
+ final boolean hasOrientationMismatch = stableBoundsOrientation != activityOrientation;
+ float appAspectRatio = desktopAppCompatAspectRatioPolicy.calculateAspectRatio(
+ task, hasOrientationMismatch);
final float tdaWidth = stableBounds.width();
final float tdaHeight = stableBounds.height();
- final int taskConfigOrientation = task.getConfiguration().orientation;
- final int activityOrientation = getActivityOrientation(activity, task);
- final Size initialSize = switch (taskConfigOrientation) {
+ final Size initialSize = switch (stableBoundsOrientation) {
case ORIENTATION_LANDSCAPE -> {
// Device in landscape orientation.
if (appAspectRatio == 0) {
appAspectRatio = 1;
}
if (canChangeAspectRatio(desktopAppCompatAspectRatioPolicy, task)) {
- if (isFixedOrientationPortrait(activityOrientation)) {
+ if (hasOrientationMismatch) {
// For portrait resizeable activities, respect apps fullscreen width but
// apply ideal size height.
yield new Size((int) ((tdaHeight / appAspectRatio) + 0.5f),
@@ -183,7 +188,7 @@ public final class DesktopModeBoundsCalculator {
final int customPortraitWidthForLandscapeApp = screenBounds.width()
- (DESKTOP_MODE_LANDSCAPE_APP_PADDING * 2);
if (canChangeAspectRatio(desktopAppCompatAspectRatioPolicy, task)) {
- if (isFixedOrientationLandscape(activityOrientation)) {
+ if (hasOrientationMismatch) {
if (appAspectRatio == 0) {
appAspectRatio = tdaWidth / (tdaWidth - 1);
}
@@ -198,7 +203,7 @@ public final class DesktopModeBoundsCalculator {
if (appAspectRatio == 0) {
appAspectRatio = 1;
}
- if (isFixedOrientationLandscape(activityOrientation)) {
+ if (hasOrientationMismatch) {
// For landscape unresizeable activities, apply custom app width to ideal size
// and calculate maximum size with this area while maintaining original aspect
// ratio.
@@ -230,19 +235,23 @@ public final class DesktopModeBoundsCalculator {
&& !desktopAppCompatAspectRatioPolicy.hasMinAspectRatioOverride(task);
}
- private static @ScreenOrientation int getActivityOrientation(
- @NonNull ActivityRecord activity, @NonNull Task task) {
+ private static @Configuration.Orientation int getActivityConfigurationOrientation(
+ @NonNull ActivityRecord activity, @NonNull Task task,
+ @Configuration.Orientation int stableBoundsOrientation) {
final int activityOrientation = activity.getOverrideOrientation();
final DesktopAppCompatAspectRatioPolicy desktopAppCompatAspectRatioPolicy =
activity.mAppCompatController.getDesktopAspectRatioPolicy();
- if (desktopAppCompatAspectRatioPolicy.shouldApplyUserMinAspectRatioOverride(task)
+ if ((desktopAppCompatAspectRatioPolicy.shouldApplyUserMinAspectRatioOverride(task)
&& (!isFixedOrientation(activityOrientation)
- || activityOrientation == SCREEN_ORIENTATION_LOCKED)) {
+ || activityOrientation == SCREEN_ORIENTATION_LOCKED))
+ || isFixedOrientationPortrait(activityOrientation)) {
// If a user aspect ratio override should be applied, treat the activity as portrait if
// it has not specified a fix orientation.
- return SCREEN_ORIENTATION_PORTRAIT;
+ return ORIENTATION_PORTRAIT;
}
- return activityOrientation;
+ // If activity orientation is undefined inherit task orientation.
+ return isFixedOrientationLandscape(activityOrientation)
+ ? ORIENTATION_LANDSCAPE : stableBoundsOrientation;
}
/**
@@ -252,7 +261,7 @@ public final class DesktopModeBoundsCalculator {
// TODO(b/400617906): Merge duplicate initial bounds calculations to shared class.
@NonNull
private static Size maximizeSizeGivenAspectRatio(
- @ScreenOrientation int orientation,
+ @Configuration.Orientation int orientation,
@NonNull Size targetArea,
float aspectRatio,
int captionHeight
@@ -261,7 +270,7 @@ public final class DesktopModeBoundsCalculator {
final int targetWidth = targetArea.getWidth();
final int finalHeight;
final int finalWidth;
- if (isFixedOrientationPortrait(orientation)) {
+ if (orientation == ORIENTATION_PORTRAIT) {
// Portrait activity.
// Calculate required width given ideal height and aspect ratio.
int tempWidth = (int) (targetHeight / aspectRatio);
diff --git a/services/core/java/com/android/server/wm/DeviceStateAutoRotateSettingIssueLogger.java b/services/core/java/com/android/server/wm/DeviceStateAutoRotateSettingIssueLogger.java
index 937039d838e5..a7d03485058f 100644
--- a/services/core/java/com/android/server/wm/DeviceStateAutoRotateSettingIssueLogger.java
+++ b/services/core/java/com/android/server/wm/DeviceStateAutoRotateSettingIssueLogger.java
@@ -29,13 +29,13 @@ import java.util.function.LongSupplier;
/**
* Logs potential race conditions that lead to incorrect auto-rotate setting.
*
- * Before go/auto-rotate-refactor, there is a race condition that happen during device state
+ * <p>Before go/auto-rotate-refactor, there is a race condition that happen during device state
* changes, as a result, incorrect auto-rotate setting are written for a device state in
* DEVICE_STATE_ROTATION_LOCK. Realistically, users shouldn’t be able to change
* DEVICE_STATE_ROTATION_LOCK while the device folds/unfolds.
*
- * This class monitors the time between a device state change and a subsequent change to the device
- * state based auto-rotate setting. If the duration is less than a threshold
+ * <p>This class monitors the time between a device state change and a subsequent change to the
+ * device state based auto-rotate setting. If the duration is less than a threshold
* (DEVICE_STATE_AUTO_ROTATE_SETTING_ISSUE_THRESHOLD), a potential issue is logged. The logging of
* the atom is not expected to occur often, realistically estimated once a month on few devices.
* But the number could be bigger, as that's what this metric is set to reveal.
@@ -72,23 +72,33 @@ public class DeviceStateAutoRotateSettingIssueLogger {
}
private void onStateChange() {
- // Only move forward if both of the events have occurred already
- if (mLastDeviceStateChangeTime != TIME_NOT_SET
- && mLastDeviceStateAutoRotateSettingChangeTime != TIME_NOT_SET) {
- final long duration =
- mLastDeviceStateAutoRotateSettingChangeTime - mLastDeviceStateChangeTime;
- boolean isDeviceStateChangeFirst = duration > 0;
+ // Only move forward if both of the events have occurred already.
+ if (mLastDeviceStateChangeTime == TIME_NOT_SET
+ || mLastDeviceStateAutoRotateSettingChangeTime == TIME_NOT_SET) {
+ return;
+ }
+ final long duration =
+ mLastDeviceStateAutoRotateSettingChangeTime - mLastDeviceStateChangeTime;
+ boolean isDeviceStateChangeFirst = duration > 0;
- if (abs(duration)
- < DEVICE_STATE_AUTO_ROTATE_SETTING_ISSUE_THRESHOLD_MILLIS) {
- FrameworkStatsLog.write(
- FrameworkStatsLog.DEVICE_STATE_AUTO_ROTATE_SETTING_ISSUE_REPORTED,
- (int) abs(duration),
- isDeviceStateChangeFirst);
- }
+ if (abs(duration)
+ < DEVICE_STATE_AUTO_ROTATE_SETTING_ISSUE_THRESHOLD_MILLIS) {
+ FrameworkStatsLog.write(
+ FrameworkStatsLog.DEVICE_STATE_AUTO_ROTATE_SETTING_ISSUE_REPORTED,
+ (int) abs(duration),
+ isDeviceStateChangeFirst);
+ // This pair is logged, reset both timestamps.
mLastDeviceStateAutoRotateSettingChangeTime = TIME_NOT_SET;
mLastDeviceStateChangeTime = TIME_NOT_SET;
+ } else {
+ // This pair was not logged, reset the earlier timestamp.
+ if (isDeviceStateChangeFirst) {
+ mLastDeviceStateChangeTime = TIME_NOT_SET;
+ } else {
+ mLastDeviceStateAutoRotateSettingChangeTime = TIME_NOT_SET;
+ }
}
+
}
}
diff --git a/services/core/java/com/android/server/wm/Dimmer.java b/services/core/java/com/android/server/wm/Dimmer.java
index 2798e843d6dd..ab87459da01a 100644
--- a/services/core/java/com/android/server/wm/Dimmer.java
+++ b/services/core/java/com/android/server/wm/Dimmer.java
@@ -218,6 +218,11 @@ class Dimmer {
*/
protected void adjustAppearance(@NonNull WindowState dimmingContainer,
float alpha, int blurRadius) {
+ if (!mHost.isVisibleRequested()) {
+ // If the host is already going away, there is no point in keeping dimming
+ return;
+ }
+
if (mDimState != null || (alpha != 0 || blurRadius != 0)) {
final DimState d = obtainDimState(dimmingContainer);
d.prepareLookChange(alpha, blurRadius);
diff --git a/services/core/java/com/android/server/wm/DisplayArea.java b/services/core/java/com/android/server/wm/DisplayArea.java
index 6718ae435cd9..d7d5b44ed210 100644
--- a/services/core/java/com/android/server/wm/DisplayArea.java
+++ b/services/core/java/com/android/server/wm/DisplayArea.java
@@ -332,12 +332,6 @@ public class DisplayArea<T extends WindowContainer> extends WindowContainer<T> {
}
@Override
- boolean needsZBoost() {
- // Z Boost should only happen at or below the ActivityStack level.
- return false;
- }
-
- @Override
boolean fillsParent() {
return true;
}
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 313b77ed4b20..f9eb0574d87c 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -137,6 +137,7 @@ import com.android.internal.view.AppearanceRegion;
import com.android.internal.widget.PointerLocationView;
import com.android.server.LocalServices;
import com.android.server.UiThread;
+import com.android.server.notification.NotificationManagerInternal;
import com.android.server.policy.WindowManagerPolicy.ScreenOnListener;
import com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs;
import com.android.server.statusbar.StatusBarManagerInternal;
@@ -1925,6 +1926,11 @@ public class DisplayPolicy {
if (wpMgr != null) {
wpMgr.onDisplayRemoveSystemDecorations(displayId);
}
+ final NotificationManagerInternal notificationManager =
+ LocalServices.getService(NotificationManagerInternal.class);
+ if (notificationManager != null) {
+ notificationManager.onDisplayRemoveSystemDecorations(displayId);
+ }
});
}
diff --git a/services/core/java/com/android/server/wm/OWNERS b/services/core/java/com/android/server/wm/OWNERS
index 243a5326b545..0989fc05e0bb 100644
--- a/services/core/java/com/android/server/wm/OWNERS
+++ b/services/core/java/com/android/server/wm/OWNERS
@@ -26,7 +26,7 @@ mcarli@google.com
per-file Background*Start* = set noparent
per-file Background*Start* = file:/BAL_OWNERS
per-file Background*Start* = ogunwale@google.com, louischang@google.com
-per-file BackgroundLaunchProcessController.java = file:/BAL_OWNERS
+per-file BackgroundLaunchProcessController*.java = file:/BAL_OWNERS
# File related to activity callers
per-file ActivityCallerState.java = file:/core/java/android/app/COMPONENT_CALLER_OWNERS
diff --git a/services/core/java/com/android/server/wm/PointerEventDispatcher.java b/services/core/java/com/android/server/wm/PointerEventDispatcher.java
index 4f8ec631d9e2..be259ec3f7c0 100644
--- a/services/core/java/com/android/server/wm/PointerEventDispatcher.java
+++ b/services/core/java/com/android/server/wm/PointerEventDispatcher.java
@@ -80,7 +80,7 @@ public class PointerEventDispatcher extends InputEventReceiver {
public void unregisterInputEventListener(PointerEventListener listener) {
synchronized (mListeners) {
if (!mListeners.contains(listener)) {
- throw new IllegalStateException("registerInputEventListener: " + listener +
+ throw new IllegalStateException("unregisterInputEventListener: " + listener +
" not registered.");
}
mListeners.remove(listener);
diff --git a/services/core/java/com/android/server/wm/SnapshotCache.java b/services/core/java/com/android/server/wm/SnapshotCache.java
index 9812a88bfa5a..685bd3b355ed 100644
--- a/services/core/java/com/android/server/wm/SnapshotCache.java
+++ b/services/core/java/com/android/server/wm/SnapshotCache.java
@@ -16,12 +16,14 @@
package com.android.server.wm;
import android.annotation.Nullable;
+import android.hardware.HardwareBuffer;
import android.util.ArrayMap;
import android.window.TaskSnapshot;
import com.android.internal.annotations.GuardedBy;
import java.io.PrintWriter;
+import java.util.function.Consumer;
/**
* Base class for an app snapshot cache
@@ -38,12 +40,18 @@ abstract class SnapshotCache<TYPE extends WindowContainer> {
@GuardedBy("mLock")
protected final ArrayMap<Integer, CacheEntry> mRunningCache = new ArrayMap<>();
+ protected Consumer<HardwareBuffer> mSafeSnapshotReleaser;
+
SnapshotCache(String name) {
mName = name;
}
abstract void putSnapshot(TYPE window, TaskSnapshot snapshot);
+ void setSafeSnapshotReleaser(Consumer<HardwareBuffer> safeSnapshotReleaser) {
+ mSafeSnapshotReleaser = safeSnapshotReleaser;
+ }
+
void clearRunningCache() {
synchronized (mLock) {
mRunningCache.clear();
diff --git a/services/core/java/com/android/server/wm/SnapshotController.java b/services/core/java/com/android/server/wm/SnapshotController.java
index 3a7222ae6d51..48f0959214c1 100644
--- a/services/core/java/com/android/server/wm/SnapshotController.java
+++ b/services/core/java/com/android/server/wm/SnapshotController.java
@@ -26,13 +26,16 @@ import static android.view.WindowManager.TRANSIT_PREPARE_BACK_NAVIGATION;
import static android.view.WindowManager.TRANSIT_TO_BACK;
import static android.view.WindowManager.TRANSIT_TO_FRONT;
+import android.hardware.HardwareBuffer;
import android.os.Trace;
import android.util.ArrayMap;
import android.view.WindowManager;
import android.window.TaskSnapshot;
import java.io.PrintWriter;
+import java.lang.ref.WeakReference;
import java.util.ArrayList;
+import java.util.function.Consumer;
/**
* Integrates common functionality from TaskSnapshotController and ActivitySnapshotController.
@@ -41,11 +44,30 @@ class SnapshotController {
private final SnapshotPersistQueue mSnapshotPersistQueue;
final TaskSnapshotController mTaskSnapshotController;
final ActivitySnapshotController mActivitySnapshotController;
+ private final WindowManagerService mService;
+ private final ArrayList<WeakReference<HardwareBuffer>> mObsoleteSnapshots = new ArrayList<>();
SnapshotController(WindowManagerService wms) {
+ mService = wms;
mSnapshotPersistQueue = new SnapshotPersistQueue();
mTaskSnapshotController = new TaskSnapshotController(wms, mSnapshotPersistQueue);
mActivitySnapshotController = new ActivitySnapshotController(wms, mSnapshotPersistQueue);
+ final Consumer<HardwareBuffer> releaser = hb -> {
+ mService.mH.post(() -> {
+ synchronized (mService.mGlobalLock) {
+ if (hb.isClosed()) {
+ return;
+ }
+ if (mService.mAtmService.getTransitionController().inTransition()) {
+ mObsoleteSnapshots.add(new WeakReference<>(hb));
+ } else {
+ hb.close();
+ }
+ }
+ });
+ };
+ mTaskSnapshotController.setSnapshotReleaser(releaser);
+ mActivitySnapshotController.setSnapshotReleaser(releaser);
}
void systemReady() {
@@ -168,6 +190,7 @@ class SnapshotController {
final boolean isTransitionClose = isTransitionClose(type);
if (!isTransitionOpen && !isTransitionClose && type < TRANSIT_FIRST_CUSTOM
|| (changeInfos.isEmpty())) {
+ closeObsoleteSnapshots();
return;
}
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "SnapshotController_analysis");
@@ -195,9 +218,22 @@ class SnapshotController {
}
}
}
+ closeObsoleteSnapshots();
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
+ private void closeObsoleteSnapshots() {
+ if (mObsoleteSnapshots.isEmpty()) {
+ return;
+ }
+ for (int i = mObsoleteSnapshots.size() - 1; i >= 0; --i) {
+ final HardwareBuffer hb = mObsoleteSnapshots.remove(i).get();
+ if (hb != null && !hb.isClosed()) {
+ hb.close();
+ }
+ }
+ }
+
private static boolean isTransitionOpen(int type) {
return type == TRANSIT_OPEN || type == TRANSIT_TO_FRONT
|| type == TRANSIT_PREPARE_BACK_NAVIGATION;
diff --git a/services/core/java/com/android/server/wm/SnapshotPersistQueue.java b/services/core/java/com/android/server/wm/SnapshotPersistQueue.java
index eafc8be7bf77..016cebac2b86 100644
--- a/services/core/java/com/android/server/wm/SnapshotPersistQueue.java
+++ b/services/core/java/com/android/server/wm/SnapshotPersistQueue.java
@@ -24,6 +24,10 @@ import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import android.annotation.NonNull;
import android.graphics.Bitmap;
+import android.graphics.PixelFormat;
+import android.hardware.HardwareBuffer;
+import android.media.Image;
+import android.media.ImageReader;
import android.os.Process;
import android.os.SystemClock;
import android.os.Trace;
@@ -33,10 +37,12 @@ import android.window.TaskSnapshot;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.policy.TransitionAnimation;
import com.android.server.LocalServices;
import com.android.server.pm.UserManagerInternal;
import com.android.server.wm.BaseAppSnapshotPersister.PersistInfoProvider;
import com.android.server.wm.nano.WindowManagerProtos.TaskSnapshotProto;
+import com.android.window.flags.Flags;
import java.io.File;
import java.io.FileOutputStream;
@@ -400,23 +406,20 @@ class SnapshotPersistQueue {
Slog.e(TAG, "Invalid task snapshot hw buffer, taskId=" + mId);
return false;
}
- final Bitmap bitmap = Bitmap.wrapHardwareBuffer(
- mSnapshot.getHardwareBuffer(), mSnapshot.getColorSpace());
- if (bitmap == null) {
- Slog.e(TAG, "Invalid task snapshot hw bitmap");
- return false;
- }
- final Bitmap swBitmap = bitmap.copy(Bitmap.Config.ARGB_8888, false /* isMutable */);
+ final HardwareBuffer hwBuffer = mSnapshot.getHardwareBuffer();
+ final int width = hwBuffer.getWidth();
+ final int height = hwBuffer.getHeight();
+ final int pixelFormat = hwBuffer.getFormat();
+ final Bitmap swBitmap = !Flags.reduceTaskSnapshotMemoryUsage()
+ || (pixelFormat != PixelFormat.RGB_565 && pixelFormat != PixelFormat.RGBA_8888)
+ || !mSnapshot.isRealSnapshot()
+ || TransitionAnimation.hasProtectedContent(hwBuffer)
+ ? copyToSwBitmapReadBack()
+ : copyToSwBitmapDirect(width, height, pixelFormat);
if (swBitmap == null) {
- Slog.e(TAG, "Bitmap conversion from (config=" + bitmap.getConfig() + ", isMutable="
- + bitmap.isMutable() + ") to (config=ARGB_8888, isMutable=false) failed.");
return false;
}
- final int width = bitmap.getWidth();
- final int height = bitmap.getHeight();
- bitmap.recycle();
-
final File file = mPersistInfoProvider.getHighResolutionBitmapFile(mId, mUserId);
try (FileOutputStream fos = new FileOutputStream(file)) {
swBitmap.compress(JPEG, COMPRESS_QUALITY, fos);
@@ -448,6 +451,58 @@ class SnapshotPersistQueue {
return true;
}
+ private Bitmap copyToSwBitmapReadBack() {
+ final Bitmap bitmap = Bitmap.wrapHardwareBuffer(
+ mSnapshot.getHardwareBuffer(), mSnapshot.getColorSpace());
+ if (bitmap == null) {
+ Slog.e(TAG, "Invalid task snapshot hw bitmap");
+ return null;
+ }
+
+ final Bitmap swBitmap = bitmap.copy(Bitmap.Config.ARGB_8888, false /* isMutable */);
+ if (swBitmap == null) {
+ Slog.e(TAG, "Bitmap conversion from (config=" + bitmap.getConfig()
+ + ", isMutable=" + bitmap.isMutable()
+ + ") to (config=ARGB_8888, isMutable=false) failed.");
+ return null;
+ }
+ bitmap.recycle();
+ return swBitmap;
+ }
+
+ /**
+ * Use ImageReader to create the software bitmap, so SkImage won't create an extra texture.
+ */
+ private Bitmap copyToSwBitmapDirect(int width, int height, int pixelFormat) {
+ try (ImageReader ir = ImageReader.newInstance(width, height,
+ pixelFormat, 1 /* maxImages */)) {
+ ir.getSurface().attachAndQueueBufferWithColorSpace(mSnapshot.getHardwareBuffer(),
+ mSnapshot.getColorSpace());
+ try (Image image = ir.acquireLatestImage()) {
+ if (image == null || image.getPlaneCount() < 1) {
+ Slog.e(TAG, "Image reader cannot acquire image");
+ return null;
+ }
+
+ final Image.Plane[] planes = image.getPlanes();
+ if (planes.length != 1) {
+ Slog.e(TAG, "Image reader cannot get plane");
+ return null;
+ }
+ final Image.Plane plane = planes[0];
+ final int rowPadding = plane.getRowStride() - plane.getPixelStride()
+ * image.getWidth();
+ final Bitmap swBitmap = Bitmap.createBitmap(
+ image.getWidth() + rowPadding / plane.getPixelStride() /* width */,
+ image.getHeight() /* height */,
+ pixelFormat == PixelFormat.RGB_565
+ ? Bitmap.Config.RGB_565 : Bitmap.Config.ARGB_8888);
+ swBitmap.copyPixelsFromBuffer(plane.getBuffer());
+ return swBitmap;
+ }
+ }
+ }
+
@Override
public boolean equals(Object o) {
if (o == null || getClass() != o.getClass()) return false;
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 8587b5a9c7ca..ec17d131958b 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -35,8 +35,6 @@ import static android.content.Intent.FLAG_ACTIVITY_RETAIN_IN_RECENTS;
import static android.content.Intent.FLAG_ACTIVITY_TASK_ON_HOME;
import static android.content.pm.ActivityInfo.FLAG_RELINQUISH_TASK_IDENTITY;
import static android.content.pm.ActivityInfo.FLAG_SHOW_FOR_ALL_USERS;
-import static android.content.pm.ActivityInfo.FORCE_NON_RESIZE_APP;
-import static android.content.pm.ActivityInfo.FORCE_RESIZE_APP;
import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZABLE_LANDSCAPE_ONLY;
import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZABLE_PORTRAIT_ONLY;
import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZABLE_PRESERVE_ORIENTATION;
@@ -51,7 +49,6 @@ import static android.view.Display.INVALID_DISPLAY;
import static android.view.SurfaceControl.METADATA_TASK_ID;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
-import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_RESIZEABLE_ACTIVITY_OVERRIDES;
import static android.view.WindowManager.TRANSIT_CLOSE;
import static android.view.WindowManager.TRANSIT_FLAG_APP_CRASHED;
import static android.view.WindowManager.TRANSIT_OPEN;
@@ -132,7 +129,6 @@ import android.app.IActivityController;
import android.app.PictureInPictureParams;
import android.app.TaskInfo;
import android.app.WindowConfiguration;
-import android.app.compat.CompatChanges;
import android.content.ComponentName;
import android.content.Intent;
import android.content.pm.ActivityInfo;
@@ -514,10 +510,16 @@ class Task extends TaskFragment {
boolean mIsPerceptible = false;
/**
- * Whether the compatibility overrides that change the resizability of the app should be allowed
- * for the specific app.
+ * Whether the task has been forced resizable, which is determined by the
+ * activity that started this task.
*/
- boolean mAllowForceResizeOverride = true;
+ private boolean mForceResizeOverride;
+
+ /**
+ * Whether the task has been forced non-resizable, which is determined by
+ * the activity that started this task.
+ */
+ private boolean mForceNonResizeOverride;
private static final int TRANSLUCENT_TIMEOUT_MSG = FIRST_ACTIVITY_TASK_MSG + 1;
@@ -675,7 +677,6 @@ class Task extends TaskFragment {
intent = _intent;
mMinWidth = minWidth;
mMinHeight = minHeight;
- updateAllowForceResizeOverride();
}
mAtmService.getTaskChangeNotificationController().notifyTaskCreated(_taskId, realActivity);
mHandler = new ActivityTaskHandler(mTaskSupervisor.mLooper);
@@ -946,6 +947,7 @@ class Task extends TaskFragment {
mCallingPackage = r.launchedFromPackage;
mCallingFeatureId = r.launchedFromFeatureId;
setIntent(intent != null ? intent : r.intent, info != null ? info : r.info);
+ updateForceResizeOverrides(r);
}
setLockTaskAuth(r);
}
@@ -1038,7 +1040,6 @@ class Task extends TaskFragment {
mTaskSupervisor.mRecentTasks.remove(this);
mTaskSupervisor.mRecentTasks.add(this);
}
- updateAllowForceResizeOverride();
}
/** Sets the original minimal width and height. */
@@ -1832,6 +1833,17 @@ class Task extends TaskFragment {
&& supportsMultiWindowInDisplayArea(tda);
}
+ /** Returns true if the task bounds should persist across power cycles. */
+ private static boolean persistTaskBounds(@NonNull WindowConfiguration configuration) {
+ return configuration.getWindowingMode() == WINDOWING_MODE_FREEFORM;
+ }
+
+ /** Returns true if the nested task is allowed to have independent bounds from its parent. */
+ private static boolean allowIndependentBoundsFromParent(
+ @NonNull WindowConfiguration configuration) {
+ return configuration.getWindowingMode() == WINDOWING_MODE_FREEFORM;
+ }
+
/**
* Check whether this task can be launched on the specified display.
*
@@ -1844,15 +1856,14 @@ class Task extends TaskFragment {
-1 /* don't check PID */, -1 /* don't check UID */, this);
}
- private void updateAllowForceResizeOverride() {
- try {
- mAllowForceResizeOverride = mAtmService.mContext.getPackageManager().getPropertyAsUser(
- PROPERTY_COMPAT_ALLOW_RESIZEABLE_ACTIVITY_OVERRIDES,
- getBasePackageName(), null /* className */, mUserId).getBoolean();
- } catch (PackageManager.NameNotFoundException e) {
- // Package not found or property not defined, reset to default value.
- mAllowForceResizeOverride = true;
- }
+ private void updateForceResizeOverrides(@NonNull ActivityRecord r) {
+ final AppCompatResizeOverrides resizeOverrides = r.mAppCompatController
+ .getResizeOverrides();
+ mForceResizeOverride = resizeOverrides.shouldOverrideForceResizeApp()
+ || r.isUniversalResizeable()
+ || r.mAppCompatController.getAspectRatioOverrides()
+ .hasFullscreenOverride();
+ mForceNonResizeOverride = resizeOverrides.shouldOverrideForceNonResizeApp();
}
/**
@@ -1996,11 +2007,11 @@ class Task extends TaskFragment {
private void onConfigurationChangedInner(Configuration newParentConfig) {
// Check if the new configuration supports persistent bounds (eg. is Freeform) and if so
// restore the last recorded non-fullscreen bounds.
- final boolean prevPersistTaskBounds = getWindowConfiguration().persistTaskBounds();
- boolean nextPersistTaskBounds =
- getRequestedOverrideConfiguration().windowConfiguration.persistTaskBounds();
+ final boolean prevPersistTaskBounds = persistTaskBounds(getWindowConfiguration());
+ boolean nextPersistTaskBounds = persistTaskBounds(
+ getRequestedOverrideConfiguration().windowConfiguration);
if (getRequestedOverrideWindowingMode() == WINDOWING_MODE_UNDEFINED) {
- nextPersistTaskBounds = newParentConfig.windowConfiguration.persistTaskBounds();
+ nextPersistTaskBounds = persistTaskBounds(newParentConfig.windowConfiguration);
}
// Only restore to the last non-fullscreen bounds when the requested override bounds
// have not been explicitly set already.
@@ -2042,7 +2053,7 @@ class Task extends TaskFragment {
// If the configuration supports persistent bounds (eg. Freeform), keep track of the
// current (non-fullscreen) bounds for persistence.
- if (getWindowConfiguration().persistTaskBounds()) {
+ if (persistTaskBounds(getWindowConfiguration())) {
final Rect currentBounds = getRequestedOverrideBounds();
if (!currentBounds.isEmpty()) {
setLastNonFullscreenBounds(currentBounds);
@@ -2383,33 +2394,48 @@ class Task extends TaskFragment {
}
void updateOverrideConfigurationFromLaunchBounds() {
- // If the task is controlled by another organized task, do not set override
- // configurations and let its parent (organized task) to control it;
final Task rootTask = getRootTask();
- boolean shouldInheritBounds = rootTask != this && rootTask.isOrganized();
- if (DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue()) {
- // Only inherit from organized parent when this task is not organized.
- shouldInheritBounds &= !isOrganized();
+ final boolean hasParentTask = rootTask != this;
+ final int windowingMode = getWindowingMode();
+ final boolean isNonStandardOrFullscreen = !isActivityTypeStandardOrUndefined()
+ || windowingMode == WINDOWING_MODE_FULLSCREEN;
+ if (!Flags.nestedTasksWithIndependentBounds()
+ && !DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue()) {
+ final Rect bounds;
+ if (hasParentTask && rootTask.isOrganized()) {
+ bounds = null;
+ } else if (isNonStandardOrFullscreen) {
+ bounds = isResizeable() ? rootTask.getRequestedOverrideBounds() : null;
+ } else if (!persistTaskBounds(getWindowConfiguration())) {
+ bounds = rootTask.getRequestedOverrideBounds();
+ } else {
+ bounds = mLastNonFullscreenBounds;
+ }
+ setBounds(bounds);
+ return;
}
- final Rect bounds = shouldInheritBounds ? null : getLaunchBounds();
- setBounds(bounds);
- }
- /** Returns the bounds that should be used to launch this task. */
- Rect getLaunchBounds() {
- final Task rootTask = getRootTask();
- if (rootTask == null) {
- return null;
+ // Non-standard/fullscreen unresizable tasks should always inherit.
+ boolean shouldInheritBounds = isNonStandardOrFullscreen && !isResizeable();
+ // Task itself is not organized (e.g. Home), just inherit from its organized parent.
+ shouldInheritBounds |= hasParentTask && rootTask.isOrganized() && !isOrganized();
+ // Nested tasks should inherit when they're not allowed to have independent bounds, such as
+ // in multi-window split-screen.
+ shouldInheritBounds |= hasParentTask
+ && !(allowIndependentBoundsFromParent(getWindowConfiguration())
+ && persistTaskBounds(getWindowConfiguration()));
+ if (shouldInheritBounds) {
+ setBounds(null);
+ return;
}
-
- final int windowingMode = getWindowingMode();
- if (!isActivityTypeStandardOrUndefined()
- || windowingMode == WINDOWING_MODE_FULLSCREEN) {
- return isResizeable() ? rootTask.getRequestedOverrideBounds() : null;
- } else if (!getWindowConfiguration().persistTaskBounds()) {
- return rootTask.getRequestedOverrideBounds();
+ if (!hasParentTask && !persistTaskBounds(getWindowConfiguration())) {
+ // Non-nested, non-persistable tasks such as PIP or multi-window floating windows.
+ return;
}
- return mLastNonFullscreenBounds;
+ // Non-nested, persisted tasks (e.g. top-level freeform) or nested persisted tasks that
+ // allow independent bounds from parent (e.g. nested freeform) should use launch-params
+ // bounds set to |mLastNonFullscreenBounds|.
+ setBounds(mLastNonFullscreenBounds);
}
void setRootProcess(WindowProcessController proc) {
@@ -2856,17 +2882,8 @@ class Task extends TaskFragment {
final boolean forceResizable = mAtmService.mForceResizableActivities
&& getActivityType() == ACTIVITY_TYPE_STANDARD;
if (forceResizable) return true;
-
- final UserHandle userHandle = UserHandle.getUserHandleForUid(mUserId);
- final boolean forceResizableOverride = mAllowForceResizeOverride
- && CompatChanges.isChangeEnabled(
- FORCE_RESIZE_APP, getBasePackageName(), userHandle);
- final boolean forceNonResizableOverride = mAllowForceResizeOverride
- && CompatChanges.isChangeEnabled(
- FORCE_NON_RESIZE_APP, getBasePackageName(), userHandle);
-
- if (forceNonResizableOverride) return false;
- return forceResizableOverride || ActivityInfo.isResizeableMode(mResizeMode)
+ if (mForceNonResizeOverride) return false;
+ return mForceResizeOverride || ActivityInfo.isResizeableMode(mResizeMode)
|| (mSupportsPictureInPicture && checkPictureInPictureSupport);
}
@@ -3607,43 +3624,39 @@ class Task extends TaskFragment {
int layer = 0;
boolean decorSurfacePlaced = false;
- // We use two passes as a way to promote children which
- // need Z-boosting to the end of the list.
for (int j = 0; j < mChildren.size(); ++j) {
final WindowContainer wc = mChildren.get(j);
wc.assignChildLayers(t);
- if (!wc.needsZBoost()) {
- // Place the decor surface under any untrusted content.
- if (mDecorSurfaceContainer != null
- && !mDecorSurfaceContainer.mIsBoosted
- && !decorSurfacePlaced
- && shouldPlaceDecorSurfaceBelowContainer(wc)) {
- mDecorSurfaceContainer.assignLayer(t, layer++);
- decorSurfacePlaced = true;
- }
- wc.assignLayer(t, layer++);
-
- // Boost the adjacent TaskFragment for dimmer if needed.
- final TaskFragment taskFragment = wc.asTaskFragment();
- if (taskFragment != null && taskFragment.isEmbedded()
- && taskFragment.hasAdjacentTaskFragment()) {
- final int[] nextLayer = { layer };
- taskFragment.forOtherAdjacentTaskFragments(adjacentTf -> {
- if (adjacentTf.shouldBoostDimmer()) {
- adjacentTf.assignLayer(t, nextLayer[0]++);
- }
- });
- layer = nextLayer[0];
- }
+ // Place the decor surface under any untrusted content.
+ if (mDecorSurfaceContainer != null
+ && !mDecorSurfaceContainer.mIsBoosted
+ && !decorSurfacePlaced
+ && shouldPlaceDecorSurfaceBelowContainer(wc)) {
+ mDecorSurfaceContainer.assignLayer(t, layer++);
+ decorSurfacePlaced = true;
+ }
+ wc.assignLayer(t, layer++);
+
+ // Boost the adjacent TaskFragment for dimmer if needed.
+ final TaskFragment taskFragment = wc.asTaskFragment();
+ if (taskFragment != null && taskFragment.isEmbedded()
+ && taskFragment.hasAdjacentTaskFragment()) {
+ final int[] nextLayer = { layer };
+ taskFragment.forOtherAdjacentTaskFragments(adjacentTf -> {
+ if (adjacentTf.shouldBoostDimmer()) {
+ adjacentTf.assignLayer(t, nextLayer[0]++);
+ }
+ });
+ layer = nextLayer[0];
+ }
- // Place the decor surface just above the owner TaskFragment.
- if (mDecorSurfaceContainer != null
- && !mDecorSurfaceContainer.mIsBoosted
- && !decorSurfacePlaced
- && wc == mDecorSurfaceContainer.mOwnerTaskFragment) {
- mDecorSurfaceContainer.assignLayer(t, layer++);
- decorSurfacePlaced = true;
- }
+ // Place the decor surface just above the owner TaskFragment.
+ if (mDecorSurfaceContainer != null
+ && !mDecorSurfaceContainer.mIsBoosted
+ && !decorSurfacePlaced
+ && wc == mDecorSurfaceContainer.mOwnerTaskFragment) {
+ mDecorSurfaceContainer.assignLayer(t, layer++);
+ decorSurfacePlaced = true;
}
}
@@ -3653,12 +3666,6 @@ class Task extends TaskFragment {
mDecorSurfaceContainer.assignLayer(t, layer++);
}
- for (int j = 0; j < mChildren.size(); ++j) {
- final WindowContainer wc = mChildren.get(j);
- if (wc.needsZBoost()) {
- wc.assignLayer(t, layer++);
- }
- }
if (mOverlayHost != null) {
mOverlayHost.setLayer(t, layer++);
}
diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java
index fb7bab4b3e26..1de139696c07 100644
--- a/services/core/java/com/android/server/wm/TaskDisplayArea.java
+++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java
@@ -48,7 +48,6 @@ import android.content.pm.ActivityInfo.ScreenOrientation;
import android.content.res.Configuration;
import android.graphics.Color;
import android.os.UserHandle;
-import android.util.IntArray;
import android.util.Slog;
import android.view.SurfaceControl;
import android.view.WindowManager;
@@ -102,9 +101,6 @@ final class TaskDisplayArea extends DisplayArea<WindowContainer> {
private final ArrayList<WindowContainer> mTmpAlwaysOnTopChildren = new ArrayList<>();
private final ArrayList<WindowContainer> mTmpNormalChildren = new ArrayList<>();
private final ArrayList<WindowContainer> mTmpHomeChildren = new ArrayList<>();
- private final IntArray mTmpNeedsZBoostIndexes = new IntArray();
-
- private ArrayList<Task> mTmpTasks = new ArrayList<>();
private ActivityTaskManagerService mAtmService;
@@ -740,40 +736,14 @@ final class TaskDisplayArea extends DisplayArea<WindowContainer> {
*/
private int adjustRootTaskLayer(SurfaceControl.Transaction t,
ArrayList<WindowContainer> children, int startLayer) {
- mTmpNeedsZBoostIndexes.clear();
final int childCount = children.size();
- boolean hasAdjacentTask = false;
for (int i = 0; i < childCount; i++) {
final WindowContainer child = children.get(i);
- final TaskDisplayArea childTda = child.asTaskDisplayArea();
- final boolean childNeedsZBoost = childTda != null
- ? childTda.childrenNeedZBoost()
- : child.needsZBoost();
-
- if (childNeedsZBoost) {
- mTmpNeedsZBoostIndexes.add(i);
- continue;
- }
-
- child.assignLayer(t, startLayer++);
- }
-
- final int zBoostSize = mTmpNeedsZBoostIndexes.size();
- for (int i = 0; i < zBoostSize; i++) {
- final WindowContainer child = children.get(mTmpNeedsZBoostIndexes.get(i));
child.assignLayer(t, startLayer++);
}
return startLayer;
}
- private boolean childrenNeedZBoost() {
- final boolean[] needsZBoost = new boolean[1];
- forAllRootTasks(task -> {
- needsZBoost[0] |= task.needsZBoost();
- });
- return needsZBoost[0];
- }
-
void setBackgroundColor(@ColorInt int colorInt) {
setBackgroundColor(colorInt, false /* restore */);
}
diff --git a/services/core/java/com/android/server/wm/TaskFpsCallbackController.java b/services/core/java/com/android/server/wm/TaskFpsCallbackController.java
index 8c798759c890..665c5cffd9ff 100644
--- a/services/core/java/com/android/server/wm/TaskFpsCallbackController.java
+++ b/services/core/java/com/android/server/wm/TaskFpsCallbackController.java
@@ -16,7 +16,6 @@
package com.android.server.wm;
-import android.content.Context;
import android.os.IBinder;
import android.os.RemoteException;
import android.window.ITaskFpsCallback;
@@ -25,12 +24,10 @@ import java.util.HashMap;
final class TaskFpsCallbackController {
- private final Context mContext;
private final HashMap<IBinder, Long> mTaskFpsCallbacks;
private final HashMap<IBinder, IBinder.DeathRecipient> mDeathRecipients;
- TaskFpsCallbackController(Context context) {
- mContext = context;
+ TaskFpsCallbackController() {
mTaskFpsCallbacks = new HashMap<>();
mDeathRecipients = new HashMap<>();
}
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotCache.java b/services/core/java/com/android/server/wm/TaskSnapshotCache.java
index cc957bd9ee42..1988f2630b6e 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotCache.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotCache.java
@@ -36,6 +36,7 @@ class TaskSnapshotCache extends SnapshotCache<Task> {
void putSnapshot(Task task, TaskSnapshot snapshot) {
synchronized (mLock) {
snapshot.addReference(TaskSnapshot.REFERENCE_CACHE);
+ snapshot.setSafeRelease(mSafeSnapshotReleaser);
final CacheEntry entry = mRunningCache.get(task.mTaskId);
if (entry != null) {
mAppIdMap.remove(entry.topApp);
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index 30313fc63857..65001f436d3f 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -3191,7 +3191,8 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener {
// Fixed rotation only applies to opening or changing activity.
return;
}
- if (task.inMultiWindowMode() && taskTopRunning.inMultiWindowMode()) {
+ if (!ActivityTaskManagerService.isPip2ExperimentEnabled()
+ && task.inMultiWindowMode() && taskTopRunning.inMultiWindowMode()) {
// Display won't be rotated for multi window Task, so the fixed rotation won't be
// applied. This can happen when the windowing mode is changed before the previous
// fixed rotation is applied. Check both task and activity because the activity keeps
@@ -3400,7 +3401,6 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener {
* Applies the new configuration for the changed displays. Returns the activities that should
* check whether to deliver the new configuration to clients.
*/
- @Nullable
void applyDisplayChangeIfNeeded(@NonNull ArraySet<WindowContainer<?>> activitiesMayChange) {
for (int i = mParticipants.size() - 1; i >= 0; --i) {
final WindowContainer<?> wc = mParticipants.valueAt(i);
diff --git a/services/core/java/com/android/server/wm/TransitionController.java b/services/core/java/com/android/server/wm/TransitionController.java
index 9b3b4451a746..02b53b0106b8 100644
--- a/services/core/java/com/android/server/wm/TransitionController.java
+++ b/services/core/java/com/android/server/wm/TransitionController.java
@@ -512,9 +512,14 @@ class TransitionController {
return false;
}
+ /** Returns {@code true} if the display contains a collecting transition. */
+ boolean isCollectingTransitionOnDisplay(@NonNull DisplayContent dc) {
+ return mCollectingTransition != null && mCollectingTransition.isOnDisplay(dc);
+ }
+
/** Returns {@code true} if the display contains a running or pending transition. */
boolean isTransitionOnDisplay(@NonNull DisplayContent dc) {
- if (mCollectingTransition != null && mCollectingTransition.isOnDisplay(dc)) {
+ if (isCollectingTransitionOnDisplay(dc)) {
return true;
}
for (int i = mWaitingTransitions.size() - 1; i >= 0; --i) {
@@ -1284,13 +1289,14 @@ class TransitionController {
// ignore ourself obviously
if (mPlayingTransitions.get(i) == transition) continue;
if (getIsIndependent(mPlayingTransitions.get(i), transition)) continue;
- if (track >= 0) {
+ if (track < 0) {
+ track = mPlayingTransitions.get(i).mAnimationTrack;
+ } else if (track != mPlayingTransitions.get(i).mAnimationTrack) {
// At this point, transition overlaps with multiple tracks, so just wait for
// everything
sync = true;
break;
}
- track = mPlayingTransitions.get(i).mAnimationTrack;
}
if (sync) {
track = 0;
diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java
index 3f2b40c1d7c9..e50545d41655 100644
--- a/services/core/java/com/android/server/wm/WindowAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowAnimator.java
@@ -18,10 +18,7 @@ package com.android.server.wm;
import static com.android.internal.protolog.WmProtoLogGroups.WM_SHOW_TRANSACTIONS;
import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_ALL;
-import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_APP_TRANSITION;
-import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_SCREEN_ROTATION;
import static com.android.server.wm.WindowContainer.AnimationFlags.CHILDREN;
-import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WINDOW_TRACE;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
@@ -57,9 +54,6 @@ public class WindowAnimator {
/** Is any window animating? */
private boolean mLastRootAnimating;
- /** True if we are running any animations that require expensive composition. */
- private boolean mRunningExpensiveAnimations;
-
final Choreographer.FrameCallback mAnimationFrameCallback;
/** Time of current animation step. Reset on each iteration */
@@ -138,8 +132,6 @@ public class WindowAnimator {
scheduleAnimation();
final RootWindowContainer root = mService.mRoot;
- final boolean useShellTransition = root.mTransitionController.isShellTransitionsEnabled();
- final int animationFlags = useShellTransition ? CHILDREN : (TRANSITION | CHILDREN);
boolean rootAnimating = false;
mCurrentTime = frameTimeNs / TimeUtils.NANOS_PER_MS;
if (DEBUG_WINDOW_TRACE) {
@@ -164,17 +156,13 @@ public class WindowAnimator {
for (int i = 0; i < numDisplays; i++) {
final DisplayContent dc = root.getChildAt(i);
-
- if (!useShellTransition) {
- dc.checkAppWindowsReadyToShow();
- }
if (accessibilityController.hasCallbacks()) {
accessibilityController
.recomputeMagnifiedRegionAndDrawMagnifiedRegionBorderIfNeeded(
dc.mDisplayId);
}
- if (dc.isAnimating(animationFlags, ANIMATION_TYPE_ALL)) {
+ if (dc.isAnimating(CHILDREN, ANIMATION_TYPE_ALL)) {
rootAnimating = true;
if (!dc.mLastContainsRunningSurfaceAnimator) {
dc.mLastContainsRunningSurfaceAnimator = true;
@@ -211,11 +199,6 @@ public class WindowAnimator {
}
mLastRootAnimating = rootAnimating;
- // APP_TRANSITION, SCREEN_ROTATION, TYPE_RECENTS are handled by shell transition.
- if (!useShellTransition) {
- updateRunningExpensiveAnimationsLegacy();
- }
-
final ArrayList<Runnable> afterPrepareSurfacesRunnables = mAfterPrepareSurfacesRunnables;
if (!afterPrepareSurfacesRunnables.isEmpty()) {
mAfterPrepareSurfacesRunnables = new ArrayList<>();
@@ -244,21 +227,6 @@ public class WindowAnimator {
}
}
- private void updateRunningExpensiveAnimationsLegacy() {
- final boolean runningExpensiveAnimations =
- mService.mRoot.isAnimating(TRANSITION | CHILDREN /* flags */,
- ANIMATION_TYPE_APP_TRANSITION
- | ANIMATION_TYPE_SCREEN_ROTATION /* typesToCheck */);
- if (runningExpensiveAnimations && !mRunningExpensiveAnimations) {
- mService.mSnapshotController.setPause(true);
- mTransaction.setEarlyWakeupStart();
- } else if (!runningExpensiveAnimations && mRunningExpensiveAnimations) {
- mService.mSnapshotController.setPause(false);
- mTransaction.setEarlyWakeupEnd();
- }
- mRunningExpensiveAnimations = runningExpensiveAnimations;
- }
-
public void dumpLocked(PrintWriter pw, String prefix, boolean dumpAll) {
final String subPrefix = " " + prefix;
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 5cbba355a06f..5b4870b0c0c7 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -279,9 +279,6 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
*/
int mTransitFlags;
- /** Whether this container should be boosted at the top of all its siblings. */
- @VisibleForTesting boolean mNeedsZBoost;
-
/** Layer used to constrain the animation to a container's stack bounds. */
SurfaceControl mAnimationBoundsLayer;
@@ -1476,14 +1473,6 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
return stillDeferringRemoval;
}
- /** Checks if all windows in an app are all drawn and shows them if needed. */
- void checkAppWindowsReadyToShow() {
- for (int i = mChildren.size() - 1; i >= 0; --i) {
- final WindowContainer wc = mChildren.get(i);
- wc.checkAppWindowsReadyToShow();
- }
- }
-
/**
* Called when this container or one of its descendants changed its requested orientation, and
* wants this container to handle it or pass it to its parent.
@@ -2744,15 +2733,7 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
for (int j = 0; j < mChildren.size(); ++j) {
final WindowContainer wc = mChildren.get(j);
wc.assignChildLayers(t);
- if (!wc.needsZBoost()) {
- wc.assignLayer(t, layer++);
- }
- }
- for (int j = 0; j < mChildren.size(); ++j) {
- final WindowContainer wc = mChildren.get(j);
- if (wc.needsZBoost()) {
- wc.assignLayer(t, layer++);
- }
+ wc.assignLayer(t, layer++);
}
if (mOverlayHost != null) {
mOverlayHost.setLayer(t, layer++);
@@ -2764,16 +2745,6 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
scheduleAnimation();
}
- boolean needsZBoost() {
- if (mNeedsZBoost) return true;
- for (int i = 0; i < mChildren.size(); i++) {
- if (mChildren.get(i).needsZBoost()) {
- return true;
- }
- }
- return false;
- }
-
/**
* Write to a protocol buffer output stream. Protocol buffer message definition is at
* {@link com.android.server.wm.WindowContainerProto}.
@@ -3114,7 +3085,6 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
public void onAnimationLeashLost(Transaction t) {
mLastLayer = -1;
mAnimationLeash = null;
- mNeedsZBoost = false;
reassignLayer(t);
updateSurfacePosition(t);
}
@@ -3140,7 +3110,6 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
protected void onAnimationFinished(@AnimationType int type, AnimationAdapter anim) {
doAnimationFinished(type, anim);
mWmService.onAnimationFinished();
- mNeedsZBoost = false;
}
/**
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index fb38c581d222..00a437cc31f9 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -1449,7 +1449,7 @@ public class WindowManagerService extends IWindowManager.Stub
mPresentationController = new PresentationController();
mBlurController = new BlurController(mContext, mPowerManager);
- mTaskFpsCallbackController = new TaskFpsCallbackController(mContext);
+ mTaskFpsCallbackController = new TaskFpsCallbackController();
mAccessibilityController = new AccessibilityController(this);
mScreenRecordingCallbackController = new ScreenRecordingCallbackController(this);
mSystemPerformanceHinter = new SystemPerformanceHinter(mContext, displayId -> {
@@ -1845,9 +1845,12 @@ public class WindowManagerService extends IWindowManager.Stub
// Only a presentation window needs a transition because its visibility affets the
// lifecycle of apps below (b/390481865).
if (enablePresentationForConnectedDisplays() && win.isPresentation()) {
- Transition transition = null;
+ final boolean wasTransitionOnDisplay =
+ win.mTransitionController.isCollectingTransitionOnDisplay(displayContent);
+ Transition newlyCreatedTransition = null;
if (!win.mTransitionController.isCollecting()) {
- transition = win.mTransitionController.createAndStartCollecting(TRANSIT_OPEN);
+ newlyCreatedTransition =
+ win.mTransitionController.createAndStartCollecting(TRANSIT_OPEN);
}
win.mTransitionController.collect(win.mToken);
res |= addWindowInner(win, displayPolicy, activity, displayContent, outInsetsState,
@@ -1856,9 +1859,14 @@ public class WindowManagerService extends IWindowManager.Stub
// A presentation hides all activities behind on the same display.
win.mDisplayContent.ensureActivitiesVisible(/*starting=*/ null,
/*notifyClients=*/ true);
- win.mTransitionController.getCollectingTransition().setReady(win.mToken, true);
- if (transition != null) {
- win.mTransitionController.requestStartTransition(transition, null,
+ if (!wasTransitionOnDisplay && win.mTransitionController
+ .isCollectingTransitionOnDisplay(displayContent)) {
+ // Set the display ready only when the display gets added to the collecting
+ // transition in this operation.
+ win.mTransitionController.setReady(win.mToken);
+ }
+ if (newlyCreatedTransition != null) {
+ win.mTransitionController.requestStartTransition(newlyCreatedTransition, null,
null /* remoteTransition */, null /* displayChange */);
}
} else {
@@ -2602,6 +2610,14 @@ public class WindowManagerService extends IWindowManager.Stub
// in the new out values right now we need to force a layout.
mWindowPlacerLocked.performSurfacePlacement(true /* force */);
+ if (!win.mHaveFrame && displayContent.mWaitingForConfig) {
+ // We just forcibly triggered the layout, but this could still be intercepted by
+ // mWaitingForConfig. Here, we are forcefully marking a value for mLayoutSeq to
+ // ensure that the resize can occur properly later. Otherwise, the window's frame
+ // will remain empty forever.
+ win.mLayoutSeq = displayContent.mLayoutSeq;
+ }
+
if (shouldRelayout) {
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "relayoutWindow: viewVisibility_1");
@@ -8827,48 +8843,45 @@ public class WindowManagerService extends IWindowManager.Stub
}
void updateNonSystemOverlayWindowsVisibilityIfNeeded(WindowState win, boolean surfaceShown) {
- if (!win.hideNonSystemOverlayWindowsWhenVisible()
- && !mHidingNonSystemOverlayWindows.contains(win)) {
+ final boolean effective = (surfaceShown && win.hideNonSystemOverlayWindowsWhenVisible());
+ if (effective == mHidingNonSystemOverlayWindows.contains(win)) {
return;
}
- final boolean systemAlertWindowsHidden = !mHidingNonSystemOverlayWindows.isEmpty();
- final int numUIDsRequestHidingPreUpdate = mHidingNonSystemOverlayWindowsCountPerUid.size();
- if (surfaceShown && win.hideNonSystemOverlayWindowsWhenVisible()) {
- if (!mHidingNonSystemOverlayWindows.contains(win)) {
- mHidingNonSystemOverlayWindows.add(win);
- int uid = win.getOwningUid();
- int count = mHidingNonSystemOverlayWindowsCountPerUid.getOrDefault(uid, 0);
- mHidingNonSystemOverlayWindowsCountPerUid.put(uid, count + 1);
- }
+
+ if (effective) {
+ mHidingNonSystemOverlayWindows.add(win);
} else {
mHidingNonSystemOverlayWindows.remove(win);
- int uid = win.getOwningUid();
- int count = mHidingNonSystemOverlayWindowsCountPerUid.getOrDefault(uid, 0);
- if (count <= 1) {
- mHidingNonSystemOverlayWindowsCountPerUid.remove(win.getOwningUid());
- } else {
- mHidingNonSystemOverlayWindowsCountPerUid.put(uid, count - 1);
- }
}
- final boolean hideSystemAlertWindows = !mHidingNonSystemOverlayWindows.isEmpty();
- final int numUIDSRequestHidingPostUpdate = mHidingNonSystemOverlayWindowsCountPerUid.size();
+
+ final boolean changed;
if (Flags.fixHideOverlayApi()) {
- if (numUIDSRequestHidingPostUpdate == numUIDsRequestHidingPreUpdate) {
- return;
- }
- // The visibility of SAWs needs to be refreshed only when the number of uids that
- // request hiding SAWs changes 0->1, 1->0, 1->2 or 2->1.
- if (numUIDSRequestHidingPostUpdate != 1 && numUIDsRequestHidingPreUpdate != 1) {
- return;
+ final int uid = win.getOwningUid();
+ final int numUIDsPreUpdate = mHidingNonSystemOverlayWindowsCountPerUid.size();
+ final int newCount = mHidingNonSystemOverlayWindowsCountPerUid.getOrDefault(uid, 0)
+ + (effective ? +1 : -1);
+ if (newCount <= 0) {
+ mHidingNonSystemOverlayWindowsCountPerUid.remove(uid);
+ } else {
+ mHidingNonSystemOverlayWindowsCountPerUid.put(uid, newCount);
}
+ final int numUIDsPostUpdate = mHidingNonSystemOverlayWindowsCountPerUid.size();
+ // The visibility of SAWs needs to be refreshed when the number of uids that
+ // request hiding SAWs changes between "0", "1", or "2+".
+ changed = (numUIDsPostUpdate != numUIDsPreUpdate)
+ && (numUIDsPostUpdate <= 1 || numUIDsPreUpdate <= 1);
} else {
- if (systemAlertWindowsHidden == hideSystemAlertWindows) {
- return;
- }
+ // The visibility of SAWs needs to be refreshed when the number of windows that
+ // request hiding SAWs changes between "0" or "1+".
+ changed = (effective && mHidingNonSystemOverlayWindows.size() == 1)
+ || (!effective && mHidingNonSystemOverlayWindows.isEmpty());
+ }
+
+ if (changed) {
+ mRoot.forAllWindows((w) -> {
+ w.setForceHideNonSystemOverlayWindowIfNeeded(shouldHideNonSystemOverlayWindow(w));
+ }, false /* traverseTopToBottom */);
}
- mRoot.forAllWindows((w) -> {
- w.setForceHideNonSystemOverlayWindowIfNeeded(shouldHideNonSystemOverlayWindow(w));
- }, false /* traverseTopToBottom */);
}
/** Called from Accessibility Controller to apply magnification spec */
diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java
index bdd13722aba4..d356128205df 100644
--- a/services/core/java/com/android/server/wm/WindowProcessController.java
+++ b/services/core/java/com/android/server/wm/WindowProcessController.java
@@ -337,6 +337,11 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio
public static final int ACTIVITY_STATE_FLAG_VISIBLE_MULTI_WINDOW_MODE = 1 << 25;
public static final int ACTIVITY_STATE_FLAG_MASK_MIN_TASK_LAYER = 0x0000ffff;
+ private static final int ACTIVITY_STATE_VISIBLE =
+ com.android.window.flags.Flags.useVisibleRequestedForProcessTracker()
+ ? ACTIVITY_STATE_FLAG_IS_VISIBLE
+ : ACTIVITY_STATE_FLAG_IS_VISIBLE | ACTIVITY_STATE_FLAG_IS_WINDOW_VISIBLE;
+
/**
* The state for oom-adjustment calculation. The higher 16 bits are the activity states, and the
* lower 16 bits are the task layer rank (see {@link Task#mLayerRank}). This field is written by
@@ -1260,8 +1265,7 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio
int nonOccludedRatio = 0;
long perceptibleTaskStoppedTimeMillis = Long.MIN_VALUE;
final boolean wasResumed = hasResumedActivity();
- final boolean wasAnyVisible = (mActivityStateFlags
- & (ACTIVITY_STATE_FLAG_IS_VISIBLE | ACTIVITY_STATE_FLAG_IS_WINDOW_VISIBLE)) != 0;
+ final boolean wasAnyVisible = (mActivityStateFlags & ACTIVITY_STATE_VISIBLE) != 0;
for (int i = mActivities.size() - 1; i >= 0; i--) {
final ActivityRecord r = mActivities.get(i);
if (r.isVisible()) {
@@ -1275,8 +1279,9 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio
if (task.mLayerRank != Task.LAYER_RANK_INVISIBLE) {
stateFlags |= ACTIVITY_STATE_FLAG_HAS_ACTIVITY_IN_VISIBLE_TASK;
}
+ final ActivityRecord.State state = r.getState();
if (r.isVisibleRequested()) {
- if (r.isState(RESUMED)) {
+ if (state == RESUMED) {
stateFlags |= ACTIVITY_STATE_FLAG_HAS_RESUMED;
final int windowingMode = r.getWindowingMode();
if (windowingMode == WINDOWING_MODE_MULTI_WINDOW
@@ -1301,13 +1306,21 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio
// this process, we'd find out the one with the minimal layer, thus it'll
// get a higher adj score.
} else if (!visible && bestInvisibleState != PAUSING) {
- if (r.isState(PAUSING, PAUSED)) {
+ if (state == PAUSING) {
bestInvisibleState = PAUSING;
- } else if (r.isState(STOPPING)) {
+ // Treat PAUSING as visible in case the next activity in the same process has
+ // not yet been set as visible-requested.
+ if (com.android.window.flags.Flags.useVisibleRequestedForProcessTracker()
+ && r.isVisible()) {
+ stateFlags |= ACTIVITY_STATE_FLAG_IS_VISIBLE;
+ }
+ } else if (state == PAUSED) {
+ bestInvisibleState = PAUSED;
+ } else if (state == STOPPING) {
bestInvisibleState = STOPPING;
// Not "finishing" if any of activity isn't finishing.
allStoppingFinishing &= r.finishing;
- } else if (bestInvisibleState == DESTROYED && r.isState(STOPPED)) {
+ } else if (bestInvisibleState == DESTROYED && state == STOPPED) {
if (task.mIsPerceptible) {
perceptibleTaskStoppedTimeMillis =
Long.max(r.mStoppedTime, perceptibleTaskStoppedTimeMillis);
@@ -1340,7 +1353,7 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio
stateFlags |= minTaskLayer & ACTIVITY_STATE_FLAG_MASK_MIN_TASK_LAYER;
if (visible) {
stateFlags |= ACTIVITY_STATE_FLAG_IS_VISIBLE;
- } else if (bestInvisibleState == PAUSING) {
+ } else if (bestInvisibleState == PAUSING || bestInvisibleState == PAUSED) {
stateFlags |= ACTIVITY_STATE_FLAG_IS_PAUSING_OR_PAUSED;
} else if (bestInvisibleState == STOPPING) {
stateFlags |= ACTIVITY_STATE_FLAG_IS_STOPPING;
@@ -1351,8 +1364,7 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio
mActivityStateFlags = stateFlags;
mPerceptibleTaskStoppedTimeMillis = perceptibleTaskStoppedTimeMillis;
- final boolean anyVisible = (stateFlags
- & (ACTIVITY_STATE_FLAG_IS_VISIBLE | ACTIVITY_STATE_FLAG_IS_WINDOW_VISIBLE)) != 0;
+ final boolean anyVisible = (stateFlags & ACTIVITY_STATE_VISIBLE) != 0;
if (!wasAnyVisible && anyVisible) {
mAtm.mVisibleActivityProcessTracker.onAnyActivityVisible(this);
mAtm.mWindowManager.onProcessActivityVisibilityChanged(mUid, true /*visible*/);
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index af5200102fc0..d43aba0d218d 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -2365,9 +2365,12 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
// Only a presentation window needs a transition because its visibility affets the
// lifecycle of apps below (b/390481865).
if (enablePresentationForConnectedDisplays() && isPresentation()) {
- Transition transition = null;
+ final boolean wasTransitionOnDisplay =
+ mTransitionController.isCollectingTransitionOnDisplay(displayContent);
+ Transition newlyCreatedTransition = null;
if (!mTransitionController.isCollecting()) {
- transition = mTransitionController.createAndStartCollecting(TRANSIT_CLOSE);
+ newlyCreatedTransition =
+ mTransitionController.createAndStartCollecting(TRANSIT_CLOSE);
}
mTransitionController.collect(mToken);
mAnimatingExit = true;
@@ -2376,9 +2379,14 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
// A presentation hides all activities behind on the same display.
mDisplayContent.ensureActivitiesVisible(/*starting=*/ null,
/*notifyClients=*/ true);
- mTransitionController.getCollectingTransition().setReady(mToken, true);
- if (transition != null) {
- mTransitionController.requestStartTransition(transition, null,
+ if (!wasTransitionOnDisplay && mTransitionController
+ .isCollectingTransitionOnDisplay(displayContent)) {
+ // Set the display ready only when the display gets added to the collecting
+ // transition in this operation.
+ mTransitionController.setReady(mToken);
+ }
+ if (newlyCreatedTransition != null) {
+ mTransitionController.requestStartTransition(newlyCreatedTransition, null,
null /* remoteTransition */, null /* displayChange */);
}
} else {
@@ -4995,18 +5003,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
return true;
}
- @Override
- boolean needsZBoost() {
- final InsetsControlTarget target = getDisplayContent().getImeTarget(IME_TARGET_LAYERING);
- if (mIsImWindow && target != null) {
- final ActivityRecord activity = target.getWindow().mActivityRecord;
- if (activity != null) {
- return activity.needsZBoost();
- }
- }
- return false;
- }
-
private boolean isStartingWindowAssociatedToTask() {
return mStartingData != null && mStartingData.mAssociatedTask != null;
}
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index ec8794f8073f..017284cded8e 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -1978,6 +1978,11 @@ NativeInputManager::interceptKeyBeforeDispatching(const sp<IBinder>& token,
return inputdispatcher::KeyEntry::InterceptKeyResult::SKIP;
}
+ // -2 : Skip sending even to application and go directly to post processing e.g. fallbacks.
+ if (delayMillis == -2) {
+ return inputdispatcher::KeyEntry::InterceptKeyResult::FALLBACK;
+ }
+
return milliseconds_to_nanoseconds(delayMillis);
}
diff --git a/services/credentials/java/com/android/server/credentials/MetricUtilities.java b/services/credentials/java/com/android/server/credentials/MetricUtilities.java
index ac4aac694c3a..11edb93dffea 100644
--- a/services/credentials/java/com/android/server/credentials/MetricUtilities.java
+++ b/services/credentials/java/com/android/server/credentials/MetricUtilities.java
@@ -383,7 +383,9 @@ public class MetricUtilities {
/* api_name */
initialPhaseMetric.getApiName(),
/* primary_candidates_indicated */
- candidatePrimaryProviderList
+ candidatePrimaryProviderList,
+ /* api_prepared */
+ initialPhaseMetric.hasApiUsedPrepareFlow()
);
} catch (Exception e) {
Slog.w(TAG, "Unexpected error during candidate provider uid metric emit: " + e);
@@ -442,7 +444,9 @@ public class MetricUtilities {
/* autofill_session_id */
initialPhaseMetric.getAutofillSessionId(),
/* autofill_request_id */
- initialPhaseMetric.getAutofillRequestId()
+ initialPhaseMetric.getAutofillRequestId(),
+ /* api_prepared */
+ initialPhaseMetric.hasApiUsedPrepareFlow()
);
} catch (Exception e) {
Slog.w(TAG, "Unexpected error during initial metric emit: " + e);
diff --git a/services/credentials/java/com/android/server/credentials/PrepareGetRequestSession.java b/services/credentials/java/com/android/server/credentials/PrepareGetRequestSession.java
index f6b107b60d62..2d4360edf3a8 100644
--- a/services/credentials/java/com/android/server/credentials/PrepareGetRequestSession.java
+++ b/services/credentials/java/com/android/server/credentials/PrepareGetRequestSession.java
@@ -27,6 +27,7 @@ import android.credentials.GetCredentialRequest;
import android.credentials.IGetCredentialCallback;
import android.credentials.IPrepareGetCredentialCallback;
import android.credentials.PrepareGetCredentialResponseInternal;
+import android.credentials.flags.Flags;
import android.credentials.selection.GetCredentialProviderData;
import android.credentials.selection.ProviderData;
import android.credentials.selection.RequestInfo;
@@ -60,8 +61,15 @@ public class PrepareGetRequestSession extends GetRequestSession {
int numTypes = (request.getCredentialOptions().stream()
.map(CredentialOption::getType).collect(
Collectors.toSet())).size(); // Dedupe type strings
- mRequestSessionMetric.collectGetFlowInitialMetricInfo(request);
+ if (!Flags.fixMetricDuplicationEmits()) {
+ mRequestSessionMetric.collectGetFlowInitialMetricInfo(request);
+ } else {
+ mRequestSessionMetric.collectGetFlowInitialMetricInfo(request,
+ /*isApiPrepared=*/ true);
+ }
mPrepareGetCredentialCallback = prepareGetCredentialCallback;
+
+ Slog.i(TAG, "PrepareGetRequestSession constructed.");
}
@Override
diff --git a/services/credentials/java/com/android/server/credentials/metrics/InitialPhaseMetric.java b/services/credentials/java/com/android/server/credentials/metrics/InitialPhaseMetric.java
index 8a4e86c440b3..811b97a5bf03 100644
--- a/services/credentials/java/com/android/server/credentials/metrics/InitialPhaseMetric.java
+++ b/services/credentials/java/com/android/server/credentials/metrics/InitialPhaseMetric.java
@@ -55,6 +55,9 @@ public class InitialPhaseMetric {
// The request id of autofill if the request is from autofill, defaults to -1
private int mAutofillRequestId = -1;
+ // Indicates if this API call used the prepare flow, defaults to false
+ private boolean mApiUsedPrepareFlow = false;
+
public InitialPhaseMetric(int sessionIdTrackOne) {
mSessionIdCaller = sessionIdTrackOne;
@@ -173,4 +176,17 @@ public class InitialPhaseMetric {
public int[] getUniqueRequestCounts() {
return mRequestCounts.values().stream().mapToInt(Integer::intValue).toArray();
}
+
+ /* ------ API Prepared ------ */
+
+ public void setApiUsedPrepareFlow(boolean apiUsedPrepareFlow) {
+ mApiUsedPrepareFlow = apiUsedPrepareFlow;
+ }
+
+ /**
+ * @return a boolean indicating if this API call utilized a prepare flow
+ */
+ public boolean hasApiUsedPrepareFlow() {
+ return mApiUsedPrepareFlow;
+ }
}
diff --git a/services/credentials/java/com/android/server/credentials/metrics/RequestSessionMetric.java b/services/credentials/java/com/android/server/credentials/metrics/RequestSessionMetric.java
index 619a56846e95..dc1747f803ea 100644
--- a/services/credentials/java/com/android/server/credentials/metrics/RequestSessionMetric.java
+++ b/services/credentials/java/com/android/server/credentials/metrics/RequestSessionMetric.java
@@ -225,6 +225,22 @@ public class RequestSessionMetric {
}
/**
+ * Collects initializations for Get flow metrics.
+ *
+ * @param request the get credential request containing information to parse for metrics
+ * @param isApiPrepared indicates this API flow utilized the 'prepare' flow
+ */
+ public void collectGetFlowInitialMetricInfo(GetCredentialRequest request,
+ boolean isApiPrepared) {
+ try {
+ collectGetFlowInitialMetricInfo(request);
+ mInitialPhaseMetric.setApiUsedPrepareFlow(isApiPrepared);
+ } catch (Exception e) {
+ Slog.i(TAG, "Unexpected error collecting get flow initial metric: " + e);
+ }
+ }
+
+ /**
* During browsing, where multiple entries can be selected, this collects the browsing phase
* metric information. This is emitted together with the final phase, and the recursive path
* with authentication entries, which may occur in rare circumstances, are captured.
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 51ed6bb2aa40..f055febca3d5 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -276,6 +276,7 @@ import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STR
import static com.android.server.SystemTimeZone.TIME_ZONE_CONFIDENCE_HIGH;
import static com.android.server.am.ActivityManagerService.STOCK_PM_FLAGS;
import static com.android.server.devicepolicy.DevicePolicyEngine.DEFAULT_POLICY_SIZE_LIMIT;
+import static com.android.server.devicepolicy.DevicePolicyEngine.SYSTEM_SUPERVISION_ROLE;
import static com.android.server.devicepolicy.DevicePolicyStatsLog.DEVICE_POLICY_MANAGEMENT_MODE;
import static com.android.server.devicepolicy.DevicePolicyStatsLog.DEVICE_POLICY_MANAGEMENT_MODE__MANAGEMENT_MODE__COPE;
import static com.android.server.devicepolicy.DevicePolicyStatsLog.DEVICE_POLICY_MANAGEMENT_MODE__MANAGEMENT_MODE__DEVICE_OWNER;
@@ -16296,6 +16297,13 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
return null;
}
+ /**
+ * When multiple admins enforce a policy, this method returns an admin according to this order:
+ * 1. Supervision
+ * 2. DPC
+ *
+ * Otherwise, it returns any other admin.
+ */
private android.app.admin.EnforcingAdmin getEnforcingAdminInternal(int userId,
String identifier) {
Objects.requireNonNull(identifier);
@@ -16304,16 +16312,22 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
if (admins.isEmpty()) {
return null;
}
-
- final EnforcingAdmin admin;
if (admins.size() == 1) {
- admin = admins.iterator().next();
- } else {
- Optional<EnforcingAdmin> dpc = admins.stream()
- .filter(a -> a.hasAuthority(EnforcingAdmin.DPC_AUTHORITY)).findFirst();
- admin = dpc.orElseGet(() -> admins.stream().findFirst().get());
+ return admins.iterator().next().getParcelableAdmin();
+ }
+ Optional<EnforcingAdmin> supervision = admins.stream()
+ .filter(a -> a.hasAuthority(
+ EnforcingAdmin.getRoleAuthorityOf(SYSTEM_SUPERVISION_ROLE)))
+ .findFirst();
+ if (supervision.isPresent()) {
+ return supervision.get().getParcelableAdmin();
+ }
+ Optional<EnforcingAdmin> dpc = admins.stream()
+ .filter(a -> a.hasAuthority(EnforcingAdmin.DPC_AUTHORITY)).findFirst();
+ if (dpc.isPresent()) {
+ return dpc.get().getParcelableAdmin();
}
- return admin == null ? null : admin.getParcelableAdmin();
+ return admins.iterator().next().getParcelableAdmin();
}
private <V> Set<EnforcingAdmin> getEnforcingAdminsForIdentifier(int userId, String identifier) {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyDefinition.java b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyDefinition.java
index 543e32fae55f..9ff6eb66064f 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyDefinition.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyDefinition.java
@@ -34,6 +34,7 @@ import android.app.admin.PackagePolicyKey;
import android.app.admin.PolicyKey;
import android.app.admin.PolicyValue;
import android.app.admin.UserRestrictionPolicyKey;
+import android.app.admin.flags.Flags;
import android.content.ComponentName;
import android.content.Context;
import android.content.IntentFilter;
@@ -282,7 +283,9 @@ final class PolicyDefinition<V> {
static PolicyDefinition<Set<String>> PERMITTED_INPUT_METHODS = new PolicyDefinition<>(
new NoArgsPolicyKey(DevicePolicyIdentifiers.PERMITTED_INPUT_METHODS_POLICY),
- new MostRecent<>(),
+ (Flags.usePolicyIntersectionForPermittedInputMethods()
+ ? new StringSetIntersection()
+ : new MostRecent<>()),
POLICY_FLAG_LOCAL_ONLY_POLICY | POLICY_FLAG_INHERITABLE,
PolicyEnforcerCallbacks::noOp,
new PackageSetPolicySerializer());
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/StringSetIntersection.java b/services/devicepolicy/java/com/android/server/devicepolicy/StringSetIntersection.java
new file mode 100644
index 000000000000..bc075b02b141
--- /dev/null
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/StringSetIntersection.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2025 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.server.devicepolicy;
+
+import android.annotation.NonNull;
+import android.app.admin.PolicyValue;
+import android.app.admin.PackageSetPolicyValue;
+
+import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.Objects;
+import java.util.Set;
+
+final class StringSetIntersection extends ResolutionMechanism<Set<String>> {
+
+ @Override
+ PolicyValue<Set<String>> resolve(
+ @NonNull LinkedHashMap<EnforcingAdmin, PolicyValue<Set<String>>> adminPolicies) {
+ Objects.requireNonNull(adminPolicies);
+ Set<String> intersectionOfPolicies = null;
+ for (PolicyValue<Set<String>> policy : adminPolicies.values()) {
+ if (intersectionOfPolicies == null) {
+ intersectionOfPolicies = new HashSet<>(policy.getValue());
+ } else {
+ intersectionOfPolicies.retainAll(policy.getValue());
+ }
+ }
+ if (intersectionOfPolicies == null) {
+ return null;
+ }
+ // Note that the resulting set below may be empty, but that's fine:
+ // particular policy should decide what is the meaning of an empty set.
+ return new PackageSetPolicyValue(intersectionOfPolicies);
+ }
+
+ @Override
+ android.app.admin.StringSetIntersection getParcelableResolutionMechanism() {
+ return new android.app.admin.StringSetIntersection();
+ }
+
+ @Override
+ public String toString() {
+ return "StringSetIntersection {}";
+ }
+}
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 788b3b883160..56ec27a0ba87 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -121,6 +121,7 @@ import com.android.internal.util.EmergencyAffordanceManager;
import com.android.internal.util.FrameworkStatsLog;
import com.android.internal.widget.ILockSettings;
import com.android.internal.widget.LockSettingsInternal;
+import com.android.modules.utils.build.SdkLevel;
import com.android.server.accessibility.AccessibilityManagerService;
import com.android.server.accounts.AccountManagerService;
import com.android.server.adb.AdbService;
@@ -2265,19 +2266,27 @@ public final class SystemServer implements Dumpable {
Slog.i(TAG, "Not starting VpnManagerService");
}
- t.traceBegin("StartVcnManagementService");
- try {
- if (VcnLocation.IS_VCN_IN_MAINLINE) {
- mSystemServiceManager.startServiceFromJar(
- CONNECTIVITY_SERVICE_INITIALIZER_B_CLASS,
- CONNECTIVITY_SERVICE_APEX_PATH);
- } else {
- mSystemServiceManager.startService(CONNECTIVITY_SERVICE_INITIALIZER_B_CLASS);
+ // TODO: b/374174952 In the end state, VCN registration will be moved to Tethering
+ // module. Thus the following code block should be removed after Baklava is released
+ if (!VcnLocation.IS_VCN_IN_MAINLINE || !SdkLevel.isAtLeastB()) {
+ t.traceBegin("StartVcnManagementService");
+
+ try {
+ if (!VcnLocation.IS_VCN_IN_MAINLINE) {
+ mSystemServiceManager.startService(
+ CONNECTIVITY_SERVICE_INITIALIZER_B_CLASS);
+ } else {
+ // When VCN is in mainline but the SDK level is B-, start the service with
+ // the apex path. This path can only be hit on an unfinalized B platform
+ mSystemServiceManager.startServiceFromJar(
+ CONNECTIVITY_SERVICE_INITIALIZER_B_CLASS,
+ CONNECTIVITY_SERVICE_APEX_PATH);
+ }
+ } catch (Throwable e) {
+ reportWtf("starting VCN Management Service", e);
}
- } catch (Throwable e) {
- reportWtf("starting VCN Management Service", e);
+ t.traceEnd();
}
- t.traceEnd();
t.traceBegin("StartSystemUpdateManagerService");
try {
diff --git a/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPolicy.kt b/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPolicy.kt
index 232bb83fdf9f..5a140d53a4d8 100644
--- a/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPolicy.kt
+++ b/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPolicy.kt
@@ -1753,6 +1753,13 @@ class AppIdPermissionPolicy : SchemePolicy() {
}
val appIdPermissionFlags = newState.mutateUserState(userId)!!.mutateAppIdPermissionFlags()
val permissionFlags = appIdPermissionFlags.mutateOrPut(appId) { MutableIndexedMap() }
+ // for debugging possible races TODO(b/401768134)
+ oldState.userStates[userId]?.appIdPermissionFlags[appId]?.map?.let {
+ if (permissionFlags.map === it) {
+ throw IllegalStateException("Unexpected sharing between old/new state")
+ }
+ }
+
permissionFlags.putWithDefault(permissionName, newFlags, 0)
if (permissionFlags.isEmpty()) {
appIdPermissionFlags -= appId
diff --git a/services/supervision/java/com/android/server/supervision/SupervisionService.java b/services/supervision/java/com/android/server/supervision/SupervisionService.java
index bb6339c79502..0b5a95b0e888 100644
--- a/services/supervision/java/com/android/server/supervision/SupervisionService.java
+++ b/services/supervision/java/com/android/server/supervision/SupervisionService.java
@@ -27,6 +27,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SuppressLint;
import android.annotation.UserIdInt;
+import android.app.KeyguardManager;
import android.app.admin.DevicePolicyManager;
import android.app.admin.DevicePolicyManagerInternal;
import android.app.supervision.ISupervisionManager;
@@ -147,9 +148,21 @@ public class SupervisionService extends ISupervisionManager.Stub {
@Override
@Nullable
public Intent createConfirmSupervisionCredentialsIntent() {
- // TODO(b/392961554): (1) Return null if supervision is not enabled.
- // (2) check if PIN exists before return a valid intent.
enforceAnyPermission(QUERY_USERS, MANAGE_USERS);
+ if (!isSupervisionEnabledForUser(mContext.getUserId())) {
+ return null;
+ }
+ // Verify the supervising user profile exists and has a secure credential set.
+ final int supervisingUserId = mInjector.getUserManagerInternal().getSupervisingProfileId();
+ final long token = Binder.clearCallingIdentity();
+ try {
+ if (supervisingUserId == UserHandle.USER_NULL
+ || !mInjector.getKeyguardManager().isDeviceSecure(supervisingUserId)) {
+ return null;
+ }
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
final Intent intent = new Intent(ACTION_CONFIRM_SUPERVISION_CREDENTIALS);
// explicitly set the package for security
intent.setPackage("com.android.settings");
@@ -277,6 +290,7 @@ public class SupervisionService extends ISupervisionManager.Stub {
static class Injector {
private final Context mContext;
private DevicePolicyManagerInternal mDpmInternal;
+ private KeyguardManager mKeyguardManager;
private PackageManager mPackageManager;
private UserManagerInternal mUserManagerInternal;
@@ -292,6 +306,13 @@ public class SupervisionService extends ISupervisionManager.Stub {
return mDpmInternal;
}
+ KeyguardManager getKeyguardManager() {
+ if (mKeyguardManager == null) {
+ mKeyguardManager = mContext.getSystemService(KeyguardManager.class);
+ }
+ return mKeyguardManager;
+ }
+
PackageManager getPackageManager() {
if (mPackageManager == null) {
mPackageManager = mContext.getPackageManager();
diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/UserDataPreparerTest.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/UserDataPreparerTest.java
index 554b5b4297f2..740424813c2a 100644
--- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/UserDataPreparerTest.java
+++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/UserDataPreparerTest.java
@@ -98,9 +98,9 @@ public class UserDataPreparerTest {
File systemDeDir = mUserDataPreparer.getDataSystemDeDirectory(TEST_USER_ID);
systemDeDir.mkdirs();
mUserDataPreparer.prepareUserData(TEST_USER, StorageManager.FLAG_STORAGE_DE);
- verify(mStorageManagerMock).prepareUserStorage(isNull(String.class), eq(TEST_USER_ID),
+ verify(mStorageManagerMock).prepareUserStorage(isNull(), eq(TEST_USER_ID),
eq(StorageManager.FLAG_STORAGE_DE));
- verify(mInstaller).createUserData(isNull(String.class), eq(TEST_USER_ID),
+ verify(mInstaller).createUserData(isNull(), eq(TEST_USER_ID),
eq(TEST_USER_SERIAL), eq(StorageManager.FLAG_STORAGE_DE));
int serialNumber = UserDataPreparer.getSerialNumber(userDeDir);
assertEquals(TEST_USER_SERIAL, serialNumber);
@@ -115,9 +115,9 @@ public class UserDataPreparerTest {
File systemCeDir = mUserDataPreparer.getDataSystemCeDirectory(TEST_USER_ID);
systemCeDir.mkdirs();
mUserDataPreparer.prepareUserData(TEST_USER, StorageManager.FLAG_STORAGE_CE);
- verify(mStorageManagerMock).prepareUserStorage(isNull(String.class), eq(TEST_USER_ID),
+ verify(mStorageManagerMock).prepareUserStorage(isNull(), eq(TEST_USER_ID),
eq(StorageManager.FLAG_STORAGE_CE));
- verify(mInstaller).createUserData(isNull(String.class), eq(TEST_USER_ID),
+ verify(mInstaller).createUserData(isNull(), eq(TEST_USER_ID),
eq(TEST_USER_SERIAL), eq(StorageManager.FLAG_STORAGE_CE));
int serialNumber = UserDataPreparer.getSerialNumber(userCeDir);
assertEquals(TEST_USER_SERIAL, serialNumber);
@@ -129,10 +129,10 @@ public class UserDataPreparerTest {
public void testPrepareUserData_forNewUser_destroysOnFailure() throws Exception {
TEST_USER.lastLoggedInTime = 0;
doThrow(new IllegalStateException("expected exception for test")).when(mStorageManagerMock)
- .prepareUserStorage(isNull(String.class), eq(TEST_USER_ID),
+ .prepareUserStorage(isNull(), eq(TEST_USER_ID),
eq(StorageManager.FLAG_STORAGE_CE));
mUserDataPreparer.prepareUserData(TEST_USER, StorageManager.FLAG_STORAGE_CE);
- verify(mStorageManagerMock).destroyUserStorage(isNull(String.class), eq(TEST_USER_ID),
+ verify(mStorageManagerMock).destroyUserStorage(isNull(), eq(TEST_USER_ID),
eq(StorageManager.FLAG_STORAGE_CE));
}
@@ -140,10 +140,10 @@ public class UserDataPreparerTest {
public void testPrepareUserData_forExistingUser_doesNotDestroyOnFailure() throws Exception {
TEST_USER.lastLoggedInTime = System.currentTimeMillis();
doThrow(new IllegalStateException("expected exception for test")).when(mStorageManagerMock)
- .prepareUserStorage(isNull(String.class), eq(TEST_USER_ID),
+ .prepareUserStorage(isNull(), eq(TEST_USER_ID),
eq(StorageManager.FLAG_STORAGE_CE));
mUserDataPreparer.prepareUserData(TEST_USER, StorageManager.FLAG_STORAGE_CE);
- verify(mStorageManagerMock, never()).destroyUserStorage(isNull(String.class),
+ verify(mStorageManagerMock, never()).destroyUserStorage(isNull(),
eq(TEST_USER_ID), eq(StorageManager.FLAG_STORAGE_CE));
}
@@ -171,9 +171,9 @@ public class UserDataPreparerTest {
mUserDataPreparer.destroyUserData(TEST_USER_ID, StorageManager.FLAG_STORAGE_DE);
- verify(mInstaller).destroyUserData(isNull(String.class), eq(TEST_USER_ID),
+ verify(mInstaller).destroyUserData(isNull(), eq(TEST_USER_ID),
eq(StorageManager.FLAG_STORAGE_DE));
- verify(mStorageManagerMock).destroyUserStorage(isNull(String.class), eq(TEST_USER_ID),
+ verify(mStorageManagerMock).destroyUserStorage(isNull(), eq(TEST_USER_ID),
eq(StorageManager.FLAG_STORAGE_DE));
// systemDir (normal path: /data/system/users/$userId) should have been deleted.
@@ -195,9 +195,9 @@ public class UserDataPreparerTest {
mUserDataPreparer.destroyUserData(TEST_USER_ID, StorageManager.FLAG_STORAGE_CE);
- verify(mInstaller).destroyUserData(isNull(String.class), eq(TEST_USER_ID),
+ verify(mInstaller).destroyUserData(isNull(), eq(TEST_USER_ID),
eq(StorageManager.FLAG_STORAGE_CE));
- verify(mStorageManagerMock).destroyUserStorage(isNull(String.class), eq(TEST_USER_ID),
+ verify(mStorageManagerMock).destroyUserStorage(isNull(), eq(TEST_USER_ID),
eq(StorageManager.FLAG_STORAGE_CE));
// systemCeDir (normal path: /data/system_ce/$userId) should still exist but be empty, since
@@ -225,7 +225,7 @@ public class UserDataPreparerTest {
.reconcileUsers(StorageManager.UUID_PRIVATE_INTERNAL, Arrays.asList(u1, u2),
Arrays.asList(dir1, dir2, dir3));
// Verify that user 3 data is removed
- verify(mInstaller).destroyUserData(isNull(String.class), eq(3),
+ verify(mInstaller).destroyUserData(isNull(), eq(3),
eq(StorageManager.FLAG_STORAGE_DE|StorageManager.FLAG_STORAGE_CE));
}
diff --git a/services/tests/displayservicetests/Android.bp b/services/tests/displayservicetests/Android.bp
index 36ea24195789..c85053d13e68 100644
--- a/services/tests/displayservicetests/Android.bp
+++ b/services/tests/displayservicetests/Android.bp
@@ -51,6 +51,7 @@ android_test {
data: [
":DisplayManagerTestApp",
+ ":TopologyTestApp",
],
certificate: "platform",
diff --git a/services/tests/displayservicetests/AndroidManifest.xml b/services/tests/displayservicetests/AndroidManifest.xml
index 205ff058275a..76f219b7433b 100644
--- a/services/tests/displayservicetests/AndroidManifest.xml
+++ b/services/tests/displayservicetests/AndroidManifest.xml
@@ -29,6 +29,7 @@
<uses-permission android:name="android.permission.READ_DEVICE_CONFIG" />
<uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
<uses-permission android:name="android.permission.MANAGE_USB" />
+ <uses-permission android:name="android.permission.MANAGE_DISPLAYS" />
<!-- Permissions needed for DisplayTransformManagerTest -->
<uses-permission android:name="android.permission.CHANGE_CONFIGURATION" />
diff --git a/services/tests/displayservicetests/AndroidTest.xml b/services/tests/displayservicetests/AndroidTest.xml
index f3697bbffd5c..2fe37233870f 100644
--- a/services/tests/displayservicetests/AndroidTest.xml
+++ b/services/tests/displayservicetests/AndroidTest.xml
@@ -28,6 +28,7 @@
<option name="cleanup-apks" value="true" />
<option name="install-arg" value="-t" />
<option name="test-file-name" value="DisplayManagerTestApp.apk" />
+ <option name="test-file-name" value="TopologyTestApp.apk" />
</target_preparer>
<option name="test-tag" value="DisplayServiceTests" />
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayEventDeliveryTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayEventDeliveryTest.java
index 1f45792e5097..bf4b61347bab 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayEventDeliveryTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayEventDeliveryTest.java
@@ -16,7 +16,6 @@
package com.android.server.display;
-import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_CACHED;
import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY;
import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC;
import static android.util.DisplayMetrics.DENSITY_HIGH;
@@ -27,19 +26,11 @@ import static org.junit.Assert.assertNull;
import static org.junit.Assert.fail;
import static org.junit.Assume.assumeTrue;
-import android.app.ActivityManager;
-import android.app.Instrumentation;
-import android.content.Context;
import android.content.Intent;
-import android.hardware.display.DisplayManager;
import android.hardware.display.VirtualDisplay;
-import android.os.BinderProxy;
import android.os.Handler;
-import android.os.HandlerThread;
import android.os.Looper;
import android.os.Message;
-import android.os.Messenger;
-import android.platform.test.annotations.AppModeSdkSandbox;
import android.platform.test.annotations.RequiresFlagsEnabled;
import android.platform.test.flag.junit.CheckFlagsRule;
import android.platform.test.flag.junit.DeviceFlagsValueProvider;
@@ -48,10 +39,7 @@ import android.util.SparseArray;
import androidx.annotation.GuardedBy;
import androidx.annotation.NonNull;
-import androidx.test.platform.app.InstrumentationRegistry;
-import com.android.compatibility.common.util.SystemUtil;
-import com.android.compatibility.common.util.TestUtils;
import com.android.server.am.Flags;
import org.junit.After;
@@ -63,9 +51,7 @@ import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameter;
import org.junit.runners.Parameterized.Parameters;
-import java.io.IOException;
import java.util.Arrays;
-import java.util.concurrent.CountDownLatch;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
@@ -73,8 +59,7 @@ import java.util.concurrent.TimeUnit;
* Tests that applications can receive display events correctly.
*/
@RunWith(Parameterized.class)
-@AppModeSdkSandbox(reason = "Allow test in the SDK sandbox (does not prevent other modes).")
-public class DisplayEventDeliveryTest {
+public class DisplayEventDeliveryTest extends EventDeliveryTestBase {
private static final String TAG = "DisplayEventDeliveryTest";
@Rule
@@ -85,37 +70,17 @@ public class DisplayEventDeliveryTest {
private static final int WIDTH = 720;
private static final int HEIGHT = 480;
- private static final int MESSAGE_LAUNCHED = 1;
- private static final int MESSAGE_CALLBACK = 2;
-
private static final int DISPLAY_ADDED = 1;
private static final int DISPLAY_CHANGED = 2;
private static final int DISPLAY_REMOVED = 3;
- private static final long DISPLAY_EVENT_TIMEOUT_MSEC = 100;
- private static final long TEST_FAILURE_TIMEOUT_MSEC = 10000;
-
private static final String TEST_PACKAGE =
"com.android.servicestests.apps.displaymanagertestapp";
private static final String TEST_ACTIVITY = TEST_PACKAGE + ".DisplayEventActivity";
private static final String TEST_DISPLAYS = "DISPLAYS";
- private static final String TEST_MESSENGER = "MESSENGER";
private final Object mLock = new Object();
- private Instrumentation mInstrumentation;
- private Context mContext;
- private DisplayManager mDisplayManager;
- private ActivityManager mActivityManager;
- private ActivityManager.OnUidImportanceListener mUidImportanceListener;
- private CountDownLatch mLatchActivityLaunch;
- private CountDownLatch mLatchActivityCached;
- private HandlerThread mHandlerThread;
- private Handler mHandler;
- private Messenger mMessenger;
- private int mPid;
- private int mUid;
-
/**
* Array of DisplayBundle. The test handler uses it to check if certain display events have
* been sent to DisplayEventActivity.
@@ -167,7 +132,7 @@ public class DisplayEventDeliveryTest {
*/
public void assertNoDisplayEvents() {
try {
- assertNull(mExpectations.poll(DISPLAY_EVENT_TIMEOUT_MSEC, TimeUnit.MILLISECONDS));
+ assertNull(mExpectations.poll(EVENT_TIMEOUT_MSEC, TimeUnit.MILLISECONDS));
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
@@ -239,37 +204,17 @@ public class DisplayEventDeliveryTest {
}
@Before
- public void setUp() throws Exception {
- mInstrumentation = InstrumentationRegistry.getInstrumentation();
- mContext = mInstrumentation.getContext();
- mDisplayManager = mContext.getSystemService(DisplayManager.class);
- mLatchActivityLaunch = new CountDownLatch(1);
- mLatchActivityCached = new CountDownLatch(1);
- mActivityManager = mContext.getSystemService(ActivityManager.class);
- mUidImportanceListener = (uid, importance) -> {
- if (uid == mUid && importance == IMPORTANCE_CACHED) {
- Log.d(TAG, "Listener " + uid + " becomes " + importance);
- mLatchActivityCached.countDown();
- }
- };
- SystemUtil.runWithShellPermissionIdentity(() ->
- mActivityManager.addOnUidImportanceListener(mUidImportanceListener,
- IMPORTANCE_CACHED));
+ public void setUp() {
+ super.setUp();
// The lock is not functionally necessary but eliminates lint error messages.
synchronized (mLock) {
mDisplayBundles = new SparseArray<>();
}
- mHandlerThread = new HandlerThread("handler");
- mHandlerThread.start();
- mHandler = new TestHandler(mHandlerThread.getLooper());
- mMessenger = new Messenger(mHandler);
- mPid = 0;
}
@After
public void tearDown() throws Exception {
- mActivityManager.removeOnUidImportanceListener(mUidImportanceListener);
- mHandlerThread.quitSafely();
+ super.tearDown();
synchronized (mLock) {
for (int i = 0; i < mDisplayBundles.size(); i++) {
DisplayBundle bundle = mDisplayBundles.valueAt(i);
@@ -278,7 +223,31 @@ public class DisplayEventDeliveryTest {
}
mDisplayBundles.clear();
}
- SystemUtil.runShellCommand(mInstrumentation, "am force-stop " + TEST_PACKAGE);
+ }
+
+ @Override
+ protected String getTag() {
+ return TAG;
+ }
+
+ @Override
+ protected Handler getHandler(Looper looper) {
+ return new TestHandler(looper);
+ }
+
+ @Override
+ protected String getTestPackage() {
+ return TEST_PACKAGE;
+ }
+
+ @Override
+ protected String getTestActivity() {
+ return TEST_ACTIVITY;
+ }
+
+ @Override
+ protected void putExtra(Intent intent) {
+ intent.putExtra(TEST_DISPLAYS, mDisplayCount);
}
/**
@@ -291,42 +260,8 @@ public class DisplayEventDeliveryTest {
}
/**
- * Return true if the freezer is enabled on this platform and if freezer notifications are
- * supported. It is not enough to test that the freezer notification feature is enabled
- * because some devices do not have the necessary kernel support.
- */
- private boolean isAppFreezerEnabled() {
- try {
- return mActivityManager.getService().isAppFreezerEnabled()
- && android.os.Flags.binderFrozenStateChangeCallback()
- && BinderProxy.isFrozenStateChangeCallbackSupported();
- } catch (Exception e) {
- Log.e(TAG, "isAppFreezerEnabled() failed: " + e);
- return false;
- }
- }
-
- private void waitForProcessFreeze(int pid, long timeoutMs) {
- // TODO: Add a listener to monitor freezer state changes.
- SystemUtil.runWithShellPermissionIdentity(() -> {
- TestUtils.waitUntil("Timed out waiting for test process to be frozen; pid=" + pid,
- (int) TimeUnit.MILLISECONDS.toSeconds(timeoutMs),
- () -> mActivityManager.isProcessFrozen(pid));
- });
- }
-
- private void waitForProcessUnfreeze(int pid, long timeoutMs) {
- // TODO: Add a listener to monitor freezer state changes.
- SystemUtil.runWithShellPermissionIdentity(() -> {
- TestUtils.waitUntil("Timed out waiting for test process to be frozen; pid=" + pid,
- (int) TimeUnit.MILLISECONDS.toSeconds(timeoutMs),
- () -> !mActivityManager.isProcessFrozen(pid));
- });
- }
-
- /**
- * Create virtual displays, change their configurations and release them. The number of
- * displays is set by the {@link #mDisplays} variable.
+ * Create virtual displays, change their configurations and release them. The number of
+ * displays is set by the {@link #data()} parameter.
*/
private void testDisplayEventsInternal(boolean cached, boolean frozen) {
Log.d(TAG, "Start test testDisplayEvents " + mDisplayCount + " " + cached + " " + frozen);
@@ -445,110 +380,6 @@ public class DisplayEventDeliveryTest {
}
/**
- * Launch the test activity that would listen to display events. Return its process ID.
- */
- private int launchTestActivity() {
- Intent intent = new Intent(Intent.ACTION_MAIN);
- intent.setClassName(TEST_PACKAGE, TEST_ACTIVITY);
- intent.putExtra(TEST_MESSENGER, mMessenger);
- intent.putExtra(TEST_DISPLAYS, mDisplayCount);
- intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- SystemUtil.runWithShellPermissionIdentity(
- () -> {
- mContext.startActivity(intent);
- },
- android.Manifest.permission.START_ACTIVITIES_FROM_SDK_SANDBOX);
- waitLatch(mLatchActivityLaunch);
-
- try {
- String cmd = "pidof " + TEST_PACKAGE;
- String result = SystemUtil.runShellCommand(mInstrumentation, cmd);
- return Integer.parseInt(result.trim());
- } catch (IOException e) {
- fail("failed to get pid of test package");
- return 0;
- } catch (NumberFormatException e) {
- fail("failed to parse pid " + e);
- return 0;
- }
- }
-
- /**
- * Bring the test activity back to top
- */
- private void bringTestActivityTop() {
- Intent intent = new Intent(Intent.ACTION_MAIN);
- intent.setClassName(TEST_PACKAGE, TEST_ACTIVITY);
- intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
- SystemUtil.runWithShellPermissionIdentity(
- () -> {
- mContext.startActivity(intent);
- },
- android.Manifest.permission.START_ACTIVITIES_FROM_SDK_SANDBOX);
- }
-
- /**
- * Bring the test activity into cached mode by launching another 2 apps
- */
- private void makeTestActivityCached() {
- // Launch another activity to bring the test activity into background
- Intent intent = new Intent(Intent.ACTION_MAIN);
- intent.setClass(mContext, SimpleActivity.class);
- intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
-
- // Launch another activity to bring the test activity into cached mode
- Intent intent2 = new Intent(Intent.ACTION_MAIN);
- intent2.setClass(mContext, SimpleActivity2.class);
- intent2.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- SystemUtil.runWithShellPermissionIdentity(
- () -> {
- mInstrumentation.startActivitySync(intent);
- mInstrumentation.startActivitySync(intent2);
- },
- android.Manifest.permission.START_ACTIVITIES_FROM_SDK_SANDBOX);
- waitLatch(mLatchActivityCached);
- }
-
- // Sleep, ignoring interrupts.
- private void pause(int s) {
- try { Thread.sleep(s * 1000); } catch (Exception e) { }
- }
-
- /**
- * Freeze the test activity.
- */
- private void makeTestActivityFrozen(int pid) {
- // The delay here is meant to allow pending binder transactions to drain. A process
- // cannot be frozen if it has pending binder transactions, and attempting to freeze such a
- // process more than a few times will result in the system killing the process.
- pause(5);
- try {
- String cmd = "am freeze --sticky ";
- SystemUtil.runShellCommand(mInstrumentation, cmd + TEST_PACKAGE);
- } catch (IOException e) {
- fail(e.toString());
- }
- // Wait for the freeze to complete in the kernel and for the frozen process
- // notification to settle out.
- waitForProcessFreeze(pid, 5 * 1000);
- }
-
- /**
- * Freeze the test activity.
- */
- private void makeTestActivityUnfrozen(int pid) {
- try {
- String cmd = "am unfreeze --sticky ";
- SystemUtil.runShellCommand(mInstrumentation, cmd + TEST_PACKAGE);
- } catch (IOException e) {
- fail(e.toString());
- }
- // Wait for the freeze to complete in the kernel and for the frozen process
- // notification to settle out.
- waitForProcessUnfreeze(pid, 5 * 1000);
- }
-
- /**
* Create a virtual display
*
* @param name The name of the new virtual display
@@ -560,15 +391,4 @@ public class DisplayEventDeliveryTest {
VIRTUAL_DISPLAY_FLAG_PUBLIC | VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY
/* flags: a public virtual display that another app can access */);
}
-
- /**
- * Wait for CountDownLatch with timeout
- */
- private void waitLatch(CountDownLatch latch) {
- try {
- latch.await(TEST_FAILURE_TIMEOUT_MSEC, TimeUnit.MILLISECONDS);
- } catch (InterruptedException e) {
- throw new RuntimeException(e);
- }
- }
}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/EventDeliveryTestBase.java b/services/tests/displayservicetests/src/com/android/server/display/EventDeliveryTestBase.java
new file mode 100644
index 000000000000..2911b9bb35c7
--- /dev/null
+++ b/services/tests/displayservicetests/src/com/android/server/display/EventDeliveryTestBase.java
@@ -0,0 +1,259 @@
+/*
+ * Copyright (C) 2025 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.server.display;
+
+import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_CACHED;
+
+import static org.junit.Assert.fail;
+
+import android.app.ActivityManager;
+import android.app.Instrumentation;
+import android.content.Context;
+import android.content.Intent;
+import android.hardware.display.DisplayManager;
+import android.os.BinderProxy;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.os.Messenger;
+import android.platform.test.annotations.AppModeSdkSandbox;
+import android.util.Log;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.android.compatibility.common.util.SystemUtil;
+import com.android.compatibility.common.util.TestUtils;
+
+import java.io.IOException;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+@AppModeSdkSandbox(reason = "Allow test in the SDK sandbox (does not prevent other modes).")
+public abstract class EventDeliveryTestBase {
+ protected static final int MESSAGE_LAUNCHED = 1;
+ protected static final int MESSAGE_CALLBACK = 2;
+
+ protected static final long EVENT_TIMEOUT_MSEC = 100;
+ protected static final long TEST_FAILURE_TIMEOUT_MSEC = 10000;
+
+ private static final String TEST_MESSENGER = "MESSENGER";
+
+ private Instrumentation mInstrumentation;
+ private Context mContext;
+ protected DisplayManager mDisplayManager;
+ private ActivityManager mActivityManager;
+ private ActivityManager.OnUidImportanceListener mUidImportanceListener;
+ protected CountDownLatch mLatchActivityLaunch;
+ private CountDownLatch mLatchActivityCached;
+ private HandlerThread mHandlerThread;
+ private Handler mHandler;
+ private Messenger mMessenger;
+ protected int mPid;
+ protected int mUid;
+
+ protected abstract String getTag();
+
+ protected abstract Handler getHandler(Looper looper);
+
+ protected abstract String getTestPackage();
+
+ protected abstract String getTestActivity();
+
+ protected abstract void putExtra(Intent intent);
+
+ protected void setUp() {
+ mInstrumentation = InstrumentationRegistry.getInstrumentation();
+ mContext = mInstrumentation.getContext();
+ mDisplayManager = mContext.getSystemService(DisplayManager.class);
+ mLatchActivityLaunch = new CountDownLatch(1);
+ mLatchActivityCached = new CountDownLatch(1);
+ mActivityManager = mContext.getSystemService(ActivityManager.class);
+ mUidImportanceListener = (uid, importance) -> {
+ if (uid == mUid && importance == IMPORTANCE_CACHED) {
+ Log.d(getTag(), "Listener " + uid + " becomes " + importance);
+ mLatchActivityCached.countDown();
+ }
+ };
+ SystemUtil.runWithShellPermissionIdentity(() ->
+ mActivityManager.addOnUidImportanceListener(mUidImportanceListener,
+ IMPORTANCE_CACHED));
+ mHandlerThread = new HandlerThread("handler");
+ mHandlerThread.start();
+ mHandler = getHandler(mHandlerThread.getLooper());
+ mMessenger = new Messenger(mHandler);
+ mPid = 0;
+ }
+
+ protected void tearDown() throws Exception {
+ mActivityManager.removeOnUidImportanceListener(mUidImportanceListener);
+ mHandlerThread.quitSafely();
+ SystemUtil.runShellCommand(mInstrumentation, "am force-stop " + getTestPackage());
+ }
+
+ /**
+ * Return true if the freezer is enabled on this platform and if freezer notifications are
+ * supported. It is not enough to test that the freezer notification feature is enabled
+ * because some devices do not have the necessary kernel support.
+ */
+ protected boolean isAppFreezerEnabled() {
+ try {
+ return ActivityManager.getService().isAppFreezerEnabled()
+ && android.os.Flags.binderFrozenStateChangeCallback()
+ && BinderProxy.isFrozenStateChangeCallbackSupported();
+ } catch (Exception e) {
+ Log.e(getTag(), "isAppFreezerEnabled() failed: " + e);
+ return false;
+ }
+ }
+
+ private void waitForProcessFreeze(int pid, long timeoutMs) {
+ // TODO: Add a listener to monitor freezer state changes.
+ SystemUtil.runWithShellPermissionIdentity(() -> {
+ TestUtils.waitUntil(
+ "Timed out waiting for test process to be frozen; pid=" + pid,
+ (int) TimeUnit.MILLISECONDS.toSeconds(timeoutMs),
+ () -> mActivityManager.isProcessFrozen(pid));
+ });
+ }
+
+ private void waitForProcessUnfreeze(int pid, long timeoutMs) {
+ // TODO: Add a listener to monitor freezer state changes.
+ SystemUtil.runWithShellPermissionIdentity(() -> {
+ TestUtils.waitUntil("Timed out waiting for test process to be frozen; pid=" + pid,
+ (int) TimeUnit.MILLISECONDS.toSeconds(timeoutMs),
+ () -> !mActivityManager.isProcessFrozen(pid));
+ });
+ }
+
+ /**
+ * Launch the test activity that would listen to events. Return its process ID.
+ */
+ protected int launchTestActivity() {
+ Intent intent = new Intent(Intent.ACTION_MAIN);
+ intent.setClassName(getTestPackage(), getTestActivity());
+ intent.putExtra(TEST_MESSENGER, mMessenger);
+ putExtra(intent);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ SystemUtil.runWithShellPermissionIdentity(
+ () -> {
+ mContext.startActivity(intent);
+ },
+ android.Manifest.permission.START_ACTIVITIES_FROM_SDK_SANDBOX);
+ waitLatch(mLatchActivityLaunch);
+
+ try {
+ String cmd = "pidof " + getTestPackage();
+ String result = SystemUtil.runShellCommand(mInstrumentation, cmd);
+ return Integer.parseInt(result.trim());
+ } catch (IOException e) {
+ fail("failed to get pid of test package");
+ return 0;
+ } catch (NumberFormatException e) {
+ fail("failed to parse pid " + e);
+ return 0;
+ }
+ }
+
+ /**
+ * Bring the test activity back to top
+ */
+ protected void bringTestActivityTop() {
+ Intent intent = new Intent(Intent.ACTION_MAIN);
+ intent.setClassName(getTestPackage(), getTestActivity());
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
+ SystemUtil.runWithShellPermissionIdentity(
+ () -> {
+ mContext.startActivity(intent);
+ },
+ android.Manifest.permission.START_ACTIVITIES_FROM_SDK_SANDBOX);
+ }
+
+
+ /**
+ * Bring the test activity into cached mode by launching another 2 apps
+ */
+ protected void makeTestActivityCached() {
+ // Launch another activity to bring the test activity into background
+ Intent intent = new Intent(Intent.ACTION_MAIN);
+ intent.setClass(mContext, SimpleActivity.class);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
+
+ // Launch another activity to bring the test activity into cached mode
+ Intent intent2 = new Intent(Intent.ACTION_MAIN);
+ intent2.setClass(mContext, SimpleActivity2.class);
+ intent2.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ SystemUtil.runWithShellPermissionIdentity(
+ () -> {
+ mInstrumentation.startActivitySync(intent);
+ mInstrumentation.startActivitySync(intent2);
+ },
+ android.Manifest.permission.START_ACTIVITIES_FROM_SDK_SANDBOX);
+ waitLatch(mLatchActivityCached);
+ }
+
+ // Sleep, ignoring interrupts.
+ private void pause(int s) {
+ try {
+ Thread.sleep(s * 1000L);
+ } catch (Exception ignored) { }
+ }
+
+ /**
+ * Freeze the test activity.
+ */
+ protected void makeTestActivityFrozen(int pid) {
+ // The delay here is meant to allow pending binder transactions to drain. A process
+ // cannot be frozen if it has pending binder transactions, and attempting to freeze such a
+ // process more than a few times will result in the system killing the process.
+ pause(5);
+ try {
+ String cmd = "am freeze --sticky ";
+ SystemUtil.runShellCommand(mInstrumentation, cmd + getTestPackage());
+ } catch (IOException e) {
+ fail(e.toString());
+ }
+ // Wait for the freeze to complete in the kernel and for the frozen process
+ // notification to settle out.
+ waitForProcessFreeze(pid, 5 * 1000);
+ }
+
+ /**
+ * Freeze the test activity.
+ */
+ protected void makeTestActivityUnfrozen(int pid) {
+ try {
+ String cmd = "am unfreeze --sticky ";
+ SystemUtil.runShellCommand(mInstrumentation, cmd + getTestPackage());
+ } catch (IOException e) {
+ fail(e.toString());
+ }
+ // Wait for the freeze to complete in the kernel and for the frozen process
+ // notification to settle out.
+ waitForProcessUnfreeze(pid, 5 * 1000);
+ }
+
+ /**
+ * Wait for CountDownLatch with timeout
+ */
+ private void waitLatch(CountDownLatch latch) {
+ try {
+ latch.await(TEST_FAILURE_TIMEOUT_MSEC, TimeUnit.MILLISECONDS);
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ }
+}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/TopologyUpdateDeliveryTest.java b/services/tests/displayservicetests/src/com/android/server/display/TopologyUpdateDeliveryTest.java
new file mode 100644
index 000000000000..5fd248dba53f
--- /dev/null
+++ b/services/tests/displayservicetests/src/com/android/server/display/TopologyUpdateDeliveryTest.java
@@ -0,0 +1,243 @@
+/*
+ * Copyright (C) 2025 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.server.display;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.fail;
+import static org.junit.Assume.assumeTrue;
+
+import android.content.Intent;
+import android.hardware.display.DisplayTopology;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Tests that applications can receive topology updates correctly.
+ */
+public class TopologyUpdateDeliveryTest extends EventDeliveryTestBase {
+ private static final String TAG = TopologyUpdateDeliveryTest.class.getSimpleName();
+
+ @Rule
+ public final CheckFlagsRule mCheckFlagsRule =
+ DeviceFlagsValueProvider.createCheckFlagsRule();
+
+ private static final String TEST_PACKAGE = "com.android.servicestests.apps.topologytestapp";
+ private static final String TEST_ACTIVITY = TEST_PACKAGE + ".TopologyUpdateActivity";
+
+ // Topology updates we expect to receive before timeout
+ private final LinkedBlockingQueue<DisplayTopology> mExpectations = new LinkedBlockingQueue<>();
+
+ /**
+ * Add the received topology update from the test activity to the queue
+ *
+ * @param topology The corresponding topology update
+ */
+ private void addTopologyUpdate(DisplayTopology topology) {
+ Log.d(TAG, "Received " + topology);
+ mExpectations.offer(topology);
+ }
+
+ /**
+ * Assert that there isn't any unexpected display event from the test activity
+ */
+ private void assertNoTopologyUpdates() {
+ try {
+ assertNull(mExpectations.poll(EVENT_TIMEOUT_MSEC, TimeUnit.MILLISECONDS));
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * Wait for the expected topology update from the test activity
+ *
+ * @param expect The expected topology update
+ */
+ private void waitTopologyUpdate(DisplayTopology expect) {
+ while (true) {
+ try {
+ DisplayTopology update = mExpectations.poll(TEST_FAILURE_TIMEOUT_MSEC,
+ TimeUnit.MILLISECONDS);
+ assertNotNull(update);
+ if (expect.equals(update)) {
+ Log.d(TAG, "Found " + update);
+ return;
+ }
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+
+ private class TestHandler extends Handler {
+ TestHandler(Looper looper) {
+ super(looper);
+ }
+
+ @Override
+ public void handleMessage(@NonNull Message msg) {
+ switch (msg.what) {
+ case MESSAGE_LAUNCHED:
+ mPid = msg.arg1;
+ mUid = msg.arg2;
+ Log.d(TAG, "Launched " + mPid + " " + mUid);
+ mLatchActivityLaunch.countDown();
+ break;
+ case MESSAGE_CALLBACK:
+ DisplayTopology topology = (DisplayTopology) msg.obj;
+ Log.d(TAG, "Callback " + topology);
+ addTopologyUpdate(topology);
+ break;
+ default:
+ fail("Unexpected value: " + msg.what);
+ break;
+ }
+ }
+ }
+
+ @Before
+ public void setUp() {
+ super.setUp();
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ super.tearDown();
+ }
+
+ @Override
+ protected String getTag() {
+ return TAG;
+ }
+
+ @Override
+ protected Handler getHandler(Looper looper) {
+ return new TestHandler(looper);
+ }
+
+ @Override
+ protected String getTestPackage() {
+ return TEST_PACKAGE;
+ }
+
+ @Override
+ protected String getTestActivity() {
+ return TEST_ACTIVITY;
+ }
+
+ @Override
+ protected void putExtra(Intent intent) { }
+
+ private void testTopologyUpdateInternal(boolean cached, boolean frozen) {
+ Log.d(TAG, "Start test testTopologyUpdate " + cached + " " + frozen);
+ // Launch activity and start listening to topology updates
+ int pid = launchTestActivity();
+
+ // The test activity in cached or frozen mode won't receive the pending topology updates.
+ if (cached) {
+ makeTestActivityCached();
+ }
+ if (frozen) {
+ makeTestActivityFrozen(pid);
+ }
+
+ // Change the topology
+ int primaryDisplayId = 3;
+ DisplayTopology.TreeNode root = new DisplayTopology.TreeNode(primaryDisplayId,
+ /* width= */ 600, /* height= */ 400, DisplayTopology.TreeNode.POSITION_LEFT,
+ /* offset= */ 0);
+ DisplayTopology.TreeNode child = new DisplayTopology.TreeNode(/* displayId= */ 1,
+ /* width= */ 800, /* height= */ 600, DisplayTopology.TreeNode.POSITION_LEFT,
+ /* offset= */ 0);
+ root.addChild(child);
+ DisplayTopology topology = new DisplayTopology(root, primaryDisplayId);
+ mDisplayManager.setDisplayTopology(topology);
+
+ if (cached || frozen) {
+ assertNoTopologyUpdates();
+ } else {
+ waitTopologyUpdate(topology);
+ }
+
+ // Unfreeze the test activity, if it was frozen.
+ if (frozen) {
+ makeTestActivityUnfrozen(pid);
+ }
+
+ if (cached || frozen) {
+ // Always ensure the test activity is not cached.
+ bringTestActivityTop();
+
+ // The test activity becomes non-cached and should receive the pending topology updates
+ waitTopologyUpdate(topology);
+ }
+ }
+
+ @Test
+ @RequiresFlagsEnabled(com.android.server.display.feature.flags.Flags.FLAG_DISPLAY_TOPOLOGY)
+ public void testTopologyUpdate() {
+ testTopologyUpdateInternal(false, false);
+ }
+
+ /**
+ * The app is moved to cached and the test verifies that no updates are delivered to the cached
+ * app.
+ */
+ @Test
+ @RequiresFlagsEnabled(com.android.server.display.feature.flags.Flags.FLAG_DISPLAY_TOPOLOGY)
+ public void testTopologyUpdateCached() {
+ testTopologyUpdateInternal(true, false);
+ }
+
+ /**
+ * The app is frozen and the test verifies that no updates are delivered to the frozen app.
+ */
+ @RequiresFlagsEnabled({com.android.server.am.Flags.FLAG_DEFER_DISPLAY_EVENTS_WHEN_FROZEN,
+ com.android.server.display.feature.flags.Flags.FLAG_DISPLAY_TOPOLOGY})
+ @Test
+ public void testTopologyUpdateFrozen() {
+ assumeTrue(isAppFreezerEnabled());
+ testTopologyUpdateInternal(false, true);
+ }
+
+ /**
+ * The app is cached and frozen and the test verifies that no updates are delivered to the app.
+ */
+ @RequiresFlagsEnabled({com.android.server.am.Flags.FLAG_DEFER_DISPLAY_EVENTS_WHEN_FROZEN,
+ com.android.server.display.feature.flags.Flags.FLAG_DISPLAY_TOPOLOGY})
+ @Test
+ public void testTopologyUpdateCachedFrozen() {
+ assumeTrue(isAppFreezerEnabled());
+ testTopologyUpdateInternal(true, true);
+ }
+}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/AppRequestObserverTest.kt b/services/tests/displayservicetests/src/com/android/server/display/mode/AppRequestObserverTest.kt
index 1f3f19fa3ea8..218728541774 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/mode/AppRequestObserverTest.kt
+++ b/services/tests/displayservicetests/src/com/android/server/display/mode/AppRequestObserverTest.kt
@@ -89,6 +89,39 @@ class AppRequestObserverTest {
assertThat(renderRateVote).isEqualTo(testCase.expectedRenderRateVote)
}
+ @Test
+ fun testAppRequestVote_externalDisplay() {
+ val displayModeDirector = DisplayModeDirector(
+ context, testHandler, mockInjector, mockFlags, mockDisplayDeviceConfigProvider)
+ val modes = arrayOf(
+ Display.Mode(1, 1000, 1000, 60f),
+ Display.Mode(2, 1000, 1000, 90f),
+ )
+
+ displayModeDirector.injectAppSupportedModesByDisplay(
+ SparseArray<Array<Display.Mode>>().apply {
+ append(Display.DEFAULT_DISPLAY, modes)
+ })
+ displayModeDirector.injectDefaultModeByDisplay(SparseArray<Display.Mode>().apply {
+ append(Display.DEFAULT_DISPLAY, modes[0])
+ })
+ displayModeDirector.addExternalDisplayId(Display.DEFAULT_DISPLAY)
+
+ displayModeDirector.appRequestObserver.setAppRequest(Display.DEFAULT_DISPLAY, 1, 0f, 0f, 0f)
+
+ val baseModeVote = displayModeDirector.getVote(Display.DEFAULT_DISPLAY,
+ Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE)
+ assertThat(baseModeVote).isEqualTo(BaseModeRefreshRateVote(60f))
+
+ val sizeVote = displayModeDirector.getVote(Display.DEFAULT_DISPLAY,
+ Vote.PRIORITY_APP_REQUEST_SIZE)
+ assertThat(sizeVote).isNull()
+
+ val renderRateVote = displayModeDirector.getVote(Display.DEFAULT_DISPLAY,
+ Vote.PRIORITY_APP_REQUEST_RENDER_FRAME_RATE_RANGE)
+ assertThat(renderRateVote).isNull()
+ }
+
enum class AppRequestTestCase(
val ignoreRefreshRateRequest: Boolean,
val modeId: Int,
diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/ModeChangeObserverTest.kt b/services/tests/displayservicetests/src/com/android/server/display/mode/ModeChangeObserverTest.kt
new file mode 100644
index 000000000000..a5fb6cdc8d4f
--- /dev/null
+++ b/services/tests/displayservicetests/src/com/android/server/display/mode/ModeChangeObserverTest.kt
@@ -0,0 +1,187 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.display.mode
+
+import android.os.Looper
+import android.view.Display
+import android.view.DisplayAddress
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.junit.MockitoJUnit
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.whenever
+
+private const val PHYSICAL_DISPLAY_ID_1 = 1L
+private const val PHYSICAL_DISPLAY_ID_2 = 2L
+private const val MODE_ID_1 = 3
+private const val MODE_ID_2 = 4
+private const val LOGICAL_DISPLAY_ID = 5
+private val physicalAddress1 = DisplayAddress.fromPhysicalDisplayId(PHYSICAL_DISPLAY_ID_1)
+private val physicalAddress2 = DisplayAddress.fromPhysicalDisplayId(PHYSICAL_DISPLAY_ID_2)
+
+/**
+ * Tests for ModeChangeObserver, comply with changes in b/31925610
+ */
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+public class ModeChangeObserverTest {
+ @get:Rule
+ val mockitoRule = MockitoJUnit.rule()
+
+ // System Under Test
+ private lateinit var modeChangeObserver: ModeChangeObserver
+
+ // Non Mocks
+ private val looper = Looper.getMainLooper()
+ private val votesStorage = VotesStorage({}, null)
+
+ // Mocks
+ private val mockInjector = mock<DisplayModeDirector.Injector>()
+ private val mockDisplay1 = mock<Display>()
+ private val mockDisplay2 = mock<Display>()
+
+ @Before
+ fun setUp() {
+ whenever(mockInjector.getDisplay(LOGICAL_DISPLAY_ID)).thenReturn(mockDisplay1)
+ whenever(mockDisplay1.getAddress()).thenReturn(physicalAddress1)
+ whenever(mockInjector.getDisplays()).thenReturn(arrayOf<Display>())
+ modeChangeObserver = ModeChangeObserver(votesStorage, mockInjector, looper)
+ modeChangeObserver.observe()
+ }
+
+ @Test
+ fun testOnModeRejectedBeforeDisplayAdded() {
+ val rejectedModes = HashSet<Int>()
+ rejectedModes.add(MODE_ID_1)
+ rejectedModes.add(MODE_ID_2)
+
+ // ModeRejected is called before display is mapped, hence votes are null
+ modeChangeObserver.mModeChangeListener.onModeRejected(PHYSICAL_DISPLAY_ID_1, MODE_ID_1)
+ modeChangeObserver.mModeChangeListener.onModeRejected(PHYSICAL_DISPLAY_ID_1, MODE_ID_2)
+ val votes = votesStorage.getVotes(LOGICAL_DISPLAY_ID)
+ assertThat(votes.size()).isEqualTo(0)
+
+ // Display is mapped to a Logical Display Id, now the Rejected Mode Votes get updated
+ modeChangeObserver.mDisplayListener.onDisplayAdded(LOGICAL_DISPLAY_ID)
+ val newVotes = votesStorage.getVotes(LOGICAL_DISPLAY_ID)
+ assertThat(newVotes.size()).isEqualTo(1)
+ val vote = newVotes.get(Vote.PRIORITY_REJECTED_MODES)
+ assertThat(vote).isInstanceOf(RejectedModesVote::class.java)
+ val rejectedModesVote = vote as RejectedModesVote
+ assertThat(rejectedModesVote.mModeIds.size).isEqualTo(rejectedModes.size)
+ assertThat(rejectedModesVote.mModeIds).contains(MODE_ID_1)
+ assertThat(rejectedModesVote.mModeIds).contains(MODE_ID_2)
+ }
+
+ @Test
+ fun testOnDisplayAddedBeforeOnModeRejected() {
+ // Display is mapped to the corresponding Logical Id, but Mode Rejected no received yet
+ // Verify that the Vote is still Null
+ modeChangeObserver.mDisplayListener.onDisplayAdded(LOGICAL_DISPLAY_ID)
+ val votes = votesStorage.getVotes(LOGICAL_DISPLAY_ID)
+ assertThat(votes.size()).isEqualTo(0)
+
+ // ModeRejected Event received for the mapped display
+ modeChangeObserver.mModeChangeListener.onModeRejected(PHYSICAL_DISPLAY_ID_1, MODE_ID_1)
+ val newVotes = votesStorage.getVotes(LOGICAL_DISPLAY_ID)
+ assertThat(newVotes.size()).isEqualTo(1)
+ val vote = newVotes.get(Vote.PRIORITY_REJECTED_MODES)
+ assertThat(vote).isInstanceOf(RejectedModesVote::class.java)
+ val rejectedModesVote = vote as RejectedModesVote
+ assertThat(rejectedModesVote.mModeIds.size).isEqualTo(1)
+ assertThat(rejectedModesVote.mModeIds).contains(MODE_ID_1)
+ }
+
+ @Test
+ fun testOnDisplayAddedThenRejectedThenRemoved() {
+ // Display is mapped to its Logical Display Id
+ modeChangeObserver.mDisplayListener.onDisplayAdded(LOGICAL_DISPLAY_ID)
+ val votes = votesStorage.getVotes(LOGICAL_DISPLAY_ID)
+ assertThat(votes.size()).isEqualTo(0)
+
+ // ModeRejected Event is received for mapped display
+ modeChangeObserver.mModeChangeListener.onModeRejected(PHYSICAL_DISPLAY_ID_1, MODE_ID_1)
+ val newVotes = votesStorage.getVotes(LOGICAL_DISPLAY_ID)
+ assertThat(newVotes.size()).isEqualTo(1)
+ val vote = newVotes.get(Vote.PRIORITY_REJECTED_MODES)
+ assertThat(vote).isInstanceOf(RejectedModesVote::class.java)
+ val rejectedModesVote = vote as RejectedModesVote
+ assertThat(rejectedModesVote.mModeIds.size).isEqualTo(1)
+ assertThat(rejectedModesVote.mModeIds).contains(MODE_ID_1)
+
+ // Display mapping is removed, hence remove the votes
+ modeChangeObserver.mDisplayListener.onDisplayRemoved(LOGICAL_DISPLAY_ID)
+ val finalVotes = votesStorage.getVotes(LOGICAL_DISPLAY_ID)
+ assertThat(finalVotes.size()).isEqualTo(0)
+ }
+
+ @Test
+ fun testForModesRejectedAfterDisplayChanged() {
+ // Mock Display 1 is mapped to logicalId
+ modeChangeObserver.mDisplayListener.onDisplayAdded(LOGICAL_DISPLAY_ID)
+ val votes = votesStorage.getVotes(LOGICAL_DISPLAY_ID)
+ assertThat(votes.size()).isEqualTo(0)
+
+ // Mode Rejected received for PhysicalId2 not mapped yet, so votes are null
+ whenever(mockInjector.getDisplay(LOGICAL_DISPLAY_ID)).thenReturn(mockDisplay2)
+ whenever(mockDisplay2.getAddress()).thenReturn(physicalAddress2)
+ modeChangeObserver.mModeChangeListener.onModeRejected(PHYSICAL_DISPLAY_ID_2, MODE_ID_2)
+ val changedVotes = votesStorage.getVotes(LOGICAL_DISPLAY_ID)
+ assertThat(changedVotes.size()).isEqualTo(0)
+
+ // Display mapping changed, now PhysicalId2 is mapped to the LogicalId, votes get updated
+ modeChangeObserver.mDisplayListener.onDisplayChanged(LOGICAL_DISPLAY_ID)
+ val finalVotes = votesStorage.getVotes(LOGICAL_DISPLAY_ID)
+ assertThat(finalVotes.size()).isEqualTo(1)
+ val finalVote = finalVotes.get(Vote.PRIORITY_REJECTED_MODES)
+ assertThat(finalVote).isInstanceOf(RejectedModesVote::class.java)
+ val newRejectedModesVote = finalVote as RejectedModesVote
+ assertThat(newRejectedModesVote.mModeIds.size).isEqualTo(1)
+ assertThat(newRejectedModesVote.mModeIds).contains(MODE_ID_2)
+ }
+
+ @Test
+ fun testForModesNotRejectedAfterDisplayChanged() {
+ // Mock Display 1 is added
+ modeChangeObserver.mDisplayListener.onDisplayAdded(LOGICAL_DISPLAY_ID)
+ val votes = votesStorage.getVotes(LOGICAL_DISPLAY_ID)
+ assertThat(votes.size()).isEqualTo(0)
+
+ // Mode Rejected received for Display 1, votes added for rejected mode
+ modeChangeObserver.mModeChangeListener.onModeRejected(PHYSICAL_DISPLAY_ID_1, MODE_ID_1)
+ val newVotes = votesStorage.getVotes(LOGICAL_DISPLAY_ID)
+ assertThat(newVotes.size()).isEqualTo(1)
+ val vote = newVotes.get(Vote.PRIORITY_REJECTED_MODES)
+ assertThat(vote).isInstanceOf(RejectedModesVote::class.java)
+ val rejectedModesVote = vote as RejectedModesVote
+ assertThat(rejectedModesVote.mModeIds.size).isEqualTo(1)
+ assertThat(rejectedModesVote.mModeIds).contains(MODE_ID_1)
+
+ // Display Changed such that logical Id corresponds to PhysicalDisplayId2
+ // Rejected Modes Vote is removed
+ whenever(mockInjector.getDisplay(LOGICAL_DISPLAY_ID)).thenReturn(mockDisplay2)
+ whenever(mockDisplay2.getAddress()).thenReturn(physicalAddress2)
+ modeChangeObserver.mDisplayListener.onDisplayChanged(LOGICAL_DISPLAY_ID)
+ val finalVotes = votesStorage.getVotes(LOGICAL_DISPLAY_ID)
+ assertThat(finalVotes.size()).isEqualTo(0)
+ }
+} \ No newline at end of file
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/ApplicationStartInfoTest.java b/services/tests/mockingservicestests/src/com/android/server/am/ApplicationStartInfoTest.java
index 987b9c6427d8..fe4baeb80ee7 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/ApplicationStartInfoTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/ApplicationStartInfoTest.java
@@ -79,7 +79,8 @@ import java.util.ArrayList;
public class ApplicationStartInfoTest {
private static final String TAG = ApplicationStartInfoTest.class.getSimpleName();
- private static final ComponentName COMPONENT = new ComponentName("com.android.test", ".Foo");
+ private static final ComponentName COMPONENT =
+ new ComponentName("com.android.test", "com.android.test.Foo");
private static final int APP_1_UID = 10123;
private static final int APP_1_PID_1 = 12345;
@@ -680,6 +681,67 @@ public class ApplicationStartInfoTest {
ApplicationStartInfo.START_TIMESTAMP_FORK));
}
+ /**
+ * Test that cleanup old records works as expected, removing records that are older than the max
+ * retention length.
+ */
+ @Test
+ @EnableFlags(android.app.Flags.FLAG_APP_START_INFO_CLEANUP_OLD_RECORDS)
+ public void testOldRecordsCleanup() throws Exception {
+ // Use a different start timestamp for each record so we can identify which was removed.
+ // This timestamp is not used for ordering and has no impact on removal.
+ final long startTimeRecord1 = 123L;
+ final long startTimeRecord2 = 456L;
+ final long startTimeRecord3 = 789L;
+
+ // Create a process record to use with all starts.
+ ProcessRecord app = makeProcessRecord(
+ APP_1_PID_1, // pid
+ APP_1_UID, // uid
+ APP_1_UID, // packageUid
+ null, // definingUid
+ APP_1_PROCESS_NAME, // processName
+ APP_1_PACKAGE_NAME); // packageName
+
+ // Set monotonic time to 1, and then trigger a start info record.
+ doReturn(1L).when(mAppStartInfoTracker).getMonotonicTimeMs();
+ mAppStartInfoTracker.handleProcessBroadcastStart(startTimeRecord1, app,
+ buildIntent(COMPONENT), false /* isAlarm */);
+
+ // Set monotonic time to 2, and then trigger another start info record.
+ doReturn(2L).when(mAppStartInfoTracker).getMonotonicTimeMs();
+ mAppStartInfoTracker.handleProcessBroadcastStart(startTimeRecord2, app,
+ buildIntent(COMPONENT), false /* isAlarm */);
+
+ // Set monotonic time to 3, then trigger another start info record.
+ doReturn(3L).when(mAppStartInfoTracker).getMonotonicTimeMs();
+ mAppStartInfoTracker.handleProcessBroadcastStart(startTimeRecord3, app,
+ buildIntent(COMPONENT), false /* isAlarm */);
+
+ // Verify that all 3 records were added successfully.
+ ArrayList<ApplicationStartInfo> list = new ArrayList<ApplicationStartInfo>();
+ mAppStartInfoTracker.getStartInfo(null, APP_1_UID, 0, 0, list);
+ assertEquals(3, list.size());
+ assertEquals(startTimeRecord3, list.get(0).getStartupTimestamps().get(0).longValue());
+ assertEquals(startTimeRecord2, list.get(1).getStartupTimestamps().get(0).longValue());
+ assertEquals(startTimeRecord1, list.get(2).getStartupTimestamps().get(0).longValue());
+
+ // Set monotonic time to max history length + 3 so that the older 2 records will be removed
+ // but that newest 1 will remain.
+ doReturn(AppStartInfoTracker.APP_START_INFO_HISTORY_LENGTH_MS + 3L)
+ .when(mAppStartInfoTracker).getMonotonicTimeMs();
+
+ // Trigger a persist which will trigger the cleanup of old records.
+ mAppStartInfoTracker.persistProcessStartInfo();
+
+ // Now verify that the records older than max were removed, and that the records not older
+ // remain.
+ list.clear();
+ mAppStartInfoTracker.getStartInfo(null, APP_1_UID, 0, 0, list);
+ assertEquals(1, list.size());
+ assertEquals(startTimeRecord3, list.get(0).getStartupTimestamps().get(0).longValue());
+ }
+
private static <T> void setFieldValue(Class clazz, Object obj, String fieldName, T val) {
try {
Field field = clazz.getDeclaredField(fieldName);
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
index e094111c327a..773114e4839d 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
@@ -74,6 +74,10 @@ import static com.android.server.am.ProcessList.SERVICE_ADJ;
import static com.android.server.am.ProcessList.SERVICE_B_ADJ;
import static com.android.server.am.ProcessList.UNKNOWN_ADJ;
import static com.android.server.am.ProcessList.VISIBLE_APP_ADJ;
+import static com.android.server.wm.WindowProcessController.ACTIVITY_STATE_FLAG_IS_PAUSING_OR_PAUSED;
+import static com.android.server.wm.WindowProcessController.ACTIVITY_STATE_FLAG_IS_STOPPING;
+import static com.android.server.wm.WindowProcessController.ACTIVITY_STATE_FLAG_IS_STOPPING_FINISHING;
+import static com.android.server.wm.WindowProcessController.ACTIVITY_STATE_FLAG_IS_VISIBLE;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -544,7 +548,7 @@ public class MockingOomAdjusterTests {
MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true));
WindowProcessController wpc = app.getWindowProcessController();
doReturn(true).when(wpc).hasActivities();
- doReturn(WindowProcessController.ACTIVITY_STATE_FLAG_IS_VISIBLE)
+ doReturn(ACTIVITY_STATE_FLAG_IS_VISIBLE)
.when(wpc).getActivityStateFlags();
setWakefulness(PowerManagerInternal.WAKEFULNESS_AWAKE);
updateOomAdj(app);
@@ -553,28 +557,58 @@ public class MockingOomAdjusterTests {
assertFalse(app.mState.isCached());
assertFalse(app.mState.isEmpty());
assertEquals("vis-activity", app.mState.getAdjType());
+ assertCpuTime(app);
- doReturn(WindowProcessController.ACTIVITY_STATE_FLAG_IS_VISIBLE
+ doReturn(ACTIVITY_STATE_FLAG_IS_VISIBLE
| WindowProcessController.ACTIVITY_STATE_FLAG_RESUMED_SPLIT_SCREEN)
.when(wpc).getActivityStateFlags();
updateOomAdj(app);
assertProcStates(app, PROCESS_STATE_TOP, VISIBLE_APP_ADJ, SCHED_GROUP_TOP_APP);
assertEquals("resumed-split-screen-activity", app.mState.getAdjType());
+ assertCpuTime(app);
- doReturn(WindowProcessController.ACTIVITY_STATE_FLAG_IS_VISIBLE
+ doReturn(ACTIVITY_STATE_FLAG_IS_VISIBLE
| WindowProcessController.ACTIVITY_STATE_FLAG_PERCEPTIBLE_FREEFORM)
.when(wpc).getActivityStateFlags();
updateOomAdj(app);
assertProcStates(app, PROCESS_STATE_TOP, VISIBLE_APP_ADJ, SCHED_GROUP_TOP_APP);
assertEquals("perceptible-freeform-activity", app.mState.getAdjType());
+ assertCpuTime(app);
- doReturn(WindowProcessController.ACTIVITY_STATE_FLAG_IS_VISIBLE
+ doReturn(ACTIVITY_STATE_FLAG_IS_VISIBLE
| WindowProcessController.ACTIVITY_STATE_FLAG_VISIBLE_MULTI_WINDOW_MODE)
.when(wpc).getActivityStateFlags();
updateOomAdj(app);
assertProcStates(app, PROCESS_STATE_TOP, VISIBLE_APP_ADJ,
SCHED_GROUP_FOREGROUND_WINDOW);
assertEquals("vis-multi-window-activity", app.mState.getAdjType());
+ assertCpuTime(app);
+ }
+ @SuppressWarnings("GuardedBy")
+ @Test
+ public void testUpdateOomAdj_DoOne_PausingStoppingActivities() {
+ ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
+ MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true));
+ WindowProcessController wpc = app.getWindowProcessController();
+ doReturn(true).when(wpc).hasActivities();
+ doReturn(ACTIVITY_STATE_FLAG_IS_PAUSING_OR_PAUSED).when(wpc).getActivityStateFlags();
+ setWakefulness(PowerManagerInternal.WAKEFULNESS_AWAKE);
+ updateOomAdj(app);
+ assertProcStates(app, PROCESS_STATE_TOP, PERCEPTIBLE_APP_ADJ, SCHED_GROUP_DEFAULT,
+ "pause-activity");
+ assertCpuTime(app);
+
+ doReturn(ACTIVITY_STATE_FLAG_IS_STOPPING).when(wpc).getActivityStateFlags();
+ updateOomAdj(app);
+ assertProcStates(app, PROCESS_STATE_LAST_ACTIVITY, PERCEPTIBLE_APP_ADJ,
+ SCHED_GROUP_BACKGROUND, "stop-activity");
+ assertCpuTime(app);
+
+ doReturn(ACTIVITY_STATE_FLAG_IS_STOPPING_FINISHING).when(wpc).getActivityStateFlags();
+ updateOomAdj(app);
+ assertProcStates(app, PROCESS_STATE_CACHED_ACTIVITY, CACHED_APP_MIN_ADJ,
+ SCHED_GROUP_BACKGROUND, "cch-act");
+ assertNoCpuTime(app);
}
@SuppressWarnings("GuardedBy")
@@ -750,7 +784,8 @@ public class MockingOomAdjusterTests {
ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true));
WindowProcessController wpc = app.getWindowProcessController();
- doReturn(true).when(wpc).hasVisibleActivities();
+ doReturn(true).when(wpc).hasActivities();
+ doReturn(ACTIVITY_STATE_FLAG_IS_VISIBLE).when(wpc).getActivityStateFlags();
final ProcessRecord app2 = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID,
MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
@@ -3483,12 +3518,14 @@ public class MockingOomAdjusterTests {
// EXPECT: stale-perceptible-act adjustment
doReturn(WindowProcessController.ACTIVITY_STATE_FLAG_IS_STOPPING_FINISHING)
.when(wpc).getActivityStateFlags();
+ assertNoCpuTime(app);
doReturn(now - OomAdjuster.PERCEPTIBLE_TASK_TIMEOUT_MILLIS).when(
wpc).getPerceptibleTaskStoppedTimeMillis();
updateOomAdj(app);
assertProcStates(app, PROCESS_STATE_LAST_ACTIVITY, PREVIOUS_APP_ADJ,
SCHED_GROUP_BACKGROUND, "stale-perceptible-act");
+ assertNoCpuTime(app);
// GIVEN: perceptible adjustment is is disabled
// EXPECT: no perceptible adjustment
@@ -3498,15 +3535,17 @@ public class MockingOomAdjusterTests {
updateOomAdj(app);
assertProcStates(app, PROCESS_STATE_CACHED_ACTIVITY, CACHED_APP_MIN_ADJ,
SCHED_GROUP_BACKGROUND, "cch-act");
+ assertNoCpuTime(app);
// GIVEN: perceptible app is in foreground
// EXPECT: no perceptible adjustment
- doReturn(WindowProcessController.ACTIVITY_STATE_FLAG_IS_VISIBLE)
+ doReturn(ACTIVITY_STATE_FLAG_IS_VISIBLE)
.when(wpc).getActivityStateFlags();
doReturn(now).when(wpc).getPerceptibleTaskStoppedTimeMillis();
updateOomAdj(app);
assertProcStates(app, PROCESS_STATE_TOP, VISIBLE_APP_ADJ,
SCHED_GROUP_DEFAULT, "vis-activity");
+ assertCpuTime(app);
}
@SuppressWarnings("GuardedBy")
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/ProcessObserverTest.java b/services/tests/mockingservicestests/src/com/android/server/am/ProcessObserverTest.java
index 43becc59c3cb..558ea29d85fc 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/ProcessObserverTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/ProcessObserverTest.java
@@ -42,6 +42,8 @@ import android.os.Binder;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
+import android.platform.test.annotations.EnableFlags;
+import android.platform.test.flag.junit.SetFlagsRule;
import android.util.Log;
import androidx.test.filters.MediumTest;
@@ -79,6 +81,8 @@ public class ProcessObserverTest {
@Rule
public final ApplicationExitInfoTest.ServiceThreadRule
mServiceThreadRule = new ApplicationExitInfoTest.ServiceThreadRule();
+ @Rule
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
private Context mContext;
private HandlerThread mHandlerThread;
@@ -239,8 +243,8 @@ public class ProcessObserverTest {
/**
* Verify that a process start event is dispatched to process observers.
*/
- @Ignore("b/323959187")
@Test
+ @EnableFlags(android.app.Flags.FLAG_ENABLE_PROCESS_OBSERVER_BROADCAST_ON_PROCESS_STARTED)
public void testNormal() throws Exception {
ProcessRecord app = startProcess();
verify(mProcessObserver).onProcessStarted(
diff --git a/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsUidStateTrackerTest.java b/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsUidStateTrackerTest.java
index 026e72f117b4..d0226805224e 100644
--- a/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsUidStateTrackerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsUidStateTrackerTest.java
@@ -26,15 +26,12 @@ import static android.app.AppOpsManager.OP_RECEIVE_EXPLICIT_USER_INTERACTION_AUD
import static android.app.AppOpsManager.OP_RECORD_AUDIO;
import static android.app.AppOpsManager.OP_WIFI_SCAN;
import static android.app.AppOpsManager.UID_STATE_BACKGROUND;
-import static android.app.AppOpsManager.UID_STATE_CACHED;
import static android.app.AppOpsManager.UID_STATE_FOREGROUND;
import static android.app.AppOpsManager.UID_STATE_FOREGROUND_SERVICE;
import static android.app.AppOpsManager.UID_STATE_MAX_LAST_NON_RESTRICTED;
import static android.app.AppOpsManager.UID_STATE_TOP;
import static android.permission.flags.Flags.delayUidStateChangesFromCapabilityUpdates;
-import static com.android.server.appop.AppOpsUidStateTracker.processStateToUidState;
-
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
@@ -42,7 +39,6 @@ import static org.junit.Assume.assumeTrue;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
@@ -64,7 +60,6 @@ import org.junit.Before;
import org.junit.Test;
import org.mockito.Mock;
import org.mockito.Mockito;
-import org.mockito.quality.Strictness;
import java.util.PriorityQueue;
@@ -94,7 +89,6 @@ public class AppOpsUidStateTrackerTest {
public void setUp() {
mSession = ExtendedMockito.mockitoSession()
.initMocks(this)
- .strictness(Strictness.LENIENT)
.startMocking();
mConstants.TOP_STATE_SETTLE_TIME = 10 * 1000L;
mConstants.FG_SERVICE_STATE_SETTLE_TIME = 5 * 1000L;
@@ -591,221 +585,6 @@ public class AppOpsUidStateTrackerTest {
}
@Test
- public void testUidStateChangedCallbackCachedToBackground() {
- testUidStateChangedCallback(
- ActivityManager.PROCESS_STATE_CACHED_ACTIVITY,
- ActivityManager.PROCESS_STATE_RECEIVER);
- }
-
- @Test
- public void testUidStateChangedCallbackCachedToForeground() {
- testUidStateChangedCallback(
- ActivityManager.PROCESS_STATE_CACHED_ACTIVITY,
- ActivityManager.PROCESS_STATE_BOUND_TOP);
- }
-
- @Test
- public void testUidStateChangedCallbackCachedToForegroundService() {
- testUidStateChangedCallback(
- ActivityManager.PROCESS_STATE_CACHED_ACTIVITY,
- ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE);
- }
-
- @Test
- public void testUidStateChangedCallbackCachedToTop() {
- testUidStateChangedCallback(
- ActivityManager.PROCESS_STATE_CACHED_ACTIVITY,
- ActivityManager.PROCESS_STATE_TOP);
- }
-
- @Test
- public void testUidStateChangedCallbackBackgroundToCached() {
- testUidStateChangedCallback(
- ActivityManager.PROCESS_STATE_RECEIVER,
- ActivityManager.PROCESS_STATE_CACHED_ACTIVITY);
- }
-
- @Test
- public void testUidStateChangedCallbackBackgroundToForeground() {
- testUidStateChangedCallback(
- ActivityManager.PROCESS_STATE_RECEIVER,
- ActivityManager.PROCESS_STATE_BOUND_TOP);
- }
-
- @Test
- public void testUidStateChangedCallbackBackgroundToForegroundService() {
- testUidStateChangedCallback(
- ActivityManager.PROCESS_STATE_RECEIVER,
- ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE);
- }
-
- @Test
- public void testUidStateChangedCallbackBackgroundToTop() {
- testUidStateChangedCallback(
- ActivityManager.PROCESS_STATE_RECEIVER,
- ActivityManager.PROCESS_STATE_TOP);
- }
-
- @Test
- public void testUidStateChangedCallbackForegroundToCached() {
- testUidStateChangedCallback(
- ActivityManager.PROCESS_STATE_BOUND_TOP,
- ActivityManager.PROCESS_STATE_CACHED_ACTIVITY);
- }
-
- @Test
- public void testUidStateChangedCallbackForegroundToBackground() {
- testUidStateChangedCallback(
- ActivityManager.PROCESS_STATE_BOUND_TOP,
- ActivityManager.PROCESS_STATE_RECEIVER);
- }
-
- @Test
- public void testUidStateChangedCallbackForegroundToForegroundService() {
- testUidStateChangedCallback(
- ActivityManager.PROCESS_STATE_BOUND_TOP,
- ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE);
- }
-
- @Test
- public void testUidStateChangedCallbackForegroundToTop() {
- testUidStateChangedCallback(
- ActivityManager.PROCESS_STATE_BOUND_TOP,
- ActivityManager.PROCESS_STATE_TOP);
- }
-
- @Test
- public void testUidStateChangedCallbackForegroundServiceToCached() {
- testUidStateChangedCallback(
- ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE,
- ActivityManager.PROCESS_STATE_CACHED_ACTIVITY);
- }
-
- @Test
- public void testUidStateChangedCallbackForegroundServiceToBackground() {
- testUidStateChangedCallback(
- ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE,
- ActivityManager.PROCESS_STATE_RECEIVER);
- }
-
- @Test
- public void testUidStateChangedCallbackForegroundServiceToForeground() {
- testUidStateChangedCallback(
- ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE,
- ActivityManager.PROCESS_STATE_BOUND_TOP);
- }
-
- @Test
- public void testUidStateChangedCallbackForegroundServiceToTop() {
- testUidStateChangedCallback(
- ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE,
- ActivityManager.PROCESS_STATE_TOP);
- }
-
- @Test
- public void testUidStateChangedCallbackTopToCached() {
- testUidStateChangedCallback(
- ActivityManager.PROCESS_STATE_TOP,
- ActivityManager.PROCESS_STATE_CACHED_ACTIVITY);
- }
-
- @Test
- public void testUidStateChangedCallbackTopToBackground() {
- testUidStateChangedCallback(
- ActivityManager.PROCESS_STATE_TOP,
- ActivityManager.PROCESS_STATE_RECEIVER);
- }
-
- @Test
- public void testUidStateChangedCallbackTopToForeground() {
- testUidStateChangedCallback(
- ActivityManager.PROCESS_STATE_TOP,
- ActivityManager.PROCESS_STATE_BOUND_TOP);
- }
-
- @Test
- public void testUidStateChangedCallbackTopToForegroundService() {
- testUidStateChangedCallback(
- ActivityManager.PROCESS_STATE_TOP,
- ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE);
- }
-
- @Test
- public void testUidStateChangedCallbackCachedToNonexistent() {
- UidStateChangedCallback cb = addUidStateChangeCallback();
-
- procStateBuilder(UID)
- .cachedState()
- .update();
-
- procStateBuilder(UID)
- .nonExistentState()
- .update();
-
- verify(cb, never()).onUidStateChanged(anyInt(), anyInt(), anyBoolean());
- }
-
- @Test
- public void testUidStateChangedCallbackBackgroundToNonexistent() {
- UidStateChangedCallback cb = addUidStateChangeCallback();
-
- procStateBuilder(UID)
- .backgroundState()
- .update();
-
- procStateBuilder(UID)
- .nonExistentState()
- .update();
-
- verify(cb, atLeastOnce()).onUidStateChanged(eq(UID), eq(UID_STATE_CACHED), eq(false));
- }
-
- @Test
- public void testUidStateChangedCallbackForegroundToNonexistent() {
- UidStateChangedCallback cb = addUidStateChangeCallback();
-
- procStateBuilder(UID)
- .foregroundState()
- .update();
-
- procStateBuilder(UID)
- .nonExistentState()
- .update();
-
- verify(cb, atLeastOnce()).onUidStateChanged(eq(UID), eq(UID_STATE_CACHED), eq(true));
- }
-
- @Test
- public void testUidStateChangedCallbackForegroundServiceToNonexistent() {
- UidStateChangedCallback cb = addUidStateChangeCallback();
-
- procStateBuilder(UID)
- .foregroundServiceState()
- .update();
-
- procStateBuilder(UID)
- .nonExistentState()
- .update();
-
- verify(cb, atLeastOnce()).onUidStateChanged(eq(UID), eq(UID_STATE_CACHED), eq(true));
- }
-
- @Test
- public void testUidStateChangedCallbackTopToNonexistent() {
- UidStateChangedCallback cb = addUidStateChangeCallback();
-
- procStateBuilder(UID)
- .topState()
- .update();
-
- procStateBuilder(UID)
- .nonExistentState()
- .update();
-
- verify(cb, atLeastOnce()).onUidStateChanged(eq(UID), eq(UID_STATE_CACHED), eq(true));
- }
-
- @Test
public void testUidStateChangedBackgroundThenForegroundImmediately() {
procStateBuilder(UID)
.topState()
@@ -882,32 +661,6 @@ public class AppOpsUidStateTrackerTest {
assertEquals(UID_STATE_TOP, mIntf.getUidState(UID));
}
- public void testUidStateChangedCallback(int initialState, int finalState) {
- int initialUidState = processStateToUidState(initialState);
- int finalUidState = processStateToUidState(finalState);
- boolean foregroundChange = initialUidState <= UID_STATE_MAX_LAST_NON_RESTRICTED
- != finalUidState <= UID_STATE_MAX_LAST_NON_RESTRICTED;
- boolean finalUidStateIsBackgroundAndLessImportant =
- finalUidState > UID_STATE_MAX_LAST_NON_RESTRICTED
- && finalUidState > initialUidState;
-
- UidStateChangedCallback cb = addUidStateChangeCallback();
-
- procStateBuilder(UID)
- .setState(initialState)
- .update();
-
- procStateBuilder(UID)
- .setState(finalState)
- .update();
-
- if (finalUidStateIsBackgroundAndLessImportant) {
- mClock.advanceTime(mConstants.TOP_STATE_SETTLE_TIME + 1);
- }
-
- verify(cb, atLeastOnce())
- .onUidStateChanged(eq(UID), eq(finalUidState), eq(foregroundChange));
- }
private UidStateChangedCallback addUidStateChangeCallback() {
UidStateChangedCallback cb =
diff --git a/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsUidStateTrackerTransitionCallbackTest.java b/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsUidStateTrackerTransitionCallbackTest.java
new file mode 100644
index 000000000000..60cdfee0834c
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsUidStateTrackerTransitionCallbackTest.java
@@ -0,0 +1,260 @@
+/*
+ * Copyright (C) 2025 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.server.appop;
+
+import static android.app.AppOpsManager.UID_STATE_CACHED;
+import static android.app.AppOpsManager.UID_STATE_MAX_LAST_NON_RESTRICTED;
+import static android.app.AppOpsManager.UID_STATE_NONEXISTENT;
+import static android.permission.flags.Flags.finishRunningOpsForKilledPackages;
+
+import static com.android.server.appop.AppOpsUidStateTracker.processStateToUidState;
+
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.app.ActivityManager;
+import android.app.ActivityManagerInternal;
+
+import com.android.dx.mockito.inline.extended.ExtendedMockito;
+import com.android.dx.mockito.inline.extended.StaticMockitoSession;
+import com.android.internal.os.Clock;
+import com.android.server.appop.AppOpsUidStateTracker.UidStateChangedCallback;
+import com.android.server.appop.AppOpsUidStateTrackerImpl.DelayableExecutor;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.PriorityQueue;
+
+@RunWith(Parameterized.class)
+public class AppOpsUidStateTrackerTransitionCallbackTest {
+
+ private static final int UID = 10001;
+
+ private static final int STATE_TOP = ActivityManager.PROCESS_STATE_TOP;
+ private static final int STATE_FOREGROUND_SERVICE =
+ ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE;
+ private static final int STATE_FOREGROUND = ActivityManager.PROCESS_STATE_BOUND_TOP;
+ private static final int STATE_BACKGROUND = ActivityManager.PROCESS_STATE_SERVICE;
+ private static final int STATE_CACHED = ActivityManager.PROCESS_STATE_CACHED_ACTIVITY;
+ private static final int STATE_NONEXISTENT = ActivityManager.PROCESS_STATE_NONEXISTENT;
+
+ private static final int[] STATES = {STATE_TOP, STATE_FOREGROUND_SERVICE, STATE_FOREGROUND,
+ STATE_BACKGROUND, STATE_CACHED, STATE_NONEXISTENT};
+ private static final String[] STATES_NAMES = {"TOP", "FOREGROUND_SERVICE", "FOREGROUND",
+ "BACKGROUND", "CACHED", "NONEXISTENT"};
+
+ @Mock
+ ActivityManagerInternal mAmi;
+
+ @Mock
+ AppOpsService.Constants mConstants;
+
+ AppOpsUidStateTrackerTestExecutor mExecutor = new AppOpsUidStateTrackerTestExecutor();
+
+ AppOpsUidStateTrackerTestClock mClock = new AppOpsUidStateTrackerTestClock(mExecutor);
+
+ AppOpsUidStateTracker mIntf;
+
+ StaticMockitoSession mSession;
+
+ private final int mInitialState;
+ private final int mMiddleState;
+ private final int mFinalState;
+
+ @Parameterized.Parameters(name = "{3} -> {4} -> {5}")
+ public static Collection<Object[]> getParameters() {
+ ArrayList<Object[]> parameters = new ArrayList<>();
+
+ for (int i = 0; i < STATES.length; i++) {
+ for (int j = 0; j < STATES.length; j++) {
+ for (int k = 0; k < STATES.length; k++) {
+ parameters
+ .add(new Object[]{STATES[i], STATES[j], STATES[k], STATES_NAMES[i],
+ STATES_NAMES[j], STATES_NAMES[k]});
+ }
+ }
+ }
+
+ return parameters;
+ }
+
+ public AppOpsUidStateTrackerTransitionCallbackTest(int initialState, int middleState,
+ int finalState, String ignoredInitialStateName, String ignoredMiddleStateName,
+ String ignoredFinalStateName) {
+ mInitialState = initialState;
+ mMiddleState = middleState;
+ mFinalState = finalState;
+ }
+
+ @Before
+ public void setUp() {
+ mSession = ExtendedMockito.mockitoSession()
+ .initMocks(this)
+ .startMocking();
+ mConstants.TOP_STATE_SETTLE_TIME = 10 * 1000L;
+ mConstants.FG_SERVICE_STATE_SETTLE_TIME = 5 * 1000L;
+ mConstants.BG_STATE_SETTLE_TIME = 1 * 1000L;
+ mIntf = new AppOpsUidStateTrackerImpl(mAmi, mExecutor, mClock, mConstants,
+ Thread.currentThread());
+ }
+
+ @After
+ public void tearDown() {
+ mSession.finishMocking();
+ }
+
+ @Test
+ public void testUidStateChangedCallback() {
+ testUidStateTransition(mInitialState, mMiddleState, true);
+ testUidStateTransition(mMiddleState, mFinalState, false);
+ }
+
+ private void testUidStateTransition(int initialState, int finalState,
+ boolean initializeState) {
+ int initialUidState = processStateToUidState(initialState);
+ int finalUidState = processStateToUidState(finalState);
+
+ boolean expectUidProcessDeath =
+ finalUidState == UID_STATE_NONEXISTENT
+ && initialUidState != UID_STATE_NONEXISTENT
+ && finishRunningOpsForKilledPackages();
+
+ boolean foregroundChange = initialUidState <= UID_STATE_MAX_LAST_NON_RESTRICTED
+ != finalUidState <= UID_STATE_MAX_LAST_NON_RESTRICTED;
+ boolean finalUidStateIsBackgroundAndLessImportant =
+ finalUidState > UID_STATE_MAX_LAST_NON_RESTRICTED
+ && finalUidState > initialUidState;
+
+ if (initializeState) {
+ mIntf.updateUidProcState(UID, initialState, ActivityManager.PROCESS_CAPABILITY_NONE);
+ }
+
+ UidStateChangedCallback cb = addUidStateChangeCallback();
+
+ mIntf.updateUidProcState(UID, finalState, ActivityManager.PROCESS_CAPABILITY_NONE);
+
+ if (finalUidStateIsBackgroundAndLessImportant) {
+ mClock.advanceTime(mConstants.TOP_STATE_SETTLE_TIME + 1);
+ }
+
+ int expectedInitialUidState = initialUidState == UID_STATE_NONEXISTENT
+ ? UID_STATE_CACHED : initialUidState;
+ int expectedFinalUidState = finalUidState == UID_STATE_NONEXISTENT
+ ? UID_STATE_CACHED : finalUidState;
+
+ if (expectedInitialUidState != expectedFinalUidState) {
+ verify(cb, times(1))
+ .onUidStateChanged(eq(UID), eq(expectedFinalUidState), eq(foregroundChange));
+ verify(cb, times(1))
+ .onUidStateChanged(anyInt(), anyInt(), anyBoolean());
+ } else {
+ verify(cb, never()).onUidStateChanged(anyInt(), anyInt(), anyBoolean());
+ }
+ if (expectUidProcessDeath) {
+ verify(cb, times(1)).onUidProcessDeath(eq(UID));
+ verify(cb, times(1)).onUidProcessDeath(anyInt());
+ } else {
+ verify(cb, never()).onUidProcessDeath(anyInt());
+ }
+ }
+
+ private UidStateChangedCallback addUidStateChangeCallback() {
+ UidStateChangedCallback cb =
+ Mockito.mock(UidStateChangedCallback.class);
+ mIntf.addUidStateChangedCallback(r -> r.run(), cb);
+ return cb;
+ }
+
+ private static class AppOpsUidStateTrackerTestClock extends Clock {
+
+ private AppOpsUidStateTrackerTestExecutor mExecutor;
+ long mElapsedRealTime = 0x5f3759df;
+
+ AppOpsUidStateTrackerTestClock(AppOpsUidStateTrackerTestExecutor executor) {
+ mExecutor = executor;
+ executor.setUptime(mElapsedRealTime);
+ }
+
+ @Override
+ public long elapsedRealtime() {
+ return mElapsedRealTime;
+ }
+
+ void advanceTime(long time) {
+ mElapsedRealTime += time;
+ mExecutor.setUptime(mElapsedRealTime); // assume uptime == elapsedtime
+ }
+ }
+
+ private static class AppOpsUidStateTrackerTestExecutor implements DelayableExecutor {
+
+ private static class QueueElement implements Comparable<QueueElement> {
+
+ private long mExecutionTime;
+ private Runnable mRunnable;
+
+ private QueueElement(long executionTime, Runnable runnable) {
+ mExecutionTime = executionTime;
+ mRunnable = runnable;
+ }
+
+ @Override
+ public int compareTo(QueueElement queueElement) {
+ return Long.compare(mExecutionTime, queueElement.mExecutionTime);
+ }
+ }
+
+ private long mUptime = 0;
+
+ private PriorityQueue<QueueElement> mDelayedMessages = new PriorityQueue();
+
+ @Override
+ public void execute(Runnable runnable) {
+ runnable.run();
+ }
+
+ @Override
+ public void executeDelayed(Runnable runnable, long delay) {
+ if (delay <= 0) {
+ execute(runnable);
+ }
+
+ mDelayedMessages.add(new QueueElement(mUptime + delay, runnable));
+ }
+
+ private void setUptime(long uptime) {
+ while (!mDelayedMessages.isEmpty()
+ && mDelayedMessages.peek().mExecutionTime <= uptime) {
+ mDelayedMessages.poll().mRunnable.run();
+ }
+
+ mUptime = uptime;
+ }
+ }
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java
index 8c09f26bb7fa..fdf78ad88311 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java
@@ -56,12 +56,12 @@ import android.app.ActivityManagerInternal;
import android.app.AppGlobals;
import android.app.IActivityManager;
import android.app.UiModeManager;
+import android.app.compat.CompatChanges;
import android.app.job.JobInfo;
import android.app.job.JobParameters;
import android.app.job.JobScheduler;
import android.app.job.JobWorkItem;
import android.app.usage.UsageStatsManagerInternal;
-import android.compat.testing.PlatformCompatChangeRule;
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
@@ -81,6 +81,7 @@ import android.os.BatteryManagerInternal.ChargingPolicyChangeListener;
import android.os.Looper;
import android.os.Process;
import android.os.RemoteException;
+import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.WorkSource;
import android.os.WorkSource.WorkChain;
@@ -98,6 +99,7 @@ import com.android.server.DeviceIdleInternal;
import com.android.server.LocalServices;
import com.android.server.PowerAllowlistInternal;
import com.android.server.SystemServiceManager;
+import com.android.server.compat.PlatformCompat;
import com.android.server.job.controllers.ConnectivityController;
import com.android.server.job.controllers.JobStatus;
import com.android.server.job.controllers.QuotaController;
@@ -106,14 +108,10 @@ import com.android.server.job.restrictions.ThermalStatusRestriction;
import com.android.server.pm.UserManagerInternal;
import com.android.server.usage.AppStandbyInternal;
-import libcore.junit.util.compat.CoreCompatChangeRule.DisableCompatChanges;
-import libcore.junit.util.compat.CoreCompatChangeRule.EnableCompatChanges;
-
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
-import org.junit.rules.TestRule;
import org.mockito.ArgumentCaptor;
import org.mockito.ArgumentMatchers;
import org.mockito.Mock;
@@ -147,9 +145,6 @@ public class JobSchedulerServiceTest {
@Rule
public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
- @Rule
- public TestRule compatChangeRule = new PlatformCompatChangeRule();
-
private ChargingPolicyChangeListener mChargingPolicyChangeListener;
private int mSourceUid;
@@ -166,8 +161,10 @@ public class JobSchedulerServiceTest {
mMockingSession = mockitoSession()
.initMocks(this)
.strictness(Strictness.LENIENT)
+ .mockStatic(CompatChanges.class)
.mockStatic(LocalServices.class)
.mockStatic(PermissionChecker.class)
+ .mockStatic(ServiceManager.class)
.startMocking();
// Called in JobSchedulerService constructor.
@@ -230,6 +227,9 @@ public class JobSchedulerServiceTest {
ArgumentCaptor<ChargingPolicyChangeListener> chargingPolicyChangeListenerCaptor =
ArgumentCaptor.forClass(ChargingPolicyChangeListener.class);
+ doReturn(mock(PlatformCompat.class))
+ .when(() -> ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE));
+
mService = new TestJobSchedulerService(mContext);
mService.waitOnAsyncLoadingForTesting();
@@ -1074,12 +1074,15 @@ public class JobSchedulerServiceTest {
*/
@Test
@EnableFlags(FLAG_HANDLE_ABANDONED_JOBS)
- @DisableCompatChanges({JobParameters.OVERRIDE_HANDLE_ABANDONED_JOBS})
public void testGetRescheduleJobForFailure_abandonedJob() {
final long nowElapsed = sElapsedRealtimeClock.millis();
final long initialBackoffMs = MINUTE_IN_MILLIS;
mService.mConstants.SYSTEM_STOP_TO_FAILURE_RATIO = 3;
+ // Mock the OVERRIDE_HANDLE_ABANDONED_JOBS compat change overrides.
+ when(CompatChanges.isChangeEnabled(
+ eq(JobParameters.OVERRIDE_HANDLE_ABANDONED_JOBS), anyInt())).thenReturn(false);
+
JobStatus originalJob = createJobStatus("testGetRescheduleJobForFailure",
createJobInfo()
.setBackoffCriteria(initialBackoffMs, JobInfo.BACKOFF_POLICY_LINEAR));
@@ -1148,8 +1151,10 @@ public class JobSchedulerServiceTest {
*/
@Test
@EnableFlags(FLAG_HANDLE_ABANDONED_JOBS)
- @DisableCompatChanges({JobParameters.OVERRIDE_HANDLE_ABANDONED_JOBS})
public void testGetRescheduleJobForFailure_EnableFlagDisableCompatCheckAggressiveBackoff() {
+ // Mock the OVERRIDE_HANDLE_ABANDONED_JOBS compat change overrides.
+ when(CompatChanges.isChangeEnabled(
+ eq(JobParameters.OVERRIDE_HANDLE_ABANDONED_JOBS), anyInt())).thenReturn(false);
assertFalse(mService.shouldUseAggressiveBackoff(
mService.mConstants.ABANDONED_JOB_TIMEOUTS_BEFORE_AGGRESSIVE_BACKOFF - 1,
mSourceUid));
@@ -1167,8 +1172,10 @@ public class JobSchedulerServiceTest {
*/
@Test
@EnableFlags(FLAG_HANDLE_ABANDONED_JOBS)
- @EnableCompatChanges({JobParameters.OVERRIDE_HANDLE_ABANDONED_JOBS})
public void testGetRescheduleJobForFailure_EnableFlagEnableCompatCheckAggressiveBackoff() {
+ // Mock the OVERRIDE_HANDLE_ABANDONED_JOBS compat change overrides.
+ when(CompatChanges.isChangeEnabled(
+ eq(JobParameters.OVERRIDE_HANDLE_ABANDONED_JOBS), anyInt())).thenReturn(true);
assertFalse(mService.shouldUseAggressiveBackoff(
mService.mConstants.ABANDONED_JOB_TIMEOUTS_BEFORE_AGGRESSIVE_BACKOFF - 1,
mSourceUid));
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
index 2d84887afb41..924fe9586af9 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
@@ -76,6 +76,7 @@ import android.os.BatteryManagerInternal;
import android.os.Handler;
import android.os.Looper;
import android.os.RemoteException;
+import android.os.ServiceManager;
import android.os.SystemClock;
import android.platform.test.annotations.DisableFlags;
import android.platform.test.annotations.EnableFlags;
@@ -93,6 +94,7 @@ import androidx.test.runner.AndroidJUnit4;
import com.android.internal.util.ArrayUtils;
import com.android.server.LocalServices;
import com.android.server.PowerAllowlistInternal;
+import com.android.server.compat.PlatformCompat;
import com.android.server.job.Flags;
import com.android.server.job.JobSchedulerInternal;
import com.android.server.job.JobSchedulerService;
@@ -104,8 +106,6 @@ import com.android.server.job.controllers.QuotaController.TimedEvent;
import com.android.server.job.controllers.QuotaController.TimingSession;
import com.android.server.usage.AppStandbyInternal;
-import libcore.junit.util.compat.CoreCompatChangeRule.EnableCompatChanges;
-
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
@@ -168,6 +168,8 @@ public class QuotaControllerTest {
private PowerAllowlistInternal mPowerAllowlistInternal;
@Mock
private UsageStatsManagerInternal mUsageStatsManager;
+ @Mock
+ private PlatformCompat mPlatformCompat;
@Rule
public final CheckFlagsRule mCheckFlagsRule =
@@ -182,6 +184,7 @@ public class QuotaControllerTest {
.strictness(Strictness.LENIENT)
.spyStatic(DeviceConfig.class)
.mockStatic(LocalServices.class)
+ .mockStatic(ServiceManager.class)
.startMocking();
// Called in StateController constructor.
@@ -198,6 +201,7 @@ public class QuotaControllerTest {
}
when(mContext.getMainLooper()).thenReturn(Looper.getMainLooper());
when(mContext.getSystemService(AlarmManager.class)).thenReturn(mAlarmManager);
+
doReturn(mActivityMangerInternal)
.when(() -> LocalServices.getService(ActivityManagerInternal.class));
doReturn(mock(AppStandbyInternal.class))
@@ -253,6 +257,8 @@ public class QuotaControllerTest {
ArgumentCaptor.forClass(PowerAllowlistInternal.TempAllowlistChangeListener.class);
ArgumentCaptor<UsageStatsManagerInternal.UsageEventListener> ueListenerCaptor =
ArgumentCaptor.forClass(UsageStatsManagerInternal.UsageEventListener.class);
+ doReturn(mPlatformCompat)
+ .when(() -> ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE));
mQuotaController = new QuotaController(mJobSchedulerService,
mock(BackgroundJobsController.class), mock(ConnectivityController.class));
@@ -5591,13 +5597,17 @@ public class QuotaControllerTest {
}
@Test
- @EnableCompatChanges({QuotaController.OVERRIDE_QUOTA_ENFORCEMENT_TO_TOP_STARTED_JOBS,
- QuotaController.OVERRIDE_QUOTA_ENFORCEMENT_TO_FGS_JOBS})
@RequiresFlagsEnabled({Flags.FLAG_ENFORCE_QUOTA_POLICY_TO_TOP_STARTED_JOBS,
Flags.FLAG_ENFORCE_QUOTA_POLICY_TO_FGS_JOBS})
public void testTracking_OutOfQuota_ForegroundAndBackground_CompactChangeOverrides() {
setDischarging();
+ // Mock the OVERRIDE_QUOTA_ENFORCEMENT_TO_TOP_STARTED_JOBS compat change overrides.
+ doReturn(true).when(mPlatformCompat).isChangeEnabledByUid(
+ eq(QuotaController.OVERRIDE_QUOTA_ENFORCEMENT_TO_TOP_STARTED_JOBS), anyInt());
+ doReturn(true).when(mPlatformCompat).isChangeEnabledByUid(
+ eq(QuotaController.OVERRIDE_QUOTA_ENFORCEMENT_TO_FGS_JOBS), anyInt());
+
JobStatus jobBg = createJobStatus("testTracking_OutOfQuota_ForegroundAndBackground", 1);
JobStatus jobTop = createJobStatus("testTracking_OutOfQuota_ForegroundAndBackground", 2);
trackJobs(jobBg, jobTop);
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryExternalStatsWorkerTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryExternalStatsWorkerTest.java
index 2b152315eec4..a1cf94b994ab 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryExternalStatsWorkerTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryExternalStatsWorkerTest.java
@@ -26,16 +26,10 @@ import static com.android.server.power.stats.BatteryStatsImpl.ExternalStatsSync.
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
import android.content.Context;
-import android.hardware.power.stats.Channel;
-import android.hardware.power.stats.EnergyConsumer;
import android.hardware.power.stats.EnergyConsumerResult;
import android.hardware.power.stats.EnergyConsumerType;
-import android.hardware.power.stats.EnergyMeasurement;
-import android.hardware.power.stats.PowerEntity;
-import android.hardware.power.stats.StateResidencyResult;
import android.os.Handler;
import android.os.Looper;
import android.os.connectivity.WifiActivityEnergyInfo;
@@ -54,7 +48,6 @@ import org.junit.Before;
import org.junit.Test;
import java.util.Arrays;
-import java.util.concurrent.CompletableFuture;
/**
* Tests for {@link BatteryExternalStatsWorker}.
@@ -66,7 +59,7 @@ import java.util.concurrent.CompletableFuture;
@android.platform.test.annotations.DisabledOnRavenwood
public class BatteryExternalStatsWorkerTest {
private BatteryExternalStatsWorker mBatteryExternalStatsWorker;
- private TestPowerStatsInternal mPowerStatsInternal;
+ private MockPowerStatsInternal mPowerStatsInternal;
private Handler mHandler;
@Before
@@ -80,7 +73,7 @@ public class BatteryExternalStatsWorkerTest {
mHandler, null, null, null,
new PowerProfile(context, true /* forTest */), buildScalingPolicies(),
new PowerStatsUidResolver());
- mPowerStatsInternal = new TestPowerStatsInternal();
+ mPowerStatsInternal = new MockPowerStatsInternal();
mBatteryExternalStatsWorker =
new BatteryExternalStatsWorker(new TestInjector(context), batteryStats, mHandler);
}
@@ -260,102 +253,4 @@ public class BatteryExternalStatsWorkerTest {
freqsByPolicy.put(4, new int[]{300000, 1000000, 2500000, 3000000});
return new CpuScalingPolicies(freqsByPolicy, freqsByPolicy);
}
-
- private static class TestPowerStatsInternal extends PowerStatsInternal {
- private final SparseArray<EnergyConsumer> mEnergyConsumers = new SparseArray();
- private final SparseArray<EnergyConsumerResult> mEnergyConsumerResults = new SparseArray();
- private final int mTimeSinceBoot = 0;
-
- @Override
- public EnergyConsumer[] getEnergyConsumerInfo() {
- final int size = mEnergyConsumers.size();
- final EnergyConsumer[] consumers = new EnergyConsumer[size];
- for (int i = 0; i < size; i++) {
- consumers[i] = mEnergyConsumers.valueAt(i);
- }
- return consumers;
- }
-
- @Override
- public CompletableFuture<EnergyConsumerResult[]> getEnergyConsumedAsync(
- int[] energyConsumerIds) {
- final CompletableFuture<EnergyConsumerResult[]> future = new CompletableFuture();
- final EnergyConsumerResult[] results;
- final int length = energyConsumerIds.length;
- if (length == 0) {
- final int size = mEnergyConsumerResults.size();
- results = new EnergyConsumerResult[size];
- for (int i = 0; i < size; i++) {
- results[i] = mEnergyConsumerResults.valueAt(i);
- }
- } else {
- results = new EnergyConsumerResult[length];
- for (int i = 0; i < length; i++) {
- results[i] = mEnergyConsumerResults.get(energyConsumerIds[i]);
- }
- }
- future.complete(results);
- return future;
- }
-
- @Override
- public PowerEntity[] getPowerEntityInfo() {
- return new PowerEntity[0];
- }
-
- @Override
- public CompletableFuture<StateResidencyResult[]> getStateResidencyAsync(
- int[] powerEntityIds) {
- return new CompletableFuture<>();
- }
-
- @Override
- public Channel[] getEnergyMeterInfo() {
- return new Channel[0];
- }
-
- @Override
- public CompletableFuture<EnergyMeasurement[]> readEnergyMeterAsync(
- int[] channelIds) {
- return new CompletableFuture<>();
- }
-
- /**
- * Util method to add a new EnergyConsumer for testing
- *
- * @return the EnergyConsumer id of the new EnergyConsumer
- */
- public int addEnergyConsumer(@EnergyConsumerType byte type, int ordinal, String name) {
- final EnergyConsumer consumer = new EnergyConsumer();
- final int id = getNextAvailableId();
- consumer.id = id;
- consumer.type = type;
- consumer.ordinal = ordinal;
- consumer.name = name;
- mEnergyConsumers.put(id, consumer);
-
- final EnergyConsumerResult result = new EnergyConsumerResult();
- result.id = id;
- result.timestampMs = mTimeSinceBoot;
- result.energyUWs = 0;
- mEnergyConsumerResults.put(id, result);
- return id;
- }
-
- public void incrementEnergyConsumption(int id, long energyUWs) {
- EnergyConsumerResult result = mEnergyConsumerResults.get(id, null);
- assertNotNull(result);
- result.energyUWs += energyUWs;
- }
-
- private int getNextAvailableId() {
- final int size = mEnergyConsumers.size();
- // Just return the first index that does not match the key (aka the EnergyConsumer id)
- for (int i = size - 1; i >= 0; i--) {
- if (mEnergyConsumers.keyAt(i) == i) return i + 1;
- }
- // Otherwise return the lowest id
- return 0;
- }
- }
}
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryHistoryStepDetailsProviderTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryHistoryStepDetailsProviderTest.java
new file mode 100644
index 000000000000..850cd8868e67
--- /dev/null
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryHistoryStepDetailsProviderTest.java
@@ -0,0 +1,163 @@
+/*
+ * Copyright (C) 2025 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.server.power.stats;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.mock;
+
+import android.content.Context;
+import android.os.BatteryManager;
+import android.os.BatteryStats;
+import android.os.ConditionVariable;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Process;
+import android.power.PowerStatsInternal;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import com.android.internal.os.BatteryStatsHistoryIterator;
+import com.android.internal.os.MonotonicClock;
+import com.android.internal.os.PowerProfile;
+import com.android.server.LocalServices;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Random;
+
+@RunWith(AndroidJUnit4.class)
+@android.platform.test.annotations.DisabledOnRavenwood(reason =
+ "PowerStatsInternal is not supported under Ravenwood")
+@SuppressWarnings("GuardedBy")
+public class BatteryHistoryStepDetailsProviderTest {
+ private static final int APP_UID = Process.FIRST_APPLICATION_UID + 42;
+
+ private final MockClock mMockClock = new MockClock();
+ private MockBatteryStatsImpl mBatteryStats;
+ private final Random mRandom = new Random();
+ private Handler mHandler;
+
+ @Before
+ public void setup() {
+ mMockClock.currentTime = 3000;
+ mHandler = new Handler(Looper.getMainLooper());
+ mBatteryStats = new MockBatteryStatsImpl(mMockClock, null, mHandler,
+ mock(PowerProfile.class));
+ mBatteryStats.setRecordAllHistoryLocked(true);
+ mBatteryStats.forceRecordAllHistory();
+ mBatteryStats.setNoAutoReset(true);
+ }
+
+ @Test
+ public void update() {
+ MockPowerStatsInternal powerStatsService = new MockPowerStatsInternal();
+ powerStatsService.addPowerEntity(42, "foo");
+ powerStatsService.addPowerEntityState(42, 0, "off");
+ powerStatsService.addPowerEntityState(42, 1, "on");
+ LocalServices.addService(PowerStatsInternal.class, powerStatsService);
+ mBatteryStats.onSystemReady(mock(Context.class));
+
+ mockUpdateCpuStats(100, 1_000_010, 1_000_010);
+ powerStatsService.addStateResidencyResult(42, 0, 1000, 2000, 3000);
+ powerStatsService.addStateResidencyResult(42, 1, 4000, 5000, 6000);
+
+ mBatteryStats.setBatteryStateLocked(BatteryManager.BATTERY_STATUS_DISCHARGING,
+ 100, /* plugType */ 0, 90, 72, 3700, 3_600_000, 4_000_000, 0,
+ 1_000_000, 1_000_000, 1_000_000);
+ awaitCompletion();
+
+ mockUpdateCpuStats(200, 5_000_010, 5_000_010);
+ powerStatsService.reset();
+ powerStatsService.addStateResidencyResult(42, 0, 1111, 2222, 3333);
+ powerStatsService.addStateResidencyResult(42, 1, 4444, 5555, 6666);
+
+ // Battery level is unchanged, so we don't write battery level details in history
+ mBatteryStats.setBatteryStateLocked(BatteryManager.BATTERY_STATUS_DISCHARGING,
+ 100, /* plugType */ 0, 80, 72, 3700, 2_400_000, 4_000_000, 0,
+ 5_500_000, 5_500_000, 5_000_000);
+ awaitCompletion();
+
+ // Not a battery state change event, so details are not written
+ mBatteryStats.noteAlarmStartLocked("wakeup", null, APP_UID, 6_000_000, 6_000_000);
+
+ mockUpdateCpuStats(300, 6_000_010, 6_000_010);
+ powerStatsService.reset();
+
+ // Battery level drops, so we write the accumulated battery level details
+ mBatteryStats.setBatteryStateLocked(BatteryManager.BATTERY_STATUS_DISCHARGING,
+ 100, /* plugType */ 0, 70, 72, 3700, 2_000_000, 4_000_000, 0,
+ 6_000_000, 6_000_000, 6_000_000);
+ awaitCompletion();
+
+ final BatteryStatsHistoryIterator iterator =
+ mBatteryStats.iterateBatteryStatsHistory(0, MonotonicClock.UNDEFINED);
+
+ BatteryStats.HistoryItem item;
+ assertThat(item = iterator.next()).isNotNull();
+ assertThat(item.cmd).isEqualTo((int) BatteryStats.HistoryItem.CMD_RESET);
+ assertThat(item.stepDetails).isNull();
+
+ assertThat(item = iterator.next()).isNotNull();
+ assertThat(item.batteryLevel).isEqualTo(90);
+ assertThat(item.stepDetails.userTime).isEqualTo(100);
+ assertThat(item.stepDetails.statSubsystemPowerState).contains("subsystem_0 name=foo "
+ + "state_0 name=off time=1000 count=2000 last entry=3000 "
+ + "state_1 name=on time=4000 count=5000 last entry=6000");
+
+ assertThat(item = iterator.next()).isNotNull();
+ assertThat(item.batteryLevel).isEqualTo(80);
+ assertThat(item.stepDetails.userTime).isEqualTo(200);
+ assertThat(item.stepDetails.statSubsystemPowerState).contains("subsystem_0 name=foo "
+ + "state_0 name=off time=1111 count=2222 last entry=3333 "
+ + "state_1 name=on time=4444 count=5555 last entry=6666");
+
+ assertThat(item = iterator.next()).isNotNull();
+ assertThat(item.batteryLevel).isEqualTo(80);
+ assertThat(item.stepDetails).isNull();
+
+ assertThat(item = iterator.next()).isNotNull();
+ assertThat(item.batteryLevel).isEqualTo(70);
+ assertThat(item.stepDetails.userTime).isEqualTo(300);
+ assertThat(item.stepDetails.statSubsystemPowerState).isNull();
+
+ assertThat(iterator.next()).isNull();
+ }
+
+ private void mockUpdateCpuStats(int totalUTimeMs, long elapsedRealtime, long uptime) {
+ BatteryStatsImpl.BatteryCallback callback = mock(BatteryStatsImpl.BatteryCallback.class);
+ doAnswer(inv -> {
+ mMockClock.realtime = elapsedRealtime;
+ mMockClock.uptime = uptime;
+ synchronized (mBatteryStats) {
+ mBatteryStats.addCpuStatsLocked(totalUTimeMs, 0, 0, 0, 0, 0, 0, 0);
+ mBatteryStats.finishAddingCpuStatsLocked();
+ }
+ return null;
+ }).when(callback).batteryNeedsCpuUpdate();
+ mBatteryStats.setCallback(callback);
+ }
+
+ private void awaitCompletion() {
+ ConditionVariable done = new ConditionVariable();
+ mHandler.post(done::open);
+ done.block();
+ }
+}
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsHistoryIteratorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsHistoryIteratorTest.java
index 503e23347cf6..1b82ffdcf3e8 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsHistoryIteratorTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsHistoryIteratorTest.java
@@ -47,14 +47,12 @@ public class BatteryStatsHistoryIteratorTest {
private final MockClock mMockClock = new MockClock();
private MockBatteryStatsImpl mBatteryStats;
private final Random mRandom = new Random();
- private final MockExternalStatsSync mExternalStatsSync = new MockExternalStatsSync();
@Before
public void setup() {
final File historyDir = createTemporaryDirectory(getClass().getSimpleName());
mMockClock.currentTime = 3000;
mBatteryStats = new MockBatteryStatsImpl(mMockClock, historyDir);
- mBatteryStats.setDummyExternalStatsSync(mExternalStatsSync);
mBatteryStats.setRecordAllHistoryLocked(true);
mBatteryStats.forceRecordAllHistory();
mBatteryStats.setNoAutoReset(true);
@@ -227,72 +225,53 @@ public class BatteryStatsHistoryIteratorTest {
100, /* plugType */ 0, 90, 72, 3700, 3_600_000, 4_000_000, 0,
1_000_000, 1_000_000, 1_000_000);
- mExternalStatsSync.updateCpuStats(100, 1_100_000, 1_100_000);
-
// Device was suspended for 3_000 seconds, note the difference in elapsed time and uptime
mBatteryStats.setBatteryStateLocked(BatteryManager.BATTERY_STATUS_DISCHARGING,
100, /* plugType */ 0, 80, 72, 3700, 2_400_000, 4_000_000, 0,
5_000_000, 2_000_000, 5_000_000);
- mExternalStatsSync.updateCpuStats(200, 5_100_000, 2_100_000);
-
// Battery level is unchanged, so we don't write battery level details in history
- mBatteryStats.noteAlarmStartLocked("wakeup", null, APP_UID, 6_000_000, 3_000_000);
+ mBatteryStats.setBatteryStateLocked(BatteryManager.BATTERY_STATUS_DISCHARGING,
+ 100, /* plugType */ 0, 80, 72, 3700, 2_400_000, 4_000_000, 0,
+ 5_500_000, 2_500_000, 5_000_000);
- assertThat(mExternalStatsSync.isSyncScheduled()).isFalse();
+ // Not a battery state change event, so details are not written
+ mBatteryStats.noteAlarmStartLocked("wakeup", null, APP_UID, 6_000_000, 3_000_000);
// Battery level drops, so we write the accumulated battery level details
mBatteryStats.setBatteryStateLocked(BatteryManager.BATTERY_STATUS_DISCHARGING,
- 100, /* plugType */ 0, 79, 72, 3700, 2_000_000, 4_000_000, 0,
+ 100, /* plugType */ 0, 70, 72, 3700, 2_000_000, 4_000_000, 0,
7_000_000, 4_000_000, 6_000_000);
- mExternalStatsSync.updateCpuStats(300, 7_100_000, 4_100_000);
-
final BatteryStatsHistoryIterator iterator =
mBatteryStats.iterateBatteryStatsHistory(0, MonotonicClock.UNDEFINED);
BatteryStats.HistoryItem item;
assertThat(item = iterator.next()).isNotNull();
assertThat(item.cmd).isEqualTo((int) BatteryStats.HistoryItem.CMD_RESET);
- assertThat(item.stepDetails).isNull();
assertThat(item = iterator.next()).isNotNull();
assertThat(item.batteryLevel).isEqualTo(90);
- assertThat(item.stepDetails).isNull();
-
- assertThat(item = iterator.next()).isNotNull();
- assertThat(item.batteryLevel).isEqualTo(90);
- assertThat(item.eventCode).isEqualTo(BatteryStats.HistoryItem.EVENT_COLLECT_EXTERNAL_STATS);
+ assertThat(item.states & BatteryStats.HistoryItem.STATE_CPU_RUNNING_FLAG).isNotEqualTo(0);
assertThat(item = iterator.next()).isNotNull();
assertThat(item.batteryLevel).isEqualTo(90);
assertThat(item.states & BatteryStats.HistoryItem.STATE_CPU_RUNNING_FLAG).isEqualTo(0);
- assertThat(item.stepDetails.userTime).isEqualTo(100);
assertThat(item = iterator.next()).isNotNull();
assertThat(item.batteryLevel).isEqualTo(80);
assertThat(item.states & BatteryStats.HistoryItem.STATE_CPU_RUNNING_FLAG).isNotEqualTo(0);
- assertThat(item.stepDetails.userTime).isEqualTo(0);
-
- assertThat(item = iterator.next()).isNotNull();
- assertThat(item.batteryLevel).isEqualTo(80);
- assertThat(item.eventCode).isEqualTo(BatteryStats.HistoryItem.EVENT_COLLECT_EXTERNAL_STATS);
assertThat(item = iterator.next()).isNotNull();
assertThat(item.batteryLevel).isEqualTo(80);
assertThat(item.eventCode).isEqualTo(BatteryStats.HistoryItem.EVENT_ALARM_START);
- assertThat(item.stepDetails).isNull();
-
- assertThat(item = iterator.next()).isNotNull();
- assertThat(item.batteryLevel).isEqualTo(79);
assertThat(item.states & BatteryStats.HistoryItem.STATE_CPU_RUNNING_FLAG).isNotEqualTo(0);
- assertThat(item.stepDetails.userTime).isEqualTo(200);
assertThat(item = iterator.next()).isNotNull();
- assertThat(item.batteryLevel).isEqualTo(79);
- assertThat(item.eventCode).isEqualTo(BatteryStats.HistoryItem.EVENT_COLLECT_EXTERNAL_STATS);
+ assertThat(item.batteryLevel).isEqualTo(70);
+ assertThat(item.states & BatteryStats.HistoryItem.STATE_CPU_RUNNING_FLAG).isNotEqualTo(0);
- assertThat(item = iterator.next()).isNull();
+ assertThat(iterator.next()).isNull();
}
@Test
@@ -394,26 +373,4 @@ public class BatteryStatsHistoryIteratorTest {
assertThat(item.time).isEqualTo(elapsedTimeMs);
}
-
- private class MockExternalStatsSync extends MockBatteryStatsImpl.DummyExternalStatsSync {
- private boolean mSyncScheduled;
-
- @Override
- public void scheduleCpuSyncDueToWakelockChange(long delayMillis) {
- mSyncScheduled = true;
- }
-
- public boolean isSyncScheduled() {
- return mSyncScheduled;
- }
-
- public void updateCpuStats(int totalUTimeMs, long elapsedRealtime, long uptime) {
- assertThat(mExternalStatsSync.mSyncScheduled).isTrue();
- mBatteryStats.recordHistoryEventLocked(elapsedRealtime, uptime,
- BatteryStats.HistoryItem.EVENT_COLLECT_EXTERNAL_STATS, "wakelock-update", 0);
- mBatteryStats.addCpuStatsLocked(totalUTimeMs, 0, 0, 0, 0, 0, 0, 0);
- mBatteryStats.finishAddingCpuStatsLocked();
- mExternalStatsSync.mSyncScheduled = false;
- }
- }
}
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsHistoryTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsHistoryTest.java
index fc864dd230d9..7acb93c0641e 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsHistoryTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsHistoryTest.java
@@ -26,7 +26,6 @@ import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.when;
import android.os.BatteryConsumer;
import android.os.BatteryManager;
@@ -58,7 +57,8 @@ import org.junit.runner.RunWith;
import org.mockito.InOrder;
import org.mockito.Mock;
import org.mockito.Mockito;
-import org.mockito.MockitoAnnotations;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
import java.io.File;
import java.io.IOException;
@@ -85,6 +85,8 @@ public class BatteryStatsHistoryTest {
@Rule
public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+ @Rule
+ public final MockitoRule mMockitoRule = MockitoJUnit.rule();
private final Parcel mHistoryBuffer = Parcel.obtain();
private File mSystemDir;
@@ -97,14 +99,11 @@ public class BatteryStatsHistoryTest {
@Mock
private BatteryStatsHistory.TraceDelegate mTracer;
@Mock
- private BatteryStatsHistory.HistoryStepDetailsCalculator mStepDetailsCalculator;
- @Mock
private BatteryStatsHistory.EventLogger mEventLogger;
private List<String> mReadFiles = new ArrayList<>();
@Before
public void setUp() throws IOException {
- MockitoAnnotations.initMocks(this);
mSystemDir = Files.createTempDirectory("BatteryStatsHistoryTest").toFile();
mHistoryDir = new File(mSystemDir, "battery-history");
String[] files = mHistoryDir.list();
@@ -138,14 +137,10 @@ public class BatteryStatsHistoryTest {
mClock.currentTime = 1743645660000L; // 2025-04-03, 2:01:00 AM
mHistory = new BatteryStatsHistory(mHistoryBuffer, MAX_HISTORY_BUFFER_SIZE, mDirectory,
- mStepDetailsCalculator, mClock, mMonotonicClock, mTracer,
+ mClock, mMonotonicClock, mTracer,
mEventLogger);
mHistory.forceRecordAllHistory();
mHistory.startRecordingHistory(mClock.realtime, mClock.uptime, false);
-
- when(mStepDetailsCalculator.getHistoryStepDetails())
- .thenReturn(new BatteryStats.HistoryStepDetails());
-
mHistoryPrinter = new BatteryStats.HistoryPrinter(TimeZone.getTimeZone("GMT"));
}
@@ -288,7 +283,7 @@ public class BatteryStatsHistoryTest {
// create a new BatteryStatsHistory object, it will pick up existing history files.
BatteryStatsHistory history2 = new BatteryStatsHistory(mHistoryBuffer, 1024, mDirectory,
- null, mClock, mMonotonicClock, mTracer, mEventLogger);
+ mClock, mMonotonicClock, mTracer, mEventLogger);
// verify constructor can pick up all files from file system.
verifyFileNames(history2, fileList);
verifyActiveFile(history2, "33000.bh");
@@ -343,12 +338,13 @@ public class BatteryStatsHistoryTest {
assertThat(mReadFiles).containsExactly("123.bh", "1000.bh");
} else if (item.eventCode == HistoryItem.EVENT_ALARM) {
eventsRead++;
- assertThat(mReadFiles).containsExactly("123.bh", "1000.bh", "2000.bh");
+ // This event is in the current buffer, so 2000.bh shouldn't be read from disk
+ assertThat(mReadFiles).containsExactly("123.bh", "1000.bh");
}
}
assertThat(eventsRead).isEqualTo(3);
- assertThat(mReadFiles).containsExactly("123.bh", "1000.bh", "2000.bh", "3000.bh");
+ assertThat(mReadFiles).containsExactly("123.bh", "1000.bh");
}
@Test
@@ -366,34 +362,41 @@ public class BatteryStatsHistoryTest {
return invocation.callRealMethod();
}).when(mHistory).readFragmentToParcel(any(), any());
- BatteryStatsHistoryIterator iterator = mHistory.iterate(1000, 3000);
+ int eventsRead = 0;
+ BatteryStatsHistoryIterator iterator = mHistory.iterate(1001, 3000);
while (iterator.hasNext()) {
HistoryItem item = iterator.next();
if (item.eventCode == HistoryItem.EVENT_JOB_START) {
fail("Event outside the range");
} else if (item.eventCode == HistoryItem.EVENT_JOB_FINISH) {
+ eventsRead++;
assertThat(mReadFiles).containsExactly("1000.bh");
} else if (item.eventCode == HistoryItem.EVENT_ALARM) {
fail("Event outside the range");
}
}
- assertThat(mReadFiles).containsExactly("1000.bh", "2000.bh");
+ assertThat(eventsRead).isEqualTo(1);
+ assertThat(mReadFiles).containsExactly("1000.bh");
}
private void prepareMultiFileHistory() {
- mClock.realtime = 1000;
- mClock.uptime = 1000;
+ mClock.realtime = 500;
+ mClock.uptime = 500;
mHistory.recordEvent(mClock.realtime, mClock.uptime,
BatteryStats.HistoryItem.EVENT_JOB_START, "job", 42);
+ mClock.realtime = 1000;
+ mClock.uptime = 1000;
mHistory.startNextFragment(mClock.realtime); // 1000.bh
- mClock.realtime = 2000;
- mClock.uptime = 2000;
+ mClock.realtime = 1500;
+ mClock.uptime = 1500;
mHistory.recordEvent(mClock.realtime, mClock.uptime,
BatteryStats.HistoryItem.EVENT_JOB_FINISH, "job", 42);
+ mClock.realtime = 2000;
+ mClock.uptime = 2000;
mHistory.startNextFragment(mClock.realtime); // 2000.bh
mClock.realtime = 3000;
@@ -401,8 +404,8 @@ public class BatteryStatsHistoryTest {
mHistory.recordEvent(mClock.realtime, mClock.uptime,
HistoryItem.EVENT_ALARM, "alarm", 42);
- // Flush accumulated history to disk
- mHistory.startNextFragment(mClock.realtime);
+ // Back up accumulated history to disk
+ mHistory.writeHistory();
}
private void verifyActiveFile(BatteryStatsHistory history, String file) {
@@ -587,7 +590,7 @@ public class BatteryStatsHistoryTest {
// Keep the preserved part of history short - we only need to capture the very tail of
// history.
mHistory = new BatteryStatsHistory(mHistoryBuffer, 6000, mDirectory,
- mStepDetailsCalculator, mClock, mMonotonicClock, mTracer, mEventLogger);
+ mClock, mMonotonicClock, mTracer, mEventLogger);
mHistory.forceRecordAllHistory();
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsProviderTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsProviderTest.java
index 8a1d37b55255..90b3e132f9d3 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsProviderTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsProviderTest.java
@@ -575,7 +575,7 @@ public class BatteryUsageStatsProviderTest {
accumulateBatteryUsageStats(batteryStats, 10000000, 0);
// Accumulate every 200 bytes of battery history
accumulateBatteryUsageStats(batteryStats, 200, 1);
- accumulateBatteryUsageStats(batteryStats, 50, 5);
+ accumulateBatteryUsageStats(batteryStats, 50, 4);
// Accumulate on every invocation of accumulateBatteryUsageStats
accumulateBatteryUsageStats(batteryStats, 0, 7);
}
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/MockBatteryStatsImpl.java b/services/tests/powerstatstests/src/com/android/server/power/stats/MockBatteryStatsImpl.java
index c7a19ce7b233..8c3bd17c5acd 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/MockBatteryStatsImpl.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/MockBatteryStatsImpl.java
@@ -329,10 +329,6 @@ public class MockBatteryStatsImpl extends BatteryStatsImpl {
}
@Override
- public void scheduleSyncDueToBatteryLevelChange(long delayMillis) {
- }
-
- @Override
public void scheduleSyncDueToProcessStateChange(int flags, long delayMillis) {
}
}
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/MockPowerStatsInternal.java b/services/tests/powerstatstests/src/com/android/server/power/stats/MockPowerStatsInternal.java
new file mode 100644
index 000000000000..dc5da8c2841f
--- /dev/null
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/MockPowerStatsInternal.java
@@ -0,0 +1,198 @@
+/*
+ * Copyright (C) 2025 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.server.power.stats;
+
+import static org.junit.Assert.assertNotNull;
+
+import android.hardware.power.stats.Channel;
+import android.hardware.power.stats.EnergyConsumer;
+import android.hardware.power.stats.EnergyConsumerResult;
+import android.hardware.power.stats.EnergyConsumerType;
+import android.hardware.power.stats.EnergyMeasurement;
+import android.hardware.power.stats.PowerEntity;
+import android.hardware.power.stats.State;
+import android.hardware.power.stats.StateResidency;
+import android.hardware.power.stats.StateResidencyResult;
+import android.power.PowerStatsInternal;
+import android.util.SparseArray;
+
+import java.util.Arrays;
+import java.util.concurrent.CompletableFuture;
+
+class MockPowerStatsInternal extends PowerStatsInternal {
+ private final SparseArray<PowerEntity> mPowerEntities = new SparseArray<>();
+ private final SparseArray<StateResidencyResult> mStateResidencyResults = new SparseArray<>();
+ private final SparseArray<EnergyConsumer> mEnergyConsumers = new SparseArray<>();
+ private final SparseArray<EnergyConsumerResult> mEnergyConsumerResults = new SparseArray<>();
+ private final int mTimeSinceBoot = 0;
+
+ @Override
+ public EnergyConsumer[] getEnergyConsumerInfo() {
+ final int size = mEnergyConsumers.size();
+ final EnergyConsumer[] consumers = new EnergyConsumer[size];
+ for (int i = 0; i < size; i++) {
+ consumers[i] = mEnergyConsumers.valueAt(i);
+ }
+ return consumers;
+ }
+
+ @Override
+ public CompletableFuture<EnergyConsumerResult[]> getEnergyConsumedAsync(
+ int[] energyConsumerIds) {
+ final CompletableFuture<EnergyConsumerResult[]> future = new CompletableFuture();
+ final EnergyConsumerResult[] results;
+ final int length = energyConsumerIds.length;
+ if (length == 0) {
+ final int size = mEnergyConsumerResults.size();
+ results = new EnergyConsumerResult[size];
+ for (int i = 0; i < size; i++) {
+ results[i] = mEnergyConsumerResults.valueAt(i);
+ }
+ } else {
+ results = new EnergyConsumerResult[length];
+ for (int i = 0; i < length; i++) {
+ results[i] = mEnergyConsumerResults.get(energyConsumerIds[i]);
+ }
+ }
+ future.complete(results);
+ return future;
+ }
+
+ @Override
+ public PowerEntity[] getPowerEntityInfo() {
+ final int size = mPowerEntities.size();
+ final PowerEntity[] entities = new PowerEntity[size];
+ for (int i = 0; i < size; i++) {
+ entities[i] = mPowerEntities.valueAt(i);
+ }
+ return entities;
+ }
+
+ @Override
+ public CompletableFuture<StateResidencyResult[]> getStateResidencyAsync(
+ int[] powerEntityIds) {
+ final CompletableFuture<StateResidencyResult[]> future = new CompletableFuture<>();
+ final StateResidencyResult[] results;
+ final int length = powerEntityIds.length;
+ if (length == 0) {
+ final int size = mStateResidencyResults.size();
+ results = new StateResidencyResult[size];
+ for (int i = 0; i < size; i++) {
+ results[i] = mStateResidencyResults.valueAt(i);
+ }
+ } else {
+ results = new StateResidencyResult[length];
+ for (int i = 0; i < length; i++) {
+ results[i] = mStateResidencyResults.get(powerEntityIds[i]);
+ }
+ }
+ future.complete(results);
+ return future;
+ }
+
+ @Override
+ public Channel[] getEnergyMeterInfo() {
+ return new Channel[0];
+ }
+
+ @Override
+ public CompletableFuture<EnergyMeasurement[]> readEnergyMeterAsync(
+ int[] channelIds) {
+ return new CompletableFuture<>();
+ }
+
+ public void reset() {
+ mStateResidencyResults.clear();
+ mEnergyConsumerResults.clear();
+ }
+
+ public void addPowerEntity(int id, String name) {
+ PowerEntity powerEntity = new PowerEntity();
+ powerEntity.id = id;
+ powerEntity.name = name;
+ powerEntity.states = new State[0];
+ mPowerEntities.put(id, powerEntity);
+ }
+
+ public void addPowerEntityState(int powerEntityId, int stateId, String name) {
+ State state = new State();
+ state.id = stateId;
+ state.name = name;
+
+ PowerEntity powerEntity = mPowerEntities.get(powerEntityId);
+ powerEntity.states = Arrays.copyOf(powerEntity.states, powerEntity.states.length + 1);
+ powerEntity.states[powerEntity.states.length - 1] = state;
+ }
+
+ public void addStateResidencyResult(int entityId, int stateId, long totalTimeInStateMs,
+ long totalStateEntryCount, long lastEntryTimestampMs) {
+ StateResidencyResult result = mStateResidencyResults.get(entityId);
+ if (result == null) {
+ result = new StateResidencyResult();
+ result.id = entityId;
+ result.stateResidencyData = new StateResidency[0];
+ mStateResidencyResults.put(entityId, result);
+ }
+
+ StateResidency residency = new StateResidency();
+ residency.id = stateId;
+ residency.totalTimeInStateMs = totalTimeInStateMs;
+ residency.totalStateEntryCount = totalStateEntryCount;
+ residency.lastEntryTimestampMs = lastEntryTimestampMs;
+
+ result.stateResidencyData = Arrays.copyOf(result.stateResidencyData,
+ result.stateResidencyData.length + 1);
+ result.stateResidencyData[result.stateResidencyData.length - 1] = residency;
+ }
+
+ /**
+ * Util method to add a new EnergyConsumer for testing
+ *
+ * @return the EnergyConsumer id of the new EnergyConsumer
+ */
+ public int addEnergyConsumer(@EnergyConsumerType byte type, int ordinal, String name) {
+ final EnergyConsumer consumer = new EnergyConsumer();
+ final int id = getNextAvailableId();
+ consumer.id = id;
+ consumer.type = type;
+ consumer.ordinal = ordinal;
+ consumer.name = name;
+ mEnergyConsumers.put(id, consumer);
+
+ final EnergyConsumerResult result = new EnergyConsumerResult();
+ result.id = id;
+ result.timestampMs = mTimeSinceBoot;
+ result.energyUWs = 0;
+ mEnergyConsumerResults.put(id, result);
+ return id;
+ }
+
+ public void incrementEnergyConsumption(int id, long energyUWs) {
+ EnergyConsumerResult result = mEnergyConsumerResults.get(id, null);
+ assertNotNull(result);
+ result.energyUWs += energyUWs;
+ }
+
+ private int getNextAvailableId() {
+ final int size = mEnergyConsumers.size();
+ // Just return the first index that does not match the key (aka the EnergyConsumer id)
+ for (int i = size - 1; i >= 0; i--) {
+ if (mEnergyConsumers.keyAt(i) == i) return i + 1;
+ }
+ // Otherwise return the lowest id
+ return 0;
+ }
+}
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/processor/PowerStatsAggregatorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/PowerStatsAggregatorTest.java
index 73d491c93bb5..7aa59bd03898 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/processor/PowerStatsAggregatorTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/PowerStatsAggregatorTest.java
@@ -59,9 +59,8 @@ public class PowerStatsAggregatorTest {
@Before
public void setup() throws ParseException {
- mHistory = new BatteryStatsHistory(null, 1024, null,
- mock(BatteryStatsHistory.HistoryStepDetailsCalculator.class), mClock,
- mMonotonicClock, mock(BatteryStatsHistory.TraceDelegate.class), null);
+ mHistory = new BatteryStatsHistory(null, 1024, null, mClock, mMonotonicClock,
+ mock(BatteryStatsHistory.TraceDelegate.class), null);
AggregatedPowerStatsConfig config = new AggregatedPowerStatsConfig();
config.trackPowerComponent(TEST_POWER_COMPONENT)
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/processor/PowerStatsExporterTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/PowerStatsExporterTest.java
index a5a29f5883b1..b33cb7e6739f 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/processor/PowerStatsExporterTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/PowerStatsExporterTest.java
@@ -114,8 +114,7 @@ public class PowerStatsExporterTest {
mPowerStatsStore = new PowerStatsStore(storeDirectory, new TestHandler());
mDirectory = new BatteryHistoryDirectory(storeDirectory, 0);
- mHistory = new BatteryStatsHistory(Parcel.obtain(), 10000, mDirectory,
- mock(BatteryStatsHistory.HistoryStepDetailsCalculator.class), mClock,
+ mHistory = new BatteryStatsHistory(Parcel.obtain(), 10000, mDirectory, mClock,
mMonotonicClock, null, null);
mPowerStatsAggregator = new PowerStatsAggregator(config);
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/processor/WakelockPowerStatsProcessorTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/WakelockPowerStatsProcessorTest.java
index 5ac7216194a4..b90019f9537a 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/processor/WakelockPowerStatsProcessorTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/processor/WakelockPowerStatsProcessorTest.java
@@ -29,8 +29,6 @@ import static com.android.server.power.stats.processor.AggregatedPowerStatsConfi
import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.Mockito.mock;
-
import android.annotation.SuppressLint;
import android.os.BatteryConsumer;
import android.os.PersistableBundle;
@@ -89,7 +87,6 @@ public class WakelockPowerStatsProcessorTest {
long[] uidStats = new long[descriptor.uidStatsArrayLength];
BatteryStatsHistory history = new BatteryStatsHistory(null, 10000, null,
- mock(BatteryStatsHistory.HistoryStepDetailsCalculator.class),
mStatsRule.getMockClock(),
new MonotonicClock(START_TIME, mStatsRule.getMockClock()), null, null);
history.forceRecordAllHistory();
diff --git a/services/tests/servicestests/Android.bp b/services/tests/servicestests/Android.bp
index d702cae248a9..64e6d323bdfd 100644
--- a/services/tests/servicestests/Android.bp
+++ b/services/tests/servicestests/Android.bp
@@ -31,6 +31,7 @@ android_test {
"test-apps/SuspendTestApp/src/**/*.java",
"test-apps/DisplayManagerTestApp/src/**/*.java",
+ "test-apps/TopologyTestApp/src/**/*.java",
],
static_libs: [
@@ -92,7 +93,6 @@ android_test {
"net_flags_lib",
"CtsVirtualDeviceCommonLib",
"com_android_server_accessibility_flags_lib",
- "locksettings_flags_lib",
] + select(soong_config_variable("ANDROID", "release_crashrecovery_module"), {
"true": ["service-crashrecovery-pre-jarjar"],
default: [],
@@ -141,6 +141,7 @@ android_test {
data: [
":DisplayManagerTestApp",
+ ":TopologyTestApp",
":SimpleServiceTestApp1",
":SimpleServiceTestApp2",
":SimpleServiceTestApp3",
diff --git a/services/tests/servicestests/AndroidTest.xml b/services/tests/servicestests/AndroidTest.xml
index 5298251b79f7..9a4983482522 100644
--- a/services/tests/servicestests/AndroidTest.xml
+++ b/services/tests/servicestests/AndroidTest.xml
@@ -36,6 +36,7 @@
<option name="cleanup-apks" value="true" />
<option name="install-arg" value="-t" />
<option name="test-file-name" value="DisplayManagerTestApp.apk" />
+ <option name="test-file-name" value="TopologyTestApp.apk" />
<option name="test-file-name" value="FrameworksServicesTests.apk" />
<option name="test-file-name" value="SuspendTestApp.apk" />
<option name="test-file-name" value="SimpleServiceTestApp1.apk" />
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
index 2ccd33648c3e..457d8a96fea4 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
@@ -82,10 +82,14 @@ import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
+import android.content.pm.UserInfo;
import android.content.res.XmlResourceParser;
import android.graphics.drawable.Icon;
import android.hardware.display.DisplayManager;
import android.hardware.display.DisplayManagerGlobal;
+import android.hardware.input.IInputManager;
+import android.hardware.input.InputManager;
+import android.hardware.input.InputManagerGlobal;
import android.hardware.input.KeyGestureEvent;
import android.net.Uri;
import android.os.Build;
@@ -131,6 +135,7 @@ import com.android.internal.accessibility.util.ShortcutUtils;
import com.android.internal.compat.IPlatformCompat;
import com.android.internal.content.PackageMonitor;
import com.android.server.LocalServices;
+import com.android.server.SystemService;
import com.android.server.accessibility.AccessibilityManagerService.AccessibilityDisplayListener;
import com.android.server.accessibility.magnification.FullScreenMagnificationController;
import com.android.server.accessibility.magnification.MagnificationConnectionManager;
@@ -235,6 +240,9 @@ public class AccessibilityManagerServiceTest {
@Mock
private HearingDevicePhoneCallNotificationController
mMockHearingDevicePhoneCallNotificationController;
+ @Mock
+ private IInputManager mMockInputManagerService;
+ private InputManagerGlobal.TestSession mInputManagerTestSession;
@Spy private IUserInitializationCompleteCallback mUserInitializationCompleteCallback;
@Captor private ArgumentCaptor<Intent> mIntentArgumentCaptor;
private IAccessibilityManager mA11yManagerServiceOnDevice;
@@ -268,6 +276,10 @@ public class AccessibilityManagerServiceTest {
mInputFilter = mock(FakeInputFilter.class);
mTestableContext.addMockSystemService(DevicePolicyManager.class, mDevicePolicyManager);
+ mInputManagerTestSession = InputManagerGlobal.createTestSession(mMockInputManagerService);
+ InputManager mockInputManager = new InputManager(mTestableContext);
+ mTestableContext.addMockSystemService(InputManager.class, mockInputManager);
+
when(mMockPackageManagerInternal.getSystemUiServiceComponent()).thenReturn(
new ComponentName("com.android.systemui", "com.android.systemui.SystemUIService"));
when(mMockPackageManagerInternal.getPackageUid(eq("com.android.systemui"), anyLong(),
@@ -332,6 +344,9 @@ public class AccessibilityManagerServiceTest {
FieldSetter.setField(
am, AccessibilityManager.class.getDeclaredField("mService"),
mA11yManagerServiceOnDevice);
+ if (mInputManagerTestSession != null) {
+ mInputManagerTestSession.close();
+ }
}
private void setupAccessibilityServiceConnection(int serviceInfoFlag) {
@@ -2122,6 +2137,36 @@ public class AccessibilityManagerServiceTest {
}
@Test
+ @EnableFlags(Flags.FLAG_MANAGER_LIFECYCLE_USER_CHANGE)
+ public void lifecycle_onUserSwitching_switchesUser() throws RemoteException {
+ mA11yms.mUserInitializationCompleteCallbacks.add(mUserInitializationCompleteCallback);
+ AccessibilityManagerService.Lifecycle lifecycle =
+ new AccessibilityManagerService.Lifecycle(mTestableContext, mA11yms);
+ int newUserId = mA11yms.getCurrentUserIdLocked() + 1;
+
+ lifecycle.onUserSwitching(
+ new SystemService.TargetUser(new UserInfo(0, "USER", 0)),
+ new SystemService.TargetUser(new UserInfo(newUserId, "USER", 0)));
+ mTestableLooper.processAllMessages();
+
+ verify(mUserInitializationCompleteCallback).onUserInitializationComplete(newUserId);
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_MANAGER_LIFECYCLE_USER_CHANGE)
+ public void intent_user_switched_switchesUser() throws RemoteException {
+ mA11yms.mUserInitializationCompleteCallbacks.add(mUserInitializationCompleteCallback);
+ int newUserId = mA11yms.getCurrentUserIdLocked() + 1;
+ final Intent intent = new Intent(Intent.ACTION_USER_SWITCHED);
+ intent.putExtra(Intent.EXTRA_USER_HANDLE, newUserId);
+
+ sendBroadcastToAccessibilityManagerService(intent, mA11yms.getCurrentUserIdLocked());
+ mTestableLooper.processAllMessages();
+
+ verify(mUserInitializationCompleteCallback).onUserInitializationComplete(newUserId);
+ }
+
+ @Test
@DisableFlags(android.provider.Flags.FLAG_A11Y_STANDALONE_GESTURE_ENABLED)
public void getShortcutTypeForGenericShortcutCalls_softwareType() {
final AccessibilityUserState userState = new AccessibilityUserState(
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/HearingDevicePhoneCallNotificationControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/HearingDevicePhoneCallNotificationControllerTest.java
index 3565244d90b3..44870eb5dd49 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/HearingDevicePhoneCallNotificationControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/HearingDevicePhoneCallNotificationControllerTest.java
@@ -25,6 +25,7 @@ import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -36,7 +37,6 @@ import android.content.Context;
import android.media.AudioDeviceInfo;
import android.media.AudioDevicePort;
import android.media.AudioManager;
-import android.platform.test.annotations.EnableFlags;
import android.platform.test.flag.junit.SetFlagsRule;
import android.telephony.TelephonyCallback;
import android.telephony.TelephonyManager;
@@ -163,7 +163,6 @@ public class HearingDevicePhoneCallNotificationControllerTest {
}
@Test
- @EnableFlags(Flags.FLAG_HEARING_INPUT_CHANGE_WHEN_COMM_DEVICE)
public void onCallStateChanged_nonHearingDevice_offHookThenIdle_callAddAndRemoveListener() {
final ArgumentCaptor<AudioManager.OnCommunicationDeviceChangedListener> listenerCaptor =
ArgumentCaptor.forClass(AudioManager.OnCommunicationDeviceChangedListener.class);
@@ -184,7 +183,6 @@ public class HearingDevicePhoneCallNotificationControllerTest {
@Test
- @EnableFlags(Flags.FLAG_HEARING_INPUT_CHANGE_WHEN_COMM_DEVICE)
public void onCallStateChanged_hearingDeviceFromCommunicationDeviceChanged_showNotification() {
final ArgumentCaptor<AudioManager.OnCommunicationDeviceChangedListener> listenerCaptor =
ArgumentCaptor.forClass(AudioManager.OnCommunicationDeviceChangedListener.class);
@@ -207,6 +205,22 @@ public class HearingDevicePhoneCallNotificationControllerTest {
eq(SystemMessageProto.SystemMessage.NOTE_HEARING_DEVICE_INPUT_SWITCH), any());
}
+ @Test
+ public void onCallStateChanged_offHookMultiple_addListenerOnlyOneTime() {
+ AudioDeviceInfo a2dpDeviceInfo = createAudioDeviceInfo(TEST_ADDRESS,
+ AudioManager.DEVICE_OUT_BLUETOOTH_A2DP);
+ when(mAudioManager.getDevices(AudioManager.GET_DEVICES_INPUTS)).thenReturn(
+ new AudioDeviceInfo[]{a2dpDeviceInfo});
+ when(mAudioManager.getCommunicationDevice()).thenReturn(a2dpDeviceInfo);
+
+ mTestCallStateListener.onCallStateChanged(TelephonyManager.CALL_STATE_OFFHOOK);
+ mTestCallStateListener.onCallStateChanged(TelephonyManager.CALL_STATE_OFFHOOK);
+
+ verify(mAudioManager, times(1)).addOnCommunicationDeviceChangedListener(
+ any(Executor.class),
+ any(AudioManager.OnCommunicationDeviceChangedListener.class));
+ }
+
private AudioDeviceInfo createAudioDeviceInfo(String address, int type) {
AudioDevicePort audioDevicePort = mock(AudioDevicePort.class);
doReturn(type).when(audioDevicePort).type();
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/autoclick/AutoclickControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/autoclick/AutoclickControllerTest.java
index 99c922ca30c4..df77866b5e7f 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/autoclick/AutoclickControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/autoclick/AutoclickControllerTest.java
@@ -25,6 +25,7 @@ import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -866,6 +867,23 @@ public class AutoclickControllerTest {
@Test
@EnableFlags(com.android.server.accessibility.Flags.FLAG_ENABLE_AUTOCLICK_INDICATOR)
+ public void scrollPanelController_directionalButtonsHideIndicator() {
+ injectFakeMouseActionHoverMoveEvent();
+
+ // Create a spy on the real object to verify method calls.
+ AutoclickIndicatorView spyIndicatorView = spy(mController.mAutoclickIndicatorView);
+ mController.mAutoclickIndicatorView = spyIndicatorView;
+
+ // Simulate hover on direction button.
+ mController.mScrollPanelController.onHoverButtonChange(
+ AutoclickScrollPanel.DIRECTION_UP, true);
+
+ // Verify clearIndicator was called.
+ verify(spyIndicatorView).clearIndicator();
+ }
+
+ @Test
+ @EnableFlags(com.android.server.accessibility.Flags.FLAG_ENABLE_AUTOCLICK_INDICATOR)
public void hoverOnAutoclickPanel_rightClickType_forceTriggerLeftClick() {
MotionEventCaptor motionEventCaptor = new MotionEventCaptor();
mController.setNext(motionEventCaptor);
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/TwoFingersDownOrSwipeTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/TwoFingersDownOrSwipeTest.java
index d94faec4cf01..49eb63b2c261 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/TwoFingersDownOrSwipeTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/TwoFingersDownOrSwipeTest.java
@@ -28,10 +28,6 @@ import static org.mockito.Mockito.verify;
import android.content.Context;
import android.graphics.PointF;
-import android.platform.test.annotations.RequiresFlagsDisabled;
-import android.platform.test.annotations.RequiresFlagsEnabled;
-import android.platform.test.flag.junit.CheckFlagsRule;
-import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.view.Display;
import android.view.MotionEvent;
import android.view.ViewConfiguration;
@@ -54,9 +50,6 @@ import java.util.List;
*/
public class TwoFingersDownOrSwipeTest {
- @Rule
- public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
-
private static final float DEFAULT_X = 100f;
private static final float DEFAULT_Y = 100f;
@@ -94,22 +87,6 @@ public class TwoFingersDownOrSwipeTest {
}
@Test
- @RequiresFlagsDisabled(android.view.accessibility.Flags.FLAG_COPY_EVENTS_FOR_GESTURE_DETECTION)
- public void sendTwoFingerDownEvent_onGestureCompleted_withoutCopiedEvents() {
- final List<MotionEvent> downEvents = twoPointersDownEvents(Display.DEFAULT_DISPLAY,
- new PointF(DEFAULT_X, DEFAULT_Y), new PointF(DEFAULT_X + 10, DEFAULT_Y + 10));
-
- for (MotionEvent event : downEvents) {
- mGesturesObserver.onMotionEvent(event, event, 0);
- }
-
- verify(mListener, timeout(sTimeoutMillis)).onGestureCompleted(
- MagnificationGestureMatcher.GESTURE_TWO_FINGERS_DOWN_OR_SWIPE, downEvents.get(1),
- downEvents.get(1), 0);
- }
-
- @Test
- @RequiresFlagsEnabled(android.view.accessibility.Flags.FLAG_COPY_EVENTS_FOR_GESTURE_DETECTION)
public void sendTwoFingerDownEvent_onGestureCompleted() {
final List<MotionEvent> downEvents = twoPointersDownEvents(Display.DEFAULT_DISPLAY,
new PointF(DEFAULT_X, DEFAULT_Y), new PointF(DEFAULT_X + 10, DEFAULT_Y + 10));
diff --git a/services/tests/servicestests/src/com/android/server/audio/AudioVolumeChangeHandlerTest.java b/services/tests/servicestests/src/com/android/server/audio/AudioVolumeChangeHandlerTest.java
new file mode 100644
index 000000000000..f252a9848bbf
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/audio/AudioVolumeChangeHandlerTest.java
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2025 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.server.audio;
+
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import static org.junit.Assert.assertThrows;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.timeout;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.media.INativeAudioVolumeGroupCallback;
+import android.media.audio.common.AudioVolumeGroupChangeEvent;
+import android.media.audiopolicy.IAudioVolumeChangeDispatcher;
+import android.os.IBinder;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.filters.MediumTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@MediumTest
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+public class AudioVolumeChangeHandlerTest {
+ private static final long DEFAULT_TIMEOUT_MS = 1000;
+
+ private AudioSystemAdapter mSpyAudioSystem;
+
+ AudioVolumeChangeHandler mAudioVolumeChangedHandler;
+
+ private final IAudioVolumeChangeDispatcher.Stub mMockDispatcher =
+ mock(IAudioVolumeChangeDispatcher.Stub.class);
+
+ @Before
+ public void setUp() {
+ mSpyAudioSystem = spy(new NoOpAudioSystemAdapter());
+ when(mMockDispatcher.asBinder()).thenReturn(mock(IBinder.class));
+ mAudioVolumeChangedHandler = new AudioVolumeChangeHandler(mSpyAudioSystem);
+ }
+
+ @Test
+ public void registerListener_withInvalidCallback() {
+ IAudioVolumeChangeDispatcher.Stub nullCb = null;
+ NullPointerException thrown = assertThrows(NullPointerException.class, () -> {
+ mAudioVolumeChangedHandler.registerListener(nullCb);
+ });
+
+ assertWithMessage("Exception for invalid registration").that(thrown).hasMessageThat()
+ .contains("Volume group callback");
+ }
+
+ @Test
+ public void unregisterListener_withInvalidCallback() {
+ IAudioVolumeChangeDispatcher.Stub nullCb = null;
+ mAudioVolumeChangedHandler.registerListener(mMockDispatcher);
+
+ NullPointerException thrown = assertThrows(NullPointerException.class, () -> {
+ mAudioVolumeChangedHandler.unregisterListener(nullCb);
+ });
+
+ assertWithMessage("Exception for invalid un-registration").that(thrown).hasMessageThat()
+ .contains("Volume group callback");
+ }
+
+ @Test
+ public void registerListener() {
+ mAudioVolumeChangedHandler.registerListener(mMockDispatcher);
+
+ verify(mSpyAudioSystem).registerAudioVolumeGroupCallback(any());
+ }
+
+ @Test
+ public void onAudioVolumeGroupChanged() throws Exception {
+ mAudioVolumeChangedHandler.registerListener(mMockDispatcher);
+ AudioVolumeGroupChangeEvent volEvent = new AudioVolumeGroupChangeEvent();
+ volEvent.groupId = 666;
+ volEvent.flags = AudioVolumeGroupChangeEvent.VOLUME_FLAG_FROM_KEY;
+
+ captureRegisteredNativeCallback().onAudioVolumeGroupChanged(volEvent);
+
+ verify(mMockDispatcher, timeout(DEFAULT_TIMEOUT_MS)).onAudioVolumeGroupChanged(
+ eq(volEvent.groupId), eq(volEvent.flags));
+ }
+
+ @Test
+ public void onAudioVolumeGroupChanged_withMultipleCallback() throws Exception {
+ int callbackCount = 10;
+ List<IAudioVolumeChangeDispatcher.Stub> validCbs =
+ new ArrayList<IAudioVolumeChangeDispatcher.Stub>();
+ for (int i = 0; i < callbackCount; i++) {
+ IAudioVolumeChangeDispatcher.Stub cb = mock(IAudioVolumeChangeDispatcher.Stub.class);
+ when(cb.asBinder()).thenReturn(mock(IBinder.class));
+ validCbs.add(cb);
+ }
+ for (IAudioVolumeChangeDispatcher.Stub cb : validCbs) {
+ mAudioVolumeChangedHandler.registerListener(cb);
+ }
+ AudioVolumeGroupChangeEvent volEvent = new AudioVolumeGroupChangeEvent();
+ volEvent.groupId = 666;
+ volEvent.flags = AudioVolumeGroupChangeEvent.VOLUME_FLAG_FROM_KEY;
+ captureRegisteredNativeCallback().onAudioVolumeGroupChanged(volEvent);
+
+ for (IAudioVolumeChangeDispatcher.Stub cb : validCbs) {
+ verify(cb, timeout(DEFAULT_TIMEOUT_MS)).onAudioVolumeGroupChanged(
+ eq(volEvent.groupId), eq(volEvent.flags));
+ }
+ }
+
+ private INativeAudioVolumeGroupCallback captureRegisteredNativeCallback() {
+ ArgumentCaptor<INativeAudioVolumeGroupCallback> nativeAudioVolumeGroupCallbackCaptor =
+ ArgumentCaptor.forClass(INativeAudioVolumeGroupCallback.class);
+ verify(mSpyAudioSystem, timeout(DEFAULT_TIMEOUT_MS))
+ .registerAudioVolumeGroupCallback(nativeAudioVolumeGroupCallbackCaptor.capture());
+ return nativeAudioVolumeGroupCallbackCaptor.getValue();
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index c50c62323212..a1f73170e549 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -816,14 +816,14 @@ public class DevicePolicyManagerTest extends DpmTestBase {
MockUtils.checkIntentAction(
DeviceAdminReceiver.ACTION_DEVICE_ADMIN_DISABLED),
MockUtils.checkUserHandle(CALLER_USER_HANDLE),
- isNull(String.class),
+ isNull(),
eq(AppOpsManager.OP_NONE),
any(Bundle.class),
any(BroadcastReceiver.class),
eq(dpms.mHandler),
eq(Activity.RESULT_OK),
- isNull(String.class),
- isNull(Bundle.class));
+ isNull(),
+ isNull());
assertThat(dpm.isAdminActiveAsUser(admin1, CALLER_USER_HANDLE)).isFalse();
verify(getServices().usageStatsManagerInternal).setActiveAdminApps(
@@ -873,14 +873,14 @@ public class DevicePolicyManagerTest extends DpmTestBase {
MockUtils.checkIntentAction(
DeviceAdminReceiver.ACTION_DEVICE_ADMIN_DISABLED),
MockUtils.checkUserHandle(CALLER_USER_HANDLE),
- isNull(String.class),
+ isNull(),
eq(AppOpsManager.OP_NONE),
any(Bundle.class),
any(BroadcastReceiver.class),
eq(dpms.mHandler),
eq(Activity.RESULT_OK),
- isNull(String.class),
- isNull(Bundle.class));
+ isNull(),
+ isNull());
assertThat(dpm.isAdminActiveAsUser(admin1, CALLER_USER_HANDLE)).isFalse();
verify(getServices().usageStatsManagerInternal).setActiveAdminApps(
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/BaseAbsoluteVolumeBehaviorTest.java b/services/tests/servicestests/src/com/android/server/hdmi/BaseAbsoluteVolumeBehaviorTest.java
index b2d48a77386f..2349120f8e76 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/BaseAbsoluteVolumeBehaviorTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/BaseAbsoluteVolumeBehaviorTest.java
@@ -226,13 +226,16 @@ public abstract class BaseAbsoluteVolumeBehaviorTest {
*/
protected void adoptFullVolumeBehaviorOnAvbCapableAudioOutputDevices() {
if (getDeviceType() == HdmiDeviceInfo.DEVICE_PLAYBACK) {
- mAudioManager.setDeviceVolumeBehavior(HdmiControlService.AUDIO_OUTPUT_DEVICE_HDMI,
- AudioManager.DEVICE_VOLUME_BEHAVIOR_FULL);
+ mAudioDeviceVolumeManager.setDeviceVolumeBehavior(
+ HdmiControlService.AUDIO_OUTPUT_DEVICE_HDMI,
+ AudioDeviceVolumeManager.DEVICE_VOLUME_BEHAVIOR_FULL);
} else if (getDeviceType() == HdmiDeviceInfo.DEVICE_TV) {
- mAudioManager.setDeviceVolumeBehavior(HdmiControlService.AUDIO_OUTPUT_DEVICE_HDMI_ARC,
- AudioManager.DEVICE_VOLUME_BEHAVIOR_FULL);
- mAudioManager.setDeviceVolumeBehavior(HdmiControlService.AUDIO_OUTPUT_DEVICE_HDMI_EARC,
- AudioManager.DEVICE_VOLUME_BEHAVIOR_FULL);
+ mAudioDeviceVolumeManager.setDeviceVolumeBehavior(
+ HdmiControlService.AUDIO_OUTPUT_DEVICE_HDMI_ARC,
+ AudioDeviceVolumeManager.DEVICE_VOLUME_BEHAVIOR_FULL);
+ mAudioDeviceVolumeManager.setDeviceVolumeBehavior(
+ HdmiControlService.AUDIO_OUTPUT_DEVICE_HDMI_EARC,
+ AudioDeviceVolumeManager.DEVICE_VOLUME_BEHAVIOR_FULL);
}
}
@@ -307,8 +310,9 @@ public abstract class BaseAbsoluteVolumeBehaviorTest {
INITIAL_SYSTEM_AUDIO_DEVICE_STATUS.getVolume(),
INITIAL_SYSTEM_AUDIO_DEVICE_STATUS.getMute());
- assertThat(mAudioManager.getDeviceVolumeBehavior(getAudioOutputDevice())).isEqualTo(
- AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE);
+ assertThat(mAudioDeviceVolumeManager.getDeviceVolumeBehavior(
+ getAudioOutputDevice())).isEqualTo(
+ AudioDeviceVolumeManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE);
}
protected void enableAdjustOnlyAbsoluteVolumeBehavior() {
@@ -320,8 +324,9 @@ public abstract class BaseAbsoluteVolumeBehaviorTest {
INITIAL_SYSTEM_AUDIO_DEVICE_STATUS.getVolume(),
INITIAL_SYSTEM_AUDIO_DEVICE_STATUS.getMute());
- assertThat(mAudioManager.getDeviceVolumeBehavior(getAudioOutputDevice())).isEqualTo(
- AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_ADJUST_ONLY);
+ assertThat(mAudioDeviceVolumeManager.getDeviceVolumeBehavior(
+ getAudioOutputDevice())).isEqualTo(
+ AudioDeviceVolumeManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_ADJUST_ONLY);
}
protected void verifyGiveAudioStatusNeverSent() {
@@ -419,14 +424,16 @@ public abstract class BaseAbsoluteVolumeBehaviorTest {
receiveSetAudioVolumeLevelSupport(DeviceFeatures.FEATURE_SUPPORTED);
// AVB should not be enabled before receiving <Report Audio Status>
- assertThat(mAudioManager.getDeviceVolumeBehavior(getAudioOutputDevice())).isEqualTo(
- AudioManager.DEVICE_VOLUME_BEHAVIOR_FULL);
+ assertThat(mAudioDeviceVolumeManager.getDeviceVolumeBehavior(
+ getAudioOutputDevice())).isEqualTo(
+ AudioDeviceVolumeManager.DEVICE_VOLUME_BEHAVIOR_FULL);
receiveReportAudioStatus(60, false);
// Check that absolute volume behavior was the last one adopted
- assertThat(mAudioManager.getDeviceVolumeBehavior(getAudioOutputDevice())).isEqualTo(
- AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE);
+ assertThat(mAudioDeviceVolumeManager.getDeviceVolumeBehavior(
+ getAudioOutputDevice())).isEqualTo(
+ AudioDeviceVolumeManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE);
// Check that the volume and mute status received were included when setting AVB
verify(mAudioDeviceVolumeManager).setDeviceAbsoluteVolumeBehavior(
@@ -447,19 +454,22 @@ public abstract class BaseAbsoluteVolumeBehaviorTest {
enableSystemAudioModeIfNeeded();
receiveSetAudioVolumeLevelSupport(DeviceFeatures.FEATURE_SUPPORTED);
- assertThat(mAudioManager.getDeviceVolumeBehavior(getAudioOutputDevice())).isEqualTo(
- AudioManager.DEVICE_VOLUME_BEHAVIOR_FULL);
+ assertThat(mAudioDeviceVolumeManager.getDeviceVolumeBehavior(
+ getAudioOutputDevice())).isEqualTo(
+ AudioDeviceVolumeManager.DEVICE_VOLUME_BEHAVIOR_FULL);
receiveReportAudioStatus(127, false);
- assertThat(mAudioManager.getDeviceVolumeBehavior(getAudioOutputDevice())).isEqualTo(
- AudioManager.DEVICE_VOLUME_BEHAVIOR_FULL);
+ assertThat(mAudioDeviceVolumeManager.getDeviceVolumeBehavior(
+ getAudioOutputDevice())).isEqualTo(
+ AudioDeviceVolumeManager.DEVICE_VOLUME_BEHAVIOR_FULL);
}
@Test
public void avbEnabled_standby_avbDisabled() {
enableAbsoluteVolumeBehavior();
mHdmiControlService.onStandby(HdmiControlService.STANDBY_SCREEN_OFF);
- assertThat(mAudioManager.getDeviceVolumeBehavior(getAudioOutputDevice())).isEqualTo(
- AudioManager.DEVICE_VOLUME_BEHAVIOR_FULL);
+ assertThat(mAudioDeviceVolumeManager.getDeviceVolumeBehavior(
+ getAudioOutputDevice())).isEqualTo(
+ AudioDeviceVolumeManager.DEVICE_VOLUME_BEHAVIOR_FULL);
}
@Test
@@ -468,8 +478,9 @@ public abstract class BaseAbsoluteVolumeBehaviorTest {
setCecVolumeControlSetting(HdmiControlManager.VOLUME_CONTROL_DISABLED);
- assertThat(mAudioManager.getDeviceVolumeBehavior(getAudioOutputDevice())).isEqualTo(
- AudioManager.DEVICE_VOLUME_BEHAVIOR_FULL);
+ assertThat(mAudioDeviceVolumeManager.getDeviceVolumeBehavior(
+ getAudioOutputDevice())).isEqualTo(
+ AudioDeviceVolumeManager.DEVICE_VOLUME_BEHAVIOR_FULL);
}
@Test
@@ -477,8 +488,9 @@ public abstract class BaseAbsoluteVolumeBehaviorTest {
enableAbsoluteVolumeBehavior();
receiveSetAudioVolumeLevelSupport(DeviceFeatures.FEATURE_NOT_SUPPORTED);
- assertThat(mAudioManager.getDeviceVolumeBehavior(getAudioOutputDevice())).isEqualTo(
- AudioManager.DEVICE_VOLUME_BEHAVIOR_FULL);
+ assertThat(mAudioDeviceVolumeManager.getDeviceVolumeBehavior(
+ getAudioOutputDevice())).isEqualTo(
+ AudioDeviceVolumeManager.DEVICE_VOLUME_BEHAVIOR_FULL);
}
@Test
@@ -489,8 +501,9 @@ public abstract class BaseAbsoluteVolumeBehaviorTest {
getSystemAudioDeviceLogicalAddress(), getLogicalAddress(),
Constants.MESSAGE_SET_AUDIO_VOLUME_LEVEL, Constants.ABORT_UNRECOGNIZED_OPCODE));
mTestLooper.dispatchAll();
- assertThat(mAudioManager.getDeviceVolumeBehavior(getAudioOutputDevice())).isEqualTo(
- AudioManager.DEVICE_VOLUME_BEHAVIOR_FULL);
+ assertThat(mAudioDeviceVolumeManager.getDeviceVolumeBehavior(
+ getAudioOutputDevice())).isEqualTo(
+ AudioDeviceVolumeManager.DEVICE_VOLUME_BEHAVIOR_FULL);
}
@Test
@@ -501,8 +514,9 @@ public abstract class BaseAbsoluteVolumeBehaviorTest {
enableAbsoluteVolumeBehavior();
receiveSetSystemAudioMode(false);
- assertThat(mAudioManager.getDeviceVolumeBehavior(getAudioOutputDevice())).isEqualTo(
- AudioManager.DEVICE_VOLUME_BEHAVIOR_FULL);
+ assertThat(mAudioDeviceVolumeManager.getDeviceVolumeBehavior(
+ getAudioOutputDevice())).isEqualTo(
+ AudioDeviceVolumeManager.DEVICE_VOLUME_BEHAVIOR_FULL);
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/BasePlaybackDeviceAvbTest.java b/services/tests/servicestests/src/com/android/server/hdmi/BasePlaybackDeviceAvbTest.java
index 4c12e436542b..7c7e2207c59c 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/BasePlaybackDeviceAvbTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/BasePlaybackDeviceAvbTest.java
@@ -20,7 +20,7 @@ import android.hardware.hdmi.DeviceFeatures;
import android.hardware.hdmi.HdmiControlManager;
import android.hardware.hdmi.HdmiDeviceInfo;
import android.media.AudioDeviceAttributes;
-import android.media.AudioManager;
+import android.media.AudioDeviceVolumeManager;
import org.junit.Test;
@@ -60,8 +60,8 @@ public abstract class BasePlaybackDeviceAvbTest extends BaseAbsoluteVolumeBehavi
*/
@Test
public void savlNotSupported_allOtherConditionsMet_giveAudioStatusNotSent() {
- mAudioManager.setDeviceVolumeBehavior(getAudioOutputDevice(),
- AudioManager.DEVICE_VOLUME_BEHAVIOR_FULL);
+ mAudioDeviceVolumeManager.setDeviceVolumeBehavior(getAudioOutputDevice(),
+ AudioDeviceVolumeManager.DEVICE_VOLUME_BEHAVIOR_FULL);
setCecVolumeControlSetting(HdmiControlManager.VOLUME_CONTROL_ENABLED);
enableSystemAudioModeIfNeeded();
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/BaseTvToAudioSystemAvbTest.java b/services/tests/servicestests/src/com/android/server/hdmi/BaseTvToAudioSystemAvbTest.java
index f44517a47f55..7a4359889994 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/BaseTvToAudioSystemAvbTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/BaseTvToAudioSystemAvbTest.java
@@ -96,13 +96,15 @@ public abstract class BaseTvToAudioSystemAvbTest extends BaseAbsoluteVolumeBehav
receiveSetAudioVolumeLevelSupport(DeviceFeatures.FEATURE_NOT_SUPPORTED);
// Adjust-only AVB should not be enabled before receiving <Report Audio Status>
- assertThat(mAudioManager.getDeviceVolumeBehavior(getAudioOutputDevice())).isEqualTo(
- AudioManager.DEVICE_VOLUME_BEHAVIOR_FULL);
+ assertThat(mAudioDeviceVolumeManager.getDeviceVolumeBehavior(
+ getAudioOutputDevice())).isEqualTo(
+ AudioDeviceVolumeManager.DEVICE_VOLUME_BEHAVIOR_FULL);
receiveReportAudioStatus(20, false);
- assertThat(mAudioManager.getDeviceVolumeBehavior(getAudioOutputDevice())).isEqualTo(
- AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_ADJUST_ONLY);
+ assertThat(mAudioDeviceVolumeManager.getDeviceVolumeBehavior(
+ getAudioOutputDevice())).isEqualTo(
+ AudioDeviceVolumeManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_ADJUST_ONLY);
verify(mAudioDeviceVolumeManager).setDeviceAbsoluteVolumeAdjustOnlyBehavior(
eq(getAudioOutputDevice()),
@@ -124,8 +126,9 @@ public abstract class BaseTvToAudioSystemAvbTest extends BaseAbsoluteVolumeBehav
receiveReportAudioStatus(40, true);
- assertThat(mAudioManager.getDeviceVolumeBehavior(getAudioOutputDevice())).isEqualTo(
- AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_ADJUST_ONLY);
+ assertThat(mAudioDeviceVolumeManager.getDeviceVolumeBehavior(
+ getAudioOutputDevice())).isEqualTo(
+ AudioDeviceVolumeManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_ADJUST_ONLY);
verify(mAudioDeviceVolumeManager).setDeviceAbsoluteVolumeAdjustOnlyBehavior(
eq(getAudioOutputDevice()),
@@ -149,8 +152,9 @@ public abstract class BaseTvToAudioSystemAvbTest extends BaseAbsoluteVolumeBehav
receiveReportAudioStatus(40, true);
- assertThat(mAudioManager.getDeviceVolumeBehavior(getAudioOutputDevice())).isEqualTo(
- AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_ADJUST_ONLY);
+ assertThat(mAudioDeviceVolumeManager.getDeviceVolumeBehavior(
+ getAudioOutputDevice())).isEqualTo(
+ AudioDeviceVolumeManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_ADJUST_ONLY);
verify(mAudioDeviceVolumeManager).setDeviceAbsoluteVolumeAdjustOnlyBehavior(
eq(getAudioOutputDevice()),
@@ -283,13 +287,15 @@ public abstract class BaseTvToAudioSystemAvbTest extends BaseAbsoluteVolumeBehav
verifyGiveAudioStatusSent();
// The device should use adjust-only AVB while waiting for <Report Audio Status>
- assertThat(mAudioManager.getDeviceVolumeBehavior(getAudioOutputDevice())).isEqualTo(
- AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_ADJUST_ONLY);
+ assertThat(mAudioDeviceVolumeManager.getDeviceVolumeBehavior(
+ getAudioOutputDevice())).isEqualTo(
+ AudioDeviceVolumeManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_ADJUST_ONLY);
// The device should switch to AVB upon receiving <Report Audio Status>
receiveReportAudioStatus(60, false);
- assertThat(mAudioManager.getDeviceVolumeBehavior(getAudioOutputDevice())).isEqualTo(
- AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE);
+ assertThat(mAudioDeviceVolumeManager.getDeviceVolumeBehavior(
+ getAudioOutputDevice())).isEqualTo(
+ AudioDeviceVolumeManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE);
}
@@ -321,8 +327,9 @@ public abstract class BaseTvToAudioSystemAvbTest extends BaseAbsoluteVolumeBehav
mTestLooper.dispatchAll();
// The device should not switch away from adjust-only AVB
- assertThat(mAudioManager.getDeviceVolumeBehavior(getAudioOutputDevice())).isEqualTo(
- AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_ADJUST_ONLY);
+ assertThat(mAudioDeviceVolumeManager.getDeviceVolumeBehavior(
+ getAudioOutputDevice())).isEqualTo(
+ AudioDeviceVolumeManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_ADJUST_ONLY);
// The device should query support for <Set Audio Volume Level> again
assertThat(mNativeWrapper.getResultMessages()).contains(
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/DeviceSelectActionFromTvTest.java b/services/tests/servicestests/src/com/android/server/hdmi/DeviceSelectActionFromTvTest.java
index a4c71bd6094e..2227eebdb128 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/DeviceSelectActionFromTvTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/DeviceSelectActionFromTvTest.java
@@ -251,6 +251,35 @@ public class DeviceSelectActionFromTvTest {
}
@Test
+ public void testDeviceSelect_DeviceAssertsActiveSource_singleSetStreamPathMessage() {
+ // TV was watching playback2 device connected at port 2, and wants to select
+ // playback1.
+ TestActionTimer actionTimer = new TestActionTimer();
+ TestCallback callback = new TestCallback();
+ DeviceSelectActionFromTv action = createDeviceSelectAction(actionTimer, callback,
+ /*isCec20=*/false);
+ mHdmiCecLocalDeviceTv.updateActiveSource(ADDR_PLAYBACK_2, PHYSICAL_ADDRESS_PLAYBACK_2,
+ "testDeviceSelect");
+ action.start();
+ mTestLooper.dispatchAll();
+
+ assertThat(mNativeWrapper.getResultMessages()).contains(SET_STREAM_PATH);
+ mNativeWrapper.clearResultMessages();
+ mHdmiCecLocalDeviceTv.updateActiveSource(ADDR_PLAYBACK_1, PHYSICAL_ADDRESS_PLAYBACK_1,
+ "testDeviceSelect");
+ mTestLooper.dispatchAll();
+
+ assertThat(actionTimer.getState()).isEqualTo(STATE_WAIT_FOR_POWER_STATE_CHANGE);
+ action.handleTimerEvent(STATE_WAIT_FOR_POWER_STATE_CHANGE);
+ assertThat(actionTimer.getState()).isEqualTo(STATE_WAIT_FOR_REPORT_POWER_STATUS);
+ action.processCommand(REPORT_POWER_STATUS_ON);
+ mTestLooper.dispatchAll();
+
+ assertThat(mNativeWrapper.getResultMessages()).doesNotContain(SET_STREAM_PATH);
+ assertThat(callback.getResult()).isEqualTo(HdmiControlManager.RESULT_SUCCESS);
+ }
+
+ @Test
public void testDeviceSelect_DeviceInStandbyStatus_Cec14b() {
mHdmiCecLocalDeviceTv.updateActiveSource(ADDR_PLAYBACK_2, PHYSICAL_ADDRESS_PLAYBACK_2,
"testDeviceSelect");
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/FakeAudioFramework.java b/services/tests/servicestests/src/com/android/server/hdmi/FakeAudioFramework.java
index 90f94cb4b596..e07f4f92084e 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/FakeAudioFramework.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/FakeAudioFramework.java
@@ -23,6 +23,7 @@ import android.annotation.CallbackExecutor;
import android.annotation.NonNull;
import android.media.AudioAttributes;
import android.media.AudioDeviceAttributes;
+import android.media.AudioDeviceVolumeManager;
import android.media.AudioManager;
import android.media.AudioSystem;
import android.media.VolumeInfo;
@@ -137,18 +138,6 @@ public class FakeAudioFramework {
// Do nothing
}
-
- @Override
- @AudioManager.DeviceVolumeBehavior
- public int getDeviceVolumeBehavior(@NonNull AudioDeviceAttributes device) {
- return mDeviceVolumeBehaviors.getOrDefault(device, DEFAULT_DEVICE_VOLUME_BEHAVIOR);
- }
-
- public void setDeviceVolumeBehavior(@NonNull AudioDeviceAttributes device,
- @AudioManager.DeviceVolumeBehavior int deviceVolumeBehavior) {
- setVolumeBehaviorHelper(device, deviceVolumeBehavior);
- }
-
@Override
@NonNull
public List<AudioDeviceAttributes> getDevicesForAttributes(
@@ -186,7 +175,8 @@ public class FakeAudioFramework {
boolean handlesVolumeAdjustment,
@NonNull @CallbackExecutor Executor executor,
@NonNull OnAudioDeviceVolumeChangedListener vclistener) {
- setVolumeBehaviorHelper(device, AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE);
+ setVolumeBehaviorHelper(device,
+ AudioDeviceVolumeManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE);
}
@Override
@@ -197,7 +187,19 @@ public class FakeAudioFramework {
@NonNull @CallbackExecutor Executor executor,
@NonNull OnAudioDeviceVolumeChangedListener vclistener) {
setVolumeBehaviorHelper(device,
- AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_ADJUST_ONLY);
+ AudioDeviceVolumeManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_ADJUST_ONLY);
+ }
+
+ @Override
+ @AudioDeviceVolumeManager.DeviceVolumeBehavior
+ public int getDeviceVolumeBehavior(@NonNull AudioDeviceAttributes device) {
+ return mDeviceVolumeBehaviors.getOrDefault(device, DEFAULT_DEVICE_VOLUME_BEHAVIOR);
+ }
+
+ @Override
+ public void setDeviceVolumeBehavior(@NonNull AudioDeviceAttributes device,
+ @AudioDeviceVolumeManager.DeviceVolumeBehavior int deviceVolumeBehavior) {
+ setVolumeBehaviorHelper(device, deviceVolumeBehavior);
}
}
@@ -222,7 +224,7 @@ public class FakeAudioFramework {
* Helper method for changing an audio device's volume behavior. Notifies listeners.
*/
private void setVolumeBehaviorHelper(AudioDeviceAttributes device,
- @AudioManager.DeviceVolumeBehavior int newVolumeBehavior) {
+ @AudioDeviceVolumeManager.DeviceVolumeBehavior int newVolumeBehavior) {
int currentVolumeBehavior = mDeviceVolumeBehaviors.getOrDefault(
device, DEFAULT_DEVICE_VOLUME_BEHAVIOR);
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/PlaybackDeviceToAudioSystemAvbTest.java b/services/tests/servicestests/src/com/android/server/hdmi/PlaybackDeviceToAudioSystemAvbTest.java
index 43ab804e04be..ffc1c62f79b3 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/PlaybackDeviceToAudioSystemAvbTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/PlaybackDeviceToAudioSystemAvbTest.java
@@ -21,7 +21,7 @@ import static com.google.common.truth.Truth.assertThat;
import android.hardware.hdmi.DeviceFeatures;
import android.hardware.hdmi.HdmiControlManager;
import android.hardware.hdmi.HdmiDeviceInfo;
-import android.media.AudioManager;
+import android.media.AudioDeviceVolumeManager;
import android.platform.test.annotations.Presubmit;
import androidx.test.filters.SmallTest;
@@ -64,8 +64,9 @@ public class PlaybackDeviceToAudioSystemAvbTest extends BasePlaybackDeviceAvbTes
// Audio System disables System Audio Mode. AVB should be disabled.
receiveSetSystemAudioMode(false);
- assertThat(mAudioManager.getDeviceVolumeBehavior(getAudioOutputDevice())).isEqualTo(
- AudioManager.DEVICE_VOLUME_BEHAVIOR_FULL);
+ assertThat(mAudioDeviceVolumeManager.getDeviceVolumeBehavior(
+ getAudioOutputDevice())).isEqualTo(
+ AudioDeviceVolumeManager.DEVICE_VOLUME_BEHAVIOR_FULL);
// TV reports support for <Set Audio Volume Level>
mNativeWrapper.onCecMessage(ReportFeaturesMessage.build(
@@ -85,7 +86,8 @@ public class PlaybackDeviceToAudioSystemAvbTest extends BasePlaybackDeviceAvbTes
false));
mTestLooper.dispatchAll();
- assertThat(mAudioManager.getDeviceVolumeBehavior(getAudioOutputDevice())).isEqualTo(
- AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE);
+ assertThat(mAudioDeviceVolumeManager.getDeviceVolumeBehavior(
+ getAudioOutputDevice())).isEqualTo(
+ AudioDeviceVolumeManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE);
}
}
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/PlaybackDeviceToTvAvbTest.java b/services/tests/servicestests/src/com/android/server/hdmi/PlaybackDeviceToTvAvbTest.java
index 9b343e34706a..092618072744 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/PlaybackDeviceToTvAvbTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/PlaybackDeviceToTvAvbTest.java
@@ -23,7 +23,7 @@ import static org.mockito.Mockito.clearInvocations;
import android.hardware.hdmi.DeviceFeatures;
import android.hardware.hdmi.HdmiControlManager;
import android.hardware.hdmi.HdmiDeviceInfo;
-import android.media.AudioManager;
+import android.media.AudioDeviceVolumeManager;
import android.platform.test.annotations.Presubmit;
import androidx.test.filters.SmallTest;
@@ -65,8 +65,9 @@ public class PlaybackDeviceToTvAvbTest extends BasePlaybackDeviceAvbTest {
// Audio System enables System Audio Mode. AVB should be disabled.
receiveSetSystemAudioMode(true);
- assertThat(mAudioManager.getDeviceVolumeBehavior(getAudioOutputDevice())).isEqualTo(
- AudioManager.DEVICE_VOLUME_BEHAVIOR_FULL);
+ assertThat(mAudioDeviceVolumeManager.getDeviceVolumeBehavior(
+ getAudioOutputDevice())).isEqualTo(
+ AudioDeviceVolumeManager.DEVICE_VOLUME_BEHAVIOR_FULL);
clearInvocations(mAudioManager, mAudioDeviceVolumeManager);
@@ -88,7 +89,8 @@ public class PlaybackDeviceToTvAvbTest extends BasePlaybackDeviceAvbTest {
false));
mTestLooper.dispatchAll();
- assertThat(mAudioManager.getDeviceVolumeBehavior(getAudioOutputDevice())).isEqualTo(
- AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE);
+ assertThat(mAudioDeviceVolumeManager.getDeviceVolumeBehavior(
+ getAudioOutputDevice())).isEqualTo(
+ AudioDeviceVolumeManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE);
}
}
diff --git a/services/tests/servicestests/src/com/android/server/location/contexthub/ContextHubEndpointTest.java b/services/tests/servicestests/src/com/android/server/location/contexthub/ContextHubEndpointTest.java
index 4d2dcf65bfeb..87cd1560509c 100644
--- a/services/tests/servicestests/src/com/android/server/location/contexthub/ContextHubEndpointTest.java
+++ b/services/tests/servicestests/src/com/android/server/location/contexthub/ContextHubEndpointTest.java
@@ -19,7 +19,10 @@ package com.android.server.location.contexthub;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.timeout;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
@@ -28,6 +31,7 @@ import static org.mockito.Mockito.when;
import android.content.Context;
import android.hardware.contexthub.EndpointInfo;
import android.hardware.contexthub.ErrorCode;
+import android.hardware.contexthub.HubEndpoint;
import android.hardware.contexthub.HubEndpointInfo;
import android.hardware.contexthub.HubEndpointInfo.HubEndpointIdentifier;
import android.hardware.contexthub.HubMessage;
@@ -67,12 +71,15 @@ public class ContextHubEndpointTest {
private static final int SESSION_ID_RANGE = ContextHubEndpointManager.SERVICE_SESSION_RANGE;
private static final int MIN_SESSION_ID = 0;
private static final int MAX_SESSION_ID = MIN_SESSION_ID + SESSION_ID_RANGE - 1;
+ private static final int SESSION_ID_FOR_OPEN_REQUEST = MAX_SESSION_ID + 1;
+ private static final int INVALID_SESSION_ID_FOR_OPEN_REQUEST = MIN_SESSION_ID + 1;
private static final String ENDPOINT_NAME = "Example test endpoint";
private static final int ENDPOINT_ID = 1;
private static final String ENDPOINT_PACKAGE_NAME = "com.android.server.location.contexthub";
private static final String TARGET_ENDPOINT_NAME = "Example target endpoint";
+ private static final String ENDPOINT_SERVICE_DESCRIPTOR = "serviceDescriptor";
private static final int TARGET_ENDPOINT_ID = 1;
private static final int SAMPLE_MESSAGE_TYPE = 1234;
@@ -225,6 +232,105 @@ public class ContextHubEndpointTest {
}
@Test
+ public void testEndpointSessionOpenRequest() throws RemoteException {
+ assertThat(mEndpointManager.getNumAvailableSessions()).isEqualTo(SESSION_ID_RANGE);
+ IContextHubEndpoint endpoint = registerExampleEndpoint();
+
+ HubEndpointInfo targetInfo =
+ new HubEndpointInfo(
+ TARGET_ENDPOINT_NAME,
+ TARGET_ENDPOINT_ID,
+ ENDPOINT_PACKAGE_NAME,
+ Collections.emptyList());
+ mHubInfoRegistry.onEndpointStarted(new HubEndpointInfo[] {targetInfo});
+ mEndpointManager.onEndpointSessionOpenRequest(
+ SESSION_ID_FOR_OPEN_REQUEST,
+ endpoint.getAssignedHubEndpointInfo().getIdentifier(),
+ targetInfo.getIdentifier(),
+ ENDPOINT_SERVICE_DESCRIPTOR);
+
+ verify(mMockCallback)
+ .onSessionOpenRequest(
+ SESSION_ID_FOR_OPEN_REQUEST, targetInfo, ENDPOINT_SERVICE_DESCRIPTOR);
+
+ // Accept
+ endpoint.openSessionRequestComplete(SESSION_ID_FOR_OPEN_REQUEST);
+ verify(mMockEndpointCommunications)
+ .endpointSessionOpenComplete(SESSION_ID_FOR_OPEN_REQUEST);
+
+ unregisterExampleEndpoint(endpoint);
+ }
+
+ @Test
+ public void testEndpointSessionOpenRequestWithInvalidSessionId() throws RemoteException {
+ assertThat(mEndpointManager.getNumAvailableSessions()).isEqualTo(SESSION_ID_RANGE);
+ IContextHubEndpoint endpoint = registerExampleEndpoint();
+
+ HubEndpointInfo targetInfo =
+ new HubEndpointInfo(
+ TARGET_ENDPOINT_NAME,
+ TARGET_ENDPOINT_ID,
+ ENDPOINT_PACKAGE_NAME,
+ Collections.emptyList());
+ mHubInfoRegistry.onEndpointStarted(new HubEndpointInfo[] {targetInfo});
+ mEndpointManager.onEndpointSessionOpenRequest(
+ INVALID_SESSION_ID_FOR_OPEN_REQUEST,
+ endpoint.getAssignedHubEndpointInfo().getIdentifier(),
+ targetInfo.getIdentifier(),
+ ENDPOINT_SERVICE_DESCRIPTOR);
+ verify(mMockEndpointCommunications)
+ .closeEndpointSession(
+ INVALID_SESSION_ID_FOR_OPEN_REQUEST,
+ Reason.OPEN_ENDPOINT_SESSION_REQUEST_REJECTED);
+ verify(mMockCallback, never())
+ .onSessionOpenRequest(
+ INVALID_SESSION_ID_FOR_OPEN_REQUEST,
+ targetInfo,
+ ENDPOINT_SERVICE_DESCRIPTOR);
+
+ unregisterExampleEndpoint(endpoint);
+ }
+
+ @Test
+ public void testEndpointSessionOpenRequest_duplicatedSessionId_noopWhenSessionIsActive()
+ throws RemoteException {
+ assertThat(mEndpointManager.getNumAvailableSessions()).isEqualTo(SESSION_ID_RANGE);
+ IContextHubEndpoint endpoint = registerExampleEndpoint();
+
+ HubEndpointInfo targetInfo =
+ new HubEndpointInfo(
+ TARGET_ENDPOINT_NAME,
+ TARGET_ENDPOINT_ID,
+ ENDPOINT_PACKAGE_NAME,
+ Collections.emptyList());
+ mHubInfoRegistry.onEndpointStarted(new HubEndpointInfo[] {targetInfo});
+ mEndpointManager.onEndpointSessionOpenRequest(
+ SESSION_ID_FOR_OPEN_REQUEST,
+ endpoint.getAssignedHubEndpointInfo().getIdentifier(),
+ targetInfo.getIdentifier(),
+ ENDPOINT_SERVICE_DESCRIPTOR);
+ endpoint.openSessionRequestComplete(SESSION_ID_FOR_OPEN_REQUEST);
+ // Now session with id SESSION_ID_FOR_OPEN_REQUEST is active
+
+ // Duplicated session open request
+ mEndpointManager.onEndpointSessionOpenRequest(
+ SESSION_ID_FOR_OPEN_REQUEST,
+ endpoint.getAssignedHubEndpointInfo().getIdentifier(),
+ targetInfo.getIdentifier(),
+ ENDPOINT_SERVICE_DESCRIPTOR);
+
+ // Client API is only invoked once
+ verify(mMockCallback, times(1))
+ .onSessionOpenRequest(
+ SESSION_ID_FOR_OPEN_REQUEST, targetInfo, ENDPOINT_SERVICE_DESCRIPTOR);
+ // HAL still receives two open complete notifications
+ verify(mMockEndpointCommunications, times(2))
+ .endpointSessionOpenComplete(SESSION_ID_FOR_OPEN_REQUEST);
+
+ unregisterExampleEndpoint(endpoint);
+ }
+
+ @Test
public void testMessageTransaction() throws RemoteException {
IContextHubEndpoint endpoint = registerExampleEndpoint();
testMessageTransactionInternal(endpoint, /* deliverMessageStatus= */ true);
@@ -282,6 +388,49 @@ public class ContextHubEndpointTest {
unregisterExampleEndpoint(endpoint);
}
+ @Test
+ public void testUnreliableMessageFailureClosesSession() throws RemoteException {
+ IContextHubEndpoint endpoint = registerExampleEndpoint();
+ int sessionId = openTestSession(endpoint);
+
+ doThrow(new RemoteException("Intended exception in test"))
+ .when(mMockCallback)
+ .onMessageReceived(anyInt(), any(HubMessage.class));
+ mEndpointManager.onMessageReceived(sessionId, SAMPLE_UNRELIABLE_MESSAGE);
+ ArgumentCaptor<HubMessage> messageCaptor = ArgumentCaptor.forClass(HubMessage.class);
+ verify(mMockCallback).onMessageReceived(eq(sessionId), messageCaptor.capture());
+ assertThat(messageCaptor.getValue()).isEqualTo(SAMPLE_UNRELIABLE_MESSAGE);
+
+ verify(mMockEndpointCommunications).closeEndpointSession(sessionId, Reason.UNSPECIFIED);
+ verify(mMockCallback).onSessionClosed(sessionId, HubEndpoint.REASON_FAILURE);
+ assertThat(mEndpointManager.getNumAvailableSessions()).isEqualTo(SESSION_ID_RANGE);
+
+ unregisterExampleEndpoint(endpoint);
+ }
+
+ @Test
+ public void testSendUnreliableMessageFailureClosesSession() throws RemoteException {
+ IContextHubEndpoint endpoint = registerExampleEndpoint();
+ int sessionId = openTestSession(endpoint);
+
+ doThrow(new RemoteException("Intended exception in test"))
+ .when(mMockEndpointCommunications)
+ .sendMessageToEndpoint(anyInt(), any(Message.class));
+ endpoint.sendMessage(sessionId, SAMPLE_UNRELIABLE_MESSAGE, /* callback= */ null);
+ ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class);
+ verify(mMockEndpointCommunications)
+ .sendMessageToEndpoint(eq(sessionId), messageCaptor.capture());
+ Message message = messageCaptor.getValue();
+ assertThat(message.type).isEqualTo(SAMPLE_UNRELIABLE_MESSAGE.getMessageType());
+ assertThat(message.content).isEqualTo(SAMPLE_UNRELIABLE_MESSAGE.getMessageBody());
+
+ verify(mMockEndpointCommunications).closeEndpointSession(sessionId, Reason.UNSPECIFIED);
+ verify(mMockCallback).onSessionClosed(sessionId, HubEndpoint.REASON_FAILURE);
+ assertThat(mEndpointManager.getNumAvailableSessions()).isEqualTo(SESSION_ID_RANGE);
+
+ unregisterExampleEndpoint(endpoint);
+ }
+
/** A helper method to create a session and validates reliable message sending. */
private void testMessageTransactionInternal(
IContextHubEndpoint endpoint, boolean deliverMessageStatus) throws RemoteException {
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTests.java b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTests.java
index 125791acc61a..f312a1bdb985 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTests.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTests.java
@@ -17,7 +17,7 @@
package com.android.server.locksettings;
import static android.Manifest.permission.CONFIGURE_FACTORY_RESET_PROTECTION;
-import static android.security.Flags.FLAG_CLEAR_STRONG_AUTH_ON_ADD_PRIMARY_CREDENTIAL;
+import static android.security.Flags.FLAG_CLEAR_STRONG_AUTH_ON_ADDING_PRIMARY_CREDENTIAL;
import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_NONE;
import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PASSWORD;
@@ -264,7 +264,7 @@ public class LockSettingsServiceTests extends BaseLockSettingsServiceTests {
}
@Test
- @RequiresFlagsEnabled(FLAG_CLEAR_STRONG_AUTH_ON_ADD_PRIMARY_CREDENTIAL)
+ @RequiresFlagsEnabled(FLAG_CLEAR_STRONG_AUTH_ON_ADDING_PRIMARY_CREDENTIAL)
public void setLockCredential_forPrimaryUser_clearsStrongAuthWhenFlagIsOn()
throws Exception {
setCredential(PRIMARY_USER_ID, newPassword("password"));
@@ -273,7 +273,7 @@ public class LockSettingsServiceTests extends BaseLockSettingsServiceTests {
}
@Test
- @RequiresFlagsDisabled(FLAG_CLEAR_STRONG_AUTH_ON_ADD_PRIMARY_CREDENTIAL)
+ @RequiresFlagsDisabled(FLAG_CLEAR_STRONG_AUTH_ON_ADDING_PRIMARY_CREDENTIAL)
public void setLockCredential_forPrimaryUser_leavesStrongAuthWhenFlagIsOff()
throws Exception {
setCredential(PRIMARY_USER_ID, newPassword("password"));
@@ -312,7 +312,7 @@ public class LockSettingsServiceTests extends BaseLockSettingsServiceTests {
}
@Test
- @RequiresFlagsEnabled(FLAG_CLEAR_STRONG_AUTH_ON_ADD_PRIMARY_CREDENTIAL)
+ @RequiresFlagsEnabled(FLAG_CLEAR_STRONG_AUTH_ON_ADDING_PRIMARY_CREDENTIAL)
public void setLockCredential_profileWithNewSeparateChallenge_clearsStrongAuthWhenFlagIsOn()
throws Exception {
mService.setSeparateProfileChallengeEnabled(MANAGED_PROFILE_USER_ID, true, null);
@@ -323,7 +323,7 @@ public class LockSettingsServiceTests extends BaseLockSettingsServiceTests {
}
@Test
- @RequiresFlagsDisabled(FLAG_CLEAR_STRONG_AUTH_ON_ADD_PRIMARY_CREDENTIAL)
+ @RequiresFlagsDisabled(FLAG_CLEAR_STRONG_AUTH_ON_ADDING_PRIMARY_CREDENTIAL)
public void setLockCredential_profileWithNewSeparateChallenge_leavesStrongAuthWhenFlagIsOff()
throws Exception {
mService.setSeparateProfileChallengeEnabled(MANAGED_PROFILE_USER_ID, true, null);
@@ -377,7 +377,7 @@ public class LockSettingsServiceTests extends BaseLockSettingsServiceTests {
}
@Test
- @RequiresFlagsEnabled(FLAG_CLEAR_STRONG_AUTH_ON_ADD_PRIMARY_CREDENTIAL)
+ @RequiresFlagsEnabled(FLAG_CLEAR_STRONG_AUTH_ON_ADDING_PRIMARY_CREDENTIAL)
public void setLockCredential_primaryWithUnifiedProfile_clearsStrongAuthForBothWhenFlagIsOn()
throws Exception {
final LockscreenCredential credential = newPassword("oldPassword");
@@ -393,7 +393,7 @@ public class LockSettingsServiceTests extends BaseLockSettingsServiceTests {
}
@Test
- @RequiresFlagsDisabled(FLAG_CLEAR_STRONG_AUTH_ON_ADD_PRIMARY_CREDENTIAL)
+ @RequiresFlagsDisabled(FLAG_CLEAR_STRONG_AUTH_ON_ADDING_PRIMARY_CREDENTIAL)
public void setLockCredential_primaryWithUnifiedProfile_leavesStrongAuthForBothWhenFlagIsOff()
throws Exception {
final LockscreenCredential credential = newPassword("oldPassword");
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java b/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java
index ae781dcb834a..52c7c48cc8b7 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java
@@ -60,10 +60,6 @@ import android.os.RemoteException;
import android.os.ServiceSpecificException;
import android.os.UserManager;
import android.platform.test.annotations.Presubmit;
-import android.platform.test.annotations.RequiresFlagsDisabled;
-import android.platform.test.annotations.RequiresFlagsEnabled;
-import android.platform.test.flag.junit.CheckFlagsRule;
-import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
@@ -74,7 +70,6 @@ import com.android.server.locksettings.ResumeOnRebootServiceProvider.ResumeOnReb
import com.android.server.pm.UserManagerInternal;
import org.junit.Before;
-import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
@@ -113,9 +108,6 @@ public class RebootEscrowManagerTests {
0x26, 0x52, 0x72, 0x63, 0x63, 0x61, 0x78, 0x23,
};
- @Rule
- public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
-
private Context mContext;
private UserManager mUserManager;
private UserManagerInternal mUserManagerInternal;
@@ -847,53 +839,6 @@ public class RebootEscrowManagerTests {
}
@Test
- @RequiresFlagsDisabled(Flags.FLAG_WAIT_FOR_INTERNET_ROR)
- public void loadRebootEscrowDataIfAvailable_ServerBasedIoError_RetryFailure() throws Exception {
- setServerBasedRebootEscrowProvider();
-
- when(mInjected.getBootCount()).thenReturn(0);
- RebootEscrowListener mockListener = mock(RebootEscrowListener.class);
- mService.setRebootEscrowListener(mockListener);
- mService.prepareRebootEscrow();
-
- clearInvocations(mServiceConnection);
- callToRebootEscrowIfNeededAndWait(PRIMARY_USER_ID);
- verify(mockListener).onPreparedForReboot(eq(true));
- verify(mServiceConnection, never()).wrapBlob(any(), anyLong(), anyLong());
-
- // Use x -> x for both wrap & unwrap functions.
- when(mServiceConnection.wrapBlob(any(), anyLong(), anyLong()))
- .thenAnswer(invocation -> invocation.getArgument(0));
- assertEquals(ARM_REBOOT_ERROR_NONE, mService.armRebootEscrowIfNeeded());
- verify(mServiceConnection).wrapBlob(any(), anyLong(), anyLong());
- assertTrue(mStorage.hasRebootEscrowServerBlob());
-
- // pretend reboot happens here
- when(mInjected.getBootCount()).thenReturn(1);
- ArgumentCaptor<Boolean> metricsSuccessCaptor = ArgumentCaptor.forClass(Boolean.class);
- ArgumentCaptor<Integer> metricsErrorCodeCaptor = ArgumentCaptor.forClass(Integer.class);
- doNothing()
- .when(mInjected)
- .reportMetric(
- metricsSuccessCaptor.capture(),
- metricsErrorCodeCaptor.capture(),
- eq(2) /* Server based */,
- eq(2) /* attempt count */,
- anyInt(),
- eq(0) /* vbmeta status */,
- anyInt());
- when(mServiceConnection.unwrap(any(), anyLong())).thenThrow(IOException.class);
-
- mService.loadRebootEscrowDataIfAvailable(mHandler);
- // Sleep 5s for the retry to complete
- Thread.sleep(5 * 1000);
- assertFalse(metricsSuccessCaptor.getValue());
- assertEquals(
- Integer.valueOf(RebootEscrowManager.ERROR_NO_NETWORK),
- metricsErrorCodeCaptor.getValue());
- }
-
- @Test
public void loadRebootEscrowDataIfAvailable_ServerBased_RetrySuccess() throws Exception {
setServerBasedRebootEscrowProvider();
@@ -941,7 +886,6 @@ public class RebootEscrowManagerTests {
}
@Test
- @RequiresFlagsEnabled(Flags.FLAG_WAIT_FOR_INTERNET_ROR)
public void loadRebootEscrowDataIfAvailable_waitForInternet_networkUnavailable()
throws Exception {
setServerBasedRebootEscrowProvider();
@@ -989,7 +933,6 @@ public class RebootEscrowManagerTests {
}
@Test
- @RequiresFlagsEnabled(Flags.FLAG_WAIT_FOR_INTERNET_ROR)
public void loadRebootEscrowDataIfAvailable_waitForInternet_networkLost() throws Exception {
setServerBasedRebootEscrowProvider();
@@ -1044,7 +987,6 @@ public class RebootEscrowManagerTests {
}
@Test
- @RequiresFlagsEnabled(Flags.FLAG_WAIT_FOR_INTERNET_ROR)
public void loadRebootEscrowDataIfAvailable_waitForInternet_networkAvailableWithDelay()
throws Exception {
setServerBasedRebootEscrowProvider();
@@ -1103,7 +1045,6 @@ public class RebootEscrowManagerTests {
}
@Test
- @RequiresFlagsEnabled(Flags.FLAG_WAIT_FOR_INTERNET_ROR)
public void loadRebootEscrowDataIfAvailable_waitForInternet_timeoutExhausted()
throws Exception {
setServerBasedRebootEscrowProvider();
@@ -1163,7 +1104,6 @@ public class RebootEscrowManagerTests {
}
@Test
- @RequiresFlagsEnabled(Flags.FLAG_WAIT_FOR_INTERNET_ROR)
public void loadRebootEscrowDataIfAvailable_serverBasedWaitForNetwork_retryCountExhausted()
throws Exception {
setServerBasedRebootEscrowProvider();
@@ -1219,7 +1159,6 @@ public class RebootEscrowManagerTests {
}
@Test
- @RequiresFlagsEnabled(Flags.FLAG_WAIT_FOR_INTERNET_ROR)
public void loadRebootEscrowDataIfAvailable_ServerBasedWaitForInternet_RetrySuccess()
throws Exception {
setServerBasedRebootEscrowProvider();
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest8.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest8.java
index 88395a4889c4..071bd739072b 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest8.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest8.java
@@ -306,9 +306,9 @@ public class ShortcutManagerTest8 extends BaseShortcutManagerTest {
// This method is always called, even with PI == null.
if (resultIntent == null) {
- verify(mServiceContext, times(1)).sendIntentSender(isNull(IntentSender.class));
+ verify(mServiceContext, times(1)).sendIntentSender(isNull());
} else {
- verify(mServiceContext, times(1)).sendIntentSender(notNull(IntentSender.class));
+ verify(mServiceContext, times(1)).sendIntentSender(notNull());
}
runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
@@ -619,7 +619,7 @@ public class ShortcutManagerTest8 extends BaseShortcutManagerTest {
makeResultIntent()));
// The intent should be sent right away.
- verify(mServiceContext, times(1)).sendIntentSender(notNull(IntentSender.class));
+ verify(mServiceContext, times(1)).sendIntentSender(notNull());
});
// Already pinned.
@@ -661,7 +661,7 @@ public class ShortcutManagerTest8 extends BaseShortcutManagerTest {
assertTrue(request.accept());
// The intent is only sent once, so times(1).
- verify(mServiceContext, times(1)).sendIntentSender(isNull(IntentSender.class));
+ verify(mServiceContext, times(1)).sendIntentSender(isNull());
});
// Still pinned.
@@ -698,7 +698,7 @@ public class ShortcutManagerTest8 extends BaseShortcutManagerTest {
makeResultIntent()));
// The intent should be sent right away.
- verify(mServiceContext, times(1)).sendIntentSender(notNull(IntentSender.class));
+ verify(mServiceContext, times(1)).sendIntentSender(notNull());
});
// Already pinned.
@@ -742,7 +742,7 @@ public class ShortcutManagerTest8 extends BaseShortcutManagerTest {
assertTrue(request.accept());
// The intent is only sent once, so times(1).
- verify(mServiceContext, times(1)).sendIntentSender(isNull(IntentSender.class));
+ verify(mServiceContext, times(1)).sendIntentSender(isNull());
});
// Still pinned.
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserInfoTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserInfoTest.java
index d1b2e8e6d868..1fb84113e278 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserInfoTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceUserInfoTest.java
@@ -274,7 +274,7 @@ public class UserManagerServiceUserInfoTest {
/** Test UserInfo.canHaveProfile for main user */
@Test
public void testCanHaveProfile() throws Exception {
- UserInfo userInfo = createUser(100, FLAG_MAIN, null);
+ UserInfo userInfo = createUser(100, FLAG_FULL | FLAG_MAIN, null);
assertTrue("Main users can have profile", userInfo.canHaveProfile());
}
diff --git a/services/tests/servicestests/src/com/android/server/power/ThermalManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/power/ThermalManagerServiceTest.java
index 952d8fa47a34..09acfddacf03 100644
--- a/services/tests/servicestests/src/com/android/server/power/ThermalManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/ThermalManagerServiceTest.java
@@ -51,6 +51,7 @@ import android.os.IThermalStatusListener;
import android.os.PowerManager;
import android.os.RemoteException;
import android.os.Temperature;
+import android.platform.test.annotations.DisableFlags;
import android.platform.test.annotations.EnableFlags;
import android.platform.test.flag.junit.SetFlagsRule;
@@ -78,6 +79,7 @@ import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
+import java.util.concurrent.atomic.AtomicInteger;
/**
* atest $ANDROID_BUILD_TOP/frameworks/base/services/tests/servicestests/src/com/android/server
@@ -117,7 +119,8 @@ public class ThermalManagerServiceTest {
*/
private class ThermalHalFake extends ThermalHalWrapper {
private static final int INIT_STATUS = Temperature.THROTTLING_NONE;
- private List<Temperature> mTemperatureList = new ArrayList<>();
+ private final List<Temperature> mTemperatureList = new ArrayList<>();
+ private AtomicInteger mGetCurrentTemperaturesCalled = new AtomicInteger();
private List<CoolingDevice> mCoolingDeviceList = new ArrayList<>();
private List<TemperatureThreshold> mTemperatureThresholdList = initializeThresholds();
@@ -173,6 +176,7 @@ public class ThermalManagerServiceTest {
mTemperatureList.add(mUsbPort);
mCoolingDeviceList.add(mCpu);
mCoolingDeviceList.add(mGpu);
+ mGetCurrentTemperaturesCalled.set(0);
}
void enableForecastSkinTemperature() {
@@ -188,14 +192,24 @@ public class ThermalManagerServiceTest {
mForecastSkinTemperaturesError = true;
}
+ void updateTemperatureList(Temperature... temperatures) {
+ synchronized (mTemperatureList) {
+ mTemperatureList.clear();
+ mTemperatureList.addAll(Arrays.asList(temperatures));
+ }
+ }
+
@Override
protected List<Temperature> getCurrentTemperatures(boolean shouldFilter, int type) {
List<Temperature> ret = new ArrayList<>();
- for (Temperature temperature : mTemperatureList) {
- if (shouldFilter && type != temperature.getType()) {
- continue;
+ synchronized (mTemperatureList) {
+ mGetCurrentTemperaturesCalled.incrementAndGet();
+ for (Temperature temperature : mTemperatureList) {
+ if (shouldFilter && type != temperature.getType()) {
+ continue;
+ }
+ ret.add(temperature);
}
- ret.add(temperature);
}
return ret;
}
@@ -407,7 +421,7 @@ public class ThermalManagerServiceTest {
Thread.sleep(CALLBACK_TIMEOUT_MILLI_SEC);
resetListenerMock();
int status = Temperature.THROTTLING_SEVERE;
- mFakeHal.mTemperatureList = new ArrayList<>();
+ mFakeHal.updateTemperatureList();
// Should not notify on non-skin type
Temperature newBattery = new Temperature(37, Temperature.TYPE_BATTERY, "batt", status);
@@ -537,6 +551,42 @@ public class ThermalManagerServiceTest {
}
@Test
+ @DisableFlags({Flags.FLAG_ALLOW_THERMAL_HAL_SKIN_FORECAST})
+ public void testGetThermalHeadroom_handlerUpdateTemperatures()
+ throws RemoteException, InterruptedException {
+ // test that handler will at least enqueue one message to periodically read temperatures
+ // even if there is sample seeded from HAL temperature callback
+ String temperatureName = "skin1";
+ Temperature temperature = new Temperature(100, Temperature.TYPE_SKIN, temperatureName,
+ Temperature.THROTTLING_NONE);
+ mFakeHal.mCallback.onTemperatureChanged(temperature);
+ float headroom = mService.mService.getThermalHeadroom(0);
+ // the callback temperature 100C (headroom > 1.0f) sample should have been appended by the
+ // immediately scheduled fake HAL current temperatures read (mSkin1, mSkin2), and because
+ // there are less samples for prediction, the latest temperature mSkin1 is used to calculate
+ // headroom (mSkin2 has no threshold), which is 0.6f (28C vs threshold 40C).
+ assertEquals(0.6f, headroom, 0.01f);
+ // one called by service onActivityManagerReady, one called by handler on headroom call
+ assertEquals(2, mFakeHal.mGetCurrentTemperaturesCalled.get());
+ // periodic read should update the samples history, so the headroom should increase 0.1f
+ // as current temperature goes up by 3C every 1100ms.
+ for (int i = 1; i < 5; i++) {
+ Temperature newTemperature = new Temperature(mFakeHal.mSkin1.getValue() + 3 * i,
+ Temperature.TYPE_SKIN,
+ temperatureName,
+ Temperature.THROTTLING_NONE);
+ mFakeHal.updateTemperatureList(newTemperature);
+ // wait for handler to update temperature
+ Thread.sleep(1100);
+ // assert that only one callback was scheduled to query HAL when making multiple
+ // headroom calls
+ assertEquals(2 + i, mFakeHal.mGetCurrentTemperaturesCalled.get());
+ headroom = mService.mService.getThermalHeadroom(0);
+ assertEquals(0.6f + 0.1f * i, headroom, 0.01f);
+ }
+ }
+
+ @Test
@EnableFlags({Flags.FLAG_ALLOW_THERMAL_HAL_SKIN_FORECAST})
public void testGetThermalHeadroom_halForecast() throws RemoteException {
mFakeHal.mForecastSkinTemperaturesCalled = 0;
diff --git a/services/tests/servicestests/src/com/android/server/security/advancedprotection/AdvancedProtectionServiceTest.java b/services/tests/servicestests/src/com/android/server/security/advancedprotection/AdvancedProtectionServiceTest.java
index c7a06b8eec7b..d6b3fecb487c 100644
--- a/services/tests/servicestests/src/com/android/server/security/advancedprotection/AdvancedProtectionServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/security/advancedprotection/AdvancedProtectionServiceTest.java
@@ -45,6 +45,8 @@ import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import java.util.List;
+import java.util.Map;
+import java.util.HashMap;
import java.util.concurrent.atomic.AtomicBoolean;
@SuppressLint("VisibleForTests")
@@ -66,17 +68,28 @@ public class AdvancedProtectionServiceTest {
mPermissionEnforcer.grant(Manifest.permission.QUERY_ADVANCED_PROTECTION_MODE);
mStore = new AdvancedProtectionService.AdvancedProtectionStore(mContext) {
+ private Map<String, Integer> mStoredValues = new HashMap<>();
private boolean mEnabled = false;
@Override
- boolean retrieve() {
+ boolean retrieveAdvancedProtectionModeEnabled() {
return mEnabled;
}
@Override
- void store(boolean enabled) {
+ void storeAdvancedProtectionModeEnabled(boolean enabled) {
this.mEnabled = enabled;
}
+
+ @Override
+ void storeInt(String key, int value) {
+ mStoredValues.put(key, value);
+ }
+
+ @Override
+ int retrieveInt(String key, int defaultValue) {
+ return mStoredValues.getOrDefault(key, defaultValue);
+ }
};
mLooper = new TestLooper();
@@ -259,7 +272,7 @@ public class AdvancedProtectionServiceTest {
AdvancedProtectionProvider provider = new AdvancedProtectionProvider() {
@Override
- public List<AdvancedProtectionFeature> getFeatures() {
+ public List<AdvancedProtectionFeature> getFeatures(Context context) {
return List.of(feature2);
}
};
@@ -291,7 +304,7 @@ public class AdvancedProtectionServiceTest {
AdvancedProtectionProvider provider = new AdvancedProtectionProvider() {
@Override
- public List<AdvancedProtectionFeature> getFeatures() {
+ public List<AdvancedProtectionFeature> getFeatures(Context context) {
return List.of(feature2);
}
};
@@ -316,6 +329,18 @@ public class AdvancedProtectionServiceTest {
}
@Test
+ public void testUsbDataProtection_withoutPermission() {
+ mPermissionEnforcer.revoke(Manifest.permission.QUERY_ADVANCED_PROTECTION_MODE);
+ assertThrows(SecurityException.class, () -> mService.isUsbDataProtectionEnabled());
+ }
+
+ @Test
+ public void testSetUsbDataProtection_withoutPermission() {
+ mPermissionEnforcer.revoke(Manifest.permission.MANAGE_ADVANCED_PROTECTION_MODE);
+ assertThrows(SecurityException.class, () -> mService.setUsbDataProtectionEnabled(true));
+ }
+
+ @Test
public void testRegisterCallback_withoutPermission() {
mPermissionEnforcer.revoke(Manifest.permission.QUERY_ADVANCED_PROTECTION_MODE);
assertThrows(SecurityException.class, () -> mService.registerAdvancedProtectionCallback(
diff --git a/services/tests/servicestests/src/com/android/server/statusbar/StatusBarManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/statusbar/StatusBarManagerServiceTest.java
index 148c96850d34..6d682ccef98d 100644
--- a/services/tests/servicestests/src/com/android/server/statusbar/StatusBarManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/statusbar/StatusBarManagerServiceTest.java
@@ -36,6 +36,7 @@ import static android.app.StatusBarManager.DISABLE_SYSTEM_INFO;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertThrows;
+import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.nullable;
@@ -69,16 +70,20 @@ import android.os.Binder;
import android.os.Looper;
import android.os.RemoteException;
import android.os.UserHandle;
+import android.platform.test.annotations.EnableFlags;
import android.service.quicksettings.TileService;
import android.testing.TestableContext;
+import android.util.Pair;
import androidx.test.InstrumentationRegistry;
+import com.android.internal.statusbar.DisableStates;
import com.android.internal.statusbar.IAddTileResultCallback;
import com.android.internal.statusbar.IStatusBar;
import com.android.server.LocalServices;
import com.android.server.policy.GlobalActionsProvider;
import com.android.server.wm.ActivityTaskManagerInternal;
+import com.android.systemui.shared.Flags;
import libcore.junit.util.compat.CoreCompatChangeRule;
@@ -105,6 +110,7 @@ public class StatusBarManagerServiceTest {
TEST_SERVICE);
private static final CharSequence APP_NAME = "AppName";
private static final CharSequence TILE_LABEL = "Tile label";
+ private static final int SECONDARY_DISPLAY_ID = 2;
@Rule
public final TestableContext mContext =
@@ -749,6 +755,40 @@ public class StatusBarManagerServiceTest {
}
@Test
+ @EnableFlags(Flags.FLAG_STATUS_BAR_CONNECTED_DISPLAYS)
+ public void testDisableForAllDisplays() throws Exception {
+ int user1Id = 0;
+ mockUidCheck();
+ mockCurrentUserCheck(user1Id);
+
+ mStatusBarManagerService.onDisplayAdded(SECONDARY_DISPLAY_ID);
+
+ int expectedFlags = DISABLE_MASK & DISABLE_BACK;
+ String pkg = mContext.getPackageName();
+
+ // before disabling
+ assertEquals(DISABLE_NONE,
+ mStatusBarManagerService.getDisableFlags(mMockStatusBar, user1Id)[0]);
+
+ // disable
+ mStatusBarManagerService.disable(expectedFlags, mMockStatusBar, pkg);
+
+ ArgumentCaptor<DisableStates> disableStatesCaptor = ArgumentCaptor.forClass(
+ DisableStates.class);
+ verify(mMockStatusBar).disableForAllDisplays(disableStatesCaptor.capture());
+ DisableStates capturedDisableStates = disableStatesCaptor.getValue();
+ assertTrue(capturedDisableStates.animate);
+ assertEquals(capturedDisableStates.displaysWithStates.size(), 2);
+ Pair<Integer, Integer> display0States = capturedDisableStates.displaysWithStates.get(0);
+ assertEquals((int) display0States.first, expectedFlags);
+ assertEquals((int) display0States.second, 0);
+ Pair<Integer, Integer> display2States = capturedDisableStates.displaysWithStates.get(
+ SECONDARY_DISPLAY_ID);
+ assertEquals((int) display2States.first, expectedFlags);
+ assertEquals((int) display2States.second, 0);
+ }
+
+ @Test
public void testSetHomeDisabled() throws Exception {
int expectedFlags = DISABLE_MASK & DISABLE_HOME;
String pkg = mContext.getPackageName();
@@ -851,6 +891,40 @@ public class StatusBarManagerServiceTest {
}
@Test
+ @EnableFlags(Flags.FLAG_STATUS_BAR_CONNECTED_DISPLAYS)
+ public void testDisable2ForAllDisplays() throws Exception {
+ int user1Id = 0;
+ mockUidCheck();
+ mockCurrentUserCheck(user1Id);
+
+ mStatusBarManagerService.onDisplayAdded(SECONDARY_DISPLAY_ID);
+
+ int expectedFlags = DISABLE2_MASK & DISABLE2_NOTIFICATION_SHADE;
+ String pkg = mContext.getPackageName();
+
+ // before disabling
+ assertEquals(DISABLE_NONE,
+ mStatusBarManagerService.getDisableFlags(mMockStatusBar, user1Id)[0]);
+
+ // disable
+ mStatusBarManagerService.disable2(expectedFlags, mMockStatusBar, pkg);
+
+ ArgumentCaptor<DisableStates> disableStatesCaptor = ArgumentCaptor.forClass(
+ DisableStates.class);
+ verify(mMockStatusBar).disableForAllDisplays(disableStatesCaptor.capture());
+ DisableStates capturedDisableStates = disableStatesCaptor.getValue();
+ assertTrue(capturedDisableStates.animate);
+ assertEquals(capturedDisableStates.displaysWithStates.size(), 2);
+ Pair<Integer, Integer> display0States = capturedDisableStates.displaysWithStates.get(0);
+ assertEquals((int) display0States.first, 0);
+ assertEquals((int) display0States.second, expectedFlags);
+ Pair<Integer, Integer> display2States = capturedDisableStates.displaysWithStates.get(
+ SECONDARY_DISPLAY_ID);
+ assertEquals((int) display2States.first, 0);
+ assertEquals((int) display2States.second, expectedFlags);
+ }
+
+ @Test
public void testSetQuickSettingsDisabled2() throws Exception {
int expectedFlags = DISABLE2_MASK & DISABLE2_QUICK_SETTINGS;
String pkg = mContext.getPackageName();
diff --git a/services/tests/servicestests/src/com/android/server/storage/DiskStatsLoggingServiceTest.java b/services/tests/servicestests/src/com/android/server/storage/DiskStatsLoggingServiceTest.java
index 4a97b4670289..291d0ec8fbfc 100644
--- a/services/tests/servicestests/src/com/android/server/storage/DiskStatsLoggingServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/storage/DiskStatsLoggingServiceTest.java
@@ -85,7 +85,7 @@ public class DiskStatsLoggingServiceTest extends AndroidTestCase {
mDownloads = new TemporaryFolder();
mDownloads.create();
mStorageStats = new ExternalStorageStats();
- when(mSsm.queryExternalStatsForUser(isNull(String.class), any(UserHandle.class)))
+ when(mSsm.queryExternalStatsForUser((String)isNull(), any(UserHandle.class)))
.thenReturn(mStorageStats);
when(mJobService.getSystemService(anyString())).thenReturn(mSsm);
}
diff --git a/services/tests/servicestests/src/com/android/server/supervision/SupervisionServiceTest.kt b/services/tests/servicestests/src/com/android/server/supervision/SupervisionServiceTest.kt
index fbf906586e8b..c59f0a05c619 100644
--- a/services/tests/servicestests/src/com/android/server/supervision/SupervisionServiceTest.kt
+++ b/services/tests/servicestests/src/com/android/server/supervision/SupervisionServiceTest.kt
@@ -17,6 +17,7 @@
package com.android.server.supervision
import android.app.Activity
+import android.app.KeyguardManager
import android.app.admin.DevicePolicyManager
import android.app.admin.DevicePolicyManagerInternal
import android.app.supervision.flags.Flags
@@ -61,6 +62,9 @@ class SupervisionServiceTest {
@get:Rule val mocks: MockitoRule = MockitoJUnit.rule()
@Mock private lateinit var mockDpmInternal: DevicePolicyManagerInternal
+
+ @Mock
+ private lateinit var mockKeyguardManager: KeyguardManager
@Mock private lateinit var mockPackageManager: PackageManager
@Mock private lateinit var mockUserManagerInternal: UserManagerInternal
@@ -71,7 +75,7 @@ class SupervisionServiceTest {
@Before
fun setUp() {
context = InstrumentationRegistry.getInstrumentation().context
- context = SupervisionContextWrapper(context, mockPackageManager)
+ context = SupervisionContextWrapper(context, mockKeyguardManager, mockPackageManager)
LocalServices.removeServiceForTest(DevicePolicyManagerInternal::class.java)
LocalServices.addService(DevicePolicyManagerInternal::class.java, mockDpmInternal)
@@ -250,11 +254,41 @@ class SupervisionServiceTest {
@Test
fun createConfirmSupervisionCredentialsIntent() {
+ service.mInternal.setSupervisionEnabledForUser(context.getUserId(), true)
+ whenever(mockUserManagerInternal.getSupervisingProfileId()).thenReturn(SUPERVISING_USER_ID)
+ whenever(mockKeyguardManager.isDeviceSecure(SUPERVISING_USER_ID)).thenReturn(true)
+
val intent = checkNotNull(service.createConfirmSupervisionCredentialsIntent())
assertThat(intent.action).isEqualTo(ACTION_CONFIRM_SUPERVISION_CREDENTIALS)
assertThat(intent.getPackage()).isEqualTo("com.android.settings")
}
+ @Test
+ fun createConfirmSupervisionCredentialsIntent_supervisionNotEnabled_returnsNull() {
+ service.mInternal.setSupervisionEnabledForUser(context.getUserId(), false)
+ whenever(mockUserManagerInternal.getSupervisingProfileId()).thenReturn(SUPERVISING_USER_ID)
+ whenever(mockKeyguardManager.isDeviceSecure(SUPERVISING_USER_ID)).thenReturn(true)
+
+ assertThat(service.createConfirmSupervisionCredentialsIntent()).isNull()
+ }
+
+ @Test
+ fun createConfirmSupervisionCredentialsIntent_noSupervisingUser_returnsNull() {
+ service.mInternal.setSupervisionEnabledForUser(context.getUserId(), true)
+ whenever(mockUserManagerInternal.getSupervisingProfileId()).thenReturn(UserHandle.USER_NULL)
+
+ assertThat(service.createConfirmSupervisionCredentialsIntent()).isNull()
+ }
+
+ @Test
+ fun createConfirmSupervisionCredentialsIntent_supervisingUserMissingSecureLock_returnsNull() {
+ service.mInternal.setSupervisionEnabledForUser(context.getUserId(), true)
+ whenever(mockUserManagerInternal.getSupervisingProfileId()).thenReturn(SUPERVISING_USER_ID)
+ whenever(mockKeyguardManager.isDeviceSecure(SUPERVISING_USER_ID)).thenReturn(false)
+
+ assertThat(service.createConfirmSupervisionCredentialsIntent()).isNull()
+ }
+
private val systemSupervisionPackage: String
get() = context.getResources().getString(R.string.config_systemSupervision)
@@ -279,6 +313,7 @@ class SupervisionServiceTest {
private companion object {
const val USER_ID = 100
const val APP_UID = USER_ID * UserHandle.PER_USER_RANGE
+ const val SUPERVISING_USER_ID = 10
}
}
@@ -286,10 +321,19 @@ class SupervisionServiceTest {
* A context wrapper that allows broadcast intents to immediately invoke the receivers without
* performing checks on the sending user.
*/
-private class SupervisionContextWrapper(val context: Context, val pkgManager: PackageManager) :
- ContextWrapper(context) {
+private class SupervisionContextWrapper(
+ val context: Context,
+ val keyguardManager: KeyguardManager,
+ val pkgManager: PackageManager,
+) : ContextWrapper(context) {
val interceptors = mutableListOf<Pair<BroadcastReceiver, IntentFilter>>()
+ override fun getSystemService(name: String): Any =
+ when (name) {
+ Context.KEYGUARD_SERVICE -> keyguardManager
+ else -> super.getSystemService(name)
+ }
+
override fun getPackageManager() = pkgManager
override fun registerReceiverForAllUsers(
diff --git a/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java b/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java
index 857a9767d9ee..842c441e09f2 100644
--- a/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java
+++ b/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java
@@ -452,14 +452,14 @@ public class SystemConfigTest {
+ " <library \n"
+ " name=\"foo\"\n"
+ " file=\"" + mFooJar + "\"\n"
- + " on-bootclasspath-before=\"A\"\n"
+ + " on-bootclasspath-before=\"Q\"\n"
+ " on-bootclasspath-since=\"W\"\n"
+ " />\n\n"
+ " </permissions>";
parseSharedLibraries(contents);
assertFooIsOnlySharedLibrary();
SystemConfig.SharedLibraryEntry entry = mSysConfig.getSharedLibraries().get("foo");
- assertThat(entry.onBootclasspathBefore).isEqualTo("A");
+ assertThat(entry.onBootclasspathBefore).isEqualTo("Q");
assertThat(entry.onBootclasspathSince).isEqualTo("W");
}
diff --git a/services/tests/servicestests/test-apps/TopologyTestApp/Android.bp b/services/tests/servicestests/test-apps/TopologyTestApp/Android.bp
new file mode 100644
index 000000000000..dcf9cc216687
--- /dev/null
+++ b/services/tests/servicestests/test-apps/TopologyTestApp/Android.bp
@@ -0,0 +1,38 @@
+// Copyright (C) 2025 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 {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+android_test_helper_app {
+ name: "TopologyTestApp",
+
+ srcs: ["**/*.java"],
+
+ dex_preopt: {
+ enabled: false,
+ },
+ optimize: {
+ enabled: false,
+ },
+
+ platform_apis: true,
+ certificate: "platform",
+}
diff --git a/services/tests/servicestests/test-apps/TopologyTestApp/AndroidManifest.xml b/services/tests/servicestests/test-apps/TopologyTestApp/AndroidManifest.xml
new file mode 100644
index 000000000000..dad2315148df
--- /dev/null
+++ b/services/tests/servicestests/test-apps/TopologyTestApp/AndroidManifest.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2025 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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.servicestests.apps.topologytestapp">
+
+ <uses-permission android:name="android.permission.MANAGE_DISPLAYS" />
+
+ <application android:label="TopologyUpdateTestApp">
+ <activity android:name="com.android.servicestests.apps.topologytestapp.TopologyUpdateActivity"
+ android:exported="true" />
+ </application>
+
+</manifest>
diff --git a/services/tests/servicestests/test-apps/TopologyTestApp/OWNERS b/services/tests/servicestests/test-apps/TopologyTestApp/OWNERS
new file mode 100644
index 000000000000..e9557f84f8fb
--- /dev/null
+++ b/services/tests/servicestests/test-apps/TopologyTestApp/OWNERS
@@ -0,0 +1,3 @@
+# Bug component: 345010
+
+include /services/core/java/com/android/server/display/OWNERS
diff --git a/services/tests/servicestests/test-apps/TopologyTestApp/src/com/android/servicestests/apps/topologytestapp/TopologyUpdateActivity.java b/services/tests/servicestests/test-apps/TopologyTestApp/src/com/android/servicestests/apps/topologytestapp/TopologyUpdateActivity.java
new file mode 100644
index 000000000000..b35ba3c2c60c
--- /dev/null
+++ b/services/tests/servicestests/test-apps/TopologyTestApp/src/com/android/servicestests/apps/topologytestapp/TopologyUpdateActivity.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2025 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.servicestests.apps.topologytestapp;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.hardware.display.DisplayManager;
+import android.hardware.display.DisplayTopology;
+import android.os.Bundle;
+import android.os.Message;
+import android.os.Messenger;
+import android.os.Process;
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.util.function.Consumer;
+
+/**
+ * A simple activity listening to topology updates
+ */
+public class TopologyUpdateActivity extends Activity {
+ public static final int MESSAGE_LAUNCHED = 1;
+ public static final int MESSAGE_CALLBACK = 2;
+
+ private static final String TAG = TopologyUpdateActivity.class.getSimpleName();
+
+ private static final String TEST_MESSENGER = "MESSENGER";
+
+ private Messenger mMessenger;
+ private DisplayManager mDisplayManager;
+ private final Consumer<DisplayTopology> mTopologyListener = this::callback;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ Intent intent = getIntent();
+ mMessenger = intent.getParcelableExtra(TEST_MESSENGER, Messenger.class);
+ mDisplayManager = getApplicationContext().getSystemService(DisplayManager.class);
+ mDisplayManager.registerTopologyListener(getMainExecutor(), mTopologyListener);
+ launched();
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ mDisplayManager.unregisterTopologyListener(mTopologyListener);
+ }
+
+ private void launched() {
+ try {
+ Message msg = Message.obtain();
+ msg.what = MESSAGE_LAUNCHED;
+ msg.arg1 = android.os.Process.myPid();
+ msg.arg2 = Process.myUid();
+ Log.d(TAG, "Launched");
+ mMessenger.send(msg);
+ } catch (RemoteException e) {
+ e.rethrowAsRuntimeException();
+ }
+ }
+
+ private void callback(DisplayTopology topology) {
+ try {
+ Message msg = Message.obtain();
+ msg.what = MESSAGE_CALLBACK;
+ msg.obj = topology;
+ Log.d(TAG, "Msg " + topology);
+ mMessenger.send(msg);
+ } catch (RemoteException e) {
+ e.rethrowAsRuntimeException();
+ }
+ }
+}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index bc8b7becc919..8c9b9bd03b9f 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -16844,22 +16844,22 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
public void updateAutomaticZenRule_implicitRuleWithoutCPS_disallowedFromApp() throws Exception {
setUpRealZenTest();
mService.setCallerIsNormalPackage();
- assertThat(mBinderService.getAutomaticZenRules()).isEmpty();
+ assertThat(mBinderService.getAutomaticZenRules().getList()).isEmpty();
// Create an implicit zen rule by calling setNotificationPolicy from an app.
mBinderService.setNotificationPolicy(mPkg, new NotificationManager.Policy(0, 0, 0), false);
- assertThat(mBinderService.getAutomaticZenRules()).hasSize(1);
- Map.Entry<String, AutomaticZenRule> rule = getOnlyElement(
- mBinderService.getAutomaticZenRules().entrySet());
- assertThat(rule.getValue().getOwner()).isNull();
- assertThat(rule.getValue().getConfigurationActivity()).isNull();
+ assertThat(mBinderService.getAutomaticZenRules().getList()).hasSize(1);
+ AutomaticZenRule.AzrWithId rule = getOnlyElement(
+ (List<AutomaticZenRule.AzrWithId>) mBinderService.getAutomaticZenRules().getList());
+ assertThat(rule.mRule.getOwner()).isNull();
+ assertThat(rule.mRule.getConfigurationActivity()).isNull();
// Now try to update said rule (e.g. disable it). Should fail.
// We also validate the exception message because NPE could be thrown by all sorts of test
// issues (e.g. misconfigured mocks).
- rule.getValue().setEnabled(false);
+ rule.mRule.setEnabled(false);
NullPointerException e = assertThrows(NullPointerException.class,
- () -> mBinderService.updateAutomaticZenRule(rule.getKey(), rule.getValue(), false));
+ () -> mBinderService.updateAutomaticZenRule(rule.mId, rule.mRule, false));
assertThat(e.getMessage()).isEqualTo(
"Rule must have a ConditionProviderService and/or configuration activity");
}
@@ -16869,24 +16869,24 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
public void updateAutomaticZenRule_implicitRuleWithoutCPS_allowedFromSystem() throws Exception {
setUpRealZenTest();
mService.setCallerIsNormalPackage();
- assertThat(mBinderService.getAutomaticZenRules()).isEmpty();
+ assertThat(mBinderService.getAutomaticZenRules().getList()).isEmpty();
// Create an implicit zen rule by calling setNotificationPolicy from an app.
mBinderService.setNotificationPolicy(mPkg, new NotificationManager.Policy(0, 0, 0), false);
- assertThat(mBinderService.getAutomaticZenRules()).hasSize(1);
- Map.Entry<String, AutomaticZenRule> rule = getOnlyElement(
- mBinderService.getAutomaticZenRules().entrySet());
- assertThat(rule.getValue().getOwner()).isNull();
- assertThat(rule.getValue().getConfigurationActivity()).isNull();
+ assertThat(mBinderService.getAutomaticZenRules().getList()).hasSize(1);
+ AutomaticZenRule.AzrWithId rule = getOnlyElement(
+ (List<AutomaticZenRule.AzrWithId>) mBinderService.getAutomaticZenRules().getList());
+ assertThat(rule.mRule.getOwner()).isNull();
+ assertThat(rule.mRule.getConfigurationActivity()).isNull();
// Now update said rule from Settings (e.g. disable it). Should work!
mService.isSystemUid = true;
- rule.getValue().setEnabled(false);
- mBinderService.updateAutomaticZenRule(rule.getKey(), rule.getValue(), false);
+ rule.mRule.setEnabled(false);
+ mBinderService.updateAutomaticZenRule(rule.mId, rule.mRule, false);
- Map.Entry<String, AutomaticZenRule> updatedRule = getOnlyElement(
- mBinderService.getAutomaticZenRules().entrySet());
- assertThat(updatedRule.getValue().isEnabled()).isFalse();
+ AutomaticZenRule.AzrWithId updatedRule = getOnlyElement(
+ (List<AutomaticZenRule.AzrWithId>) mBinderService.getAutomaticZenRules().getList());
+ assertThat(updatedRule.mRule.isEnabled()).isFalse();
}
@Test
@@ -18657,4 +18657,37 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
verify(mGroupHelper).onNotificationRemoved(eq(n), any(), eq(false));
}
+
+ @Test
+ public void onDisplayRemoveSystemDecorations_cancelToasts() throws RemoteException {
+ final String testPackage = "testPackageName";
+ final INotificationManager service = ((INotificationManager) mService.mService);
+ final IBinder firstExternal = new Binder();
+ final IBinder secondExternal = new Binder();
+ final IBinder firstBuiltin = new Binder();
+ service.enqueueTextToast(testPackage,
+ firstExternal, "First external", TOAST_DURATION,
+ /* isUiContext= */ true, /* displayId= */ 10, /* callback= */ null);
+ service.enqueueTextToast(testPackage,
+ secondExternal, "Second external", TOAST_DURATION,
+ /* isUiContext= */ true, /* displayId= */ 10, /* callback= */ null);
+ service.enqueueTextToast(testPackage,
+ firstBuiltin, "First built-in", TOAST_DURATION, /* isUiContext= */ true,
+ /* displayId= */ DEFAULT_DISPLAY, /* callback= */ null);
+
+ mInternalService.onDisplayRemoveSystemDecorations(10);
+
+ verify(mStatusBar).showToast(anyInt(), eq(testPackage), eq(firstExternal),
+ any(String.class), any(IBinder.class), anyInt(), any(), eq(10));
+ verify(mStatusBar).hideToast(eq(testPackage), eq(firstExternal));
+ // The second toast has not been shown but invokes hide() anyway as
+ // NotificationManagerService does not remembered if it invoked show().
+ verify(mStatusBar, never()).showToast(anyInt(), eq(testPackage), eq(secondExternal),
+ any(String.class), any(IBinder.class), anyInt(), any(), eq(10));
+ verify(mStatusBar).hideToast(eq(testPackage), eq(secondExternal));
+ // The toast on the default display is shown as other notifications are cancelled.
+ verify(mStatusBar).showToast(anyInt(), eq(testPackage), eq(firstBuiltin), any(String.class),
+ any(IBinder.class), anyInt(), any(), eq(DEFAULT_DISPLAY));
+ verify(mStatusBar, never()).hideToast(eq(testPackage), eq(firstBuiltin));
+ }
}
diff --git a/services/tests/wmtests/src/com/android/server/policy/CombinationKeyTests.java b/services/tests/wmtests/src/com/android/server/policy/CombinationKeyTests.java
index 036e03c60091..e473a06b25e7 100644
--- a/services/tests/wmtests/src/com/android/server/policy/CombinationKeyTests.java
+++ b/services/tests/wmtests/src/com/android/server/policy/CombinationKeyTests.java
@@ -49,6 +49,7 @@ public class CombinationKeyTests extends ShortcutKeyTestBase {
@Before
public void setUp() {
setUpPhoneWindowManager();
+ mPhoneWindowManager.overrideStatusBarManagerInternal();
}
/**
diff --git a/services/tests/wmtests/src/com/android/server/policy/KeyGestureEventTests.java b/services/tests/wmtests/src/com/android/server/policy/KeyGestureEventTests.java
index fcdf88f16550..5ab00361d3c0 100644
--- a/services/tests/wmtests/src/com/android/server/policy/KeyGestureEventTests.java
+++ b/services/tests/wmtests/src/com/android/server/policy/KeyGestureEventTests.java
@@ -39,8 +39,6 @@ import androidx.test.filters.MediumTest;
import com.android.hardware.input.Flags;
import com.android.internal.annotations.Keep;
-import junit.framework.Assert;
-
import junitparams.JUnitParamsRunner;
import junitparams.Parameters;
@@ -433,112 +431,99 @@ public class KeyGestureEventTests extends ShortcutKeyTestBase {
@Test
public void testKeyGestureRecentApps() {
- Assert.assertTrue(
- sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_RECENT_APPS));
+ sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_RECENT_APPS);
mPhoneWindowManager.assertShowRecentApps();
}
@Test
public void testKeyGestureAppSwitch() {
- Assert.assertTrue(
- sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_APP_SWITCH));
+ sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_APP_SWITCH);
mPhoneWindowManager.assertToggleRecentApps();
}
@Test
public void testKeyGestureLaunchAssistant() {
- Assert.assertTrue(
- sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_ASSISTANT));
+ sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_ASSISTANT);
mPhoneWindowManager.assertSearchManagerLaunchAssist();
}
@Test
public void testKeyGestureLaunchVoiceAssistant() {
- Assert.assertTrue(
- sendKeyGestureEventComplete(
- KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_VOICE_ASSISTANT));
+ sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_VOICE_ASSISTANT);
mPhoneWindowManager.assertSearchManagerLaunchAssist();
}
@Test
public void testKeyGestureGoHome() {
- Assert.assertTrue(
- sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_HOME));
+ sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_HOME);
mPhoneWindowManager.assertGoToHomescreen();
}
@Test
public void testKeyGestureLaunchSystemSettings() {
- Assert.assertTrue(
- sendKeyGestureEventComplete(
- KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_SYSTEM_SETTINGS));
+ sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_SYSTEM_SETTINGS);
mPhoneWindowManager.assertLaunchSystemSettings();
}
@Test
public void testKeyGestureLock() {
- Assert.assertTrue(
- sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_LOCK_SCREEN));
+ sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_LOCK_SCREEN);
mPhoneWindowManager.assertLockedAfterAppTransitionFinished();
}
@Test
public void testKeyGestureToggleNotificationPanel() throws RemoteException {
- Assert.assertTrue(
- sendKeyGestureEventComplete(
- KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_NOTIFICATION_PANEL));
+ sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_NOTIFICATION_PANEL);
mPhoneWindowManager.assertTogglePanel();
}
@Test
public void testKeyGestureScreenshot() {
- Assert.assertTrue(
- sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_TAKE_SCREENSHOT));
+ sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_TAKE_SCREENSHOT);
mPhoneWindowManager.assertTakeScreenshotCalled();
}
@Test
public void testKeyGestureTriggerBugReport() throws RemoteException {
- Assert.assertTrue(
- sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_TRIGGER_BUG_REPORT));
+ sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_TRIGGER_BUG_REPORT);
mPhoneWindowManager.assertTakeBugreport(true);
}
@Test
public void testKeyGestureBack() {
- Assert.assertTrue(sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_BACK));
+ mPhoneWindowManager.overrideDelegateBackGestureRemote(true);
+ sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_BACK);
+ mPhoneWindowManager.assertBackEventInjected();
+
+ mPhoneWindowManager.overrideDelegateBackGestureRemote(false);
+ sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_BACK);
mPhoneWindowManager.assertBackEventInjected();
}
@Test
public void testKeyGestureMultiWindowNavigation() {
- Assert.assertTrue(sendKeyGestureEventComplete(
- KeyGestureEvent.KEY_GESTURE_TYPE_MULTI_WINDOW_NAVIGATION));
+ sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_MULTI_WINDOW_NAVIGATION);
mPhoneWindowManager.assertMoveFocusedTaskToFullscreen();
}
@Test
public void testKeyGestureDesktopMode() {
- Assert.assertTrue(
- sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_DESKTOP_MODE));
+ sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_DESKTOP_MODE);
mPhoneWindowManager.assertMoveFocusedTaskToDesktop();
}
@Test
public void testKeyGestureSplitscreenNavigation() {
- Assert.assertTrue(sendKeyGestureEventComplete(
- KeyGestureEvent.KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION_LEFT));
+ sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION_LEFT);
mPhoneWindowManager.assertMoveFocusedTaskToStageSplit(true);
- Assert.assertTrue(sendKeyGestureEventComplete(
- KeyGestureEvent.KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION_RIGHT));
+ sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION_RIGHT);
mPhoneWindowManager.assertMoveFocusedTaskToStageSplit(false);
}
@Test
public void testKeyGestureShortcutHelper() {
- Assert.assertTrue(sendKeyGestureEventComplete(
- KeyGestureEvent.KEY_GESTURE_TYPE_OPEN_SHORTCUT_HELPER));
+ sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_OPEN_SHORTCUT_HELPER);
mPhoneWindowManager.assertToggleShortcutsMenu();
}
@@ -549,173 +534,139 @@ public class KeyGestureEventTests extends ShortcutKeyTestBase {
for (int i = 0; i < currentBrightness.length; i++) {
mPhoneWindowManager.prepareBrightnessDecrease(currentBrightness[i]);
- Assert.assertTrue(
- sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_BRIGHTNESS_DOWN));
+ sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_BRIGHTNESS_DOWN);
mPhoneWindowManager.verifyNewBrightness(newBrightness[i]);
}
}
@Test
public void testKeyGestureRecentAppSwitcher() {
- Assert.assertTrue(sendKeyGestureEventStart(
- KeyGestureEvent.KEY_GESTURE_TYPE_RECENT_APPS_SWITCHER));
+ sendKeyGestureEventStart(KeyGestureEvent.KEY_GESTURE_TYPE_RECENT_APPS_SWITCHER);
mPhoneWindowManager.assertShowRecentApps();
-
- Assert.assertTrue(sendKeyGestureEventComplete(
- KeyGestureEvent.KEY_GESTURE_TYPE_RECENT_APPS_SWITCHER));
+ sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_RECENT_APPS_SWITCHER);
mPhoneWindowManager.assertHideRecentApps();
}
@Test
public void testKeyGestureLanguageSwitch() {
- Assert.assertTrue(
- sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_LANGUAGE_SWITCH));
+ sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_LANGUAGE_SWITCH);
mPhoneWindowManager.assertSwitchKeyboardLayout(1, DEFAULT_DISPLAY);
- Assert.assertTrue(
- sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_LANGUAGE_SWITCH,
- KeyEvent.META_SHIFT_ON));
+ sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_LANGUAGE_SWITCH,
+ KeyEvent.META_SHIFT_ON);
mPhoneWindowManager.assertSwitchKeyboardLayout(-1, DEFAULT_DISPLAY);
}
@Test
public void testKeyGestureLaunchSearch() {
- Assert.assertTrue(
- sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_SEARCH));
+ sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_SEARCH);
mPhoneWindowManager.assertLaunchSearch();
}
@Test
public void testKeyGestureScreenshotChord() {
- Assert.assertTrue(
- sendKeyGestureEventStart(KeyGestureEvent.KEY_GESTURE_TYPE_SCREENSHOT_CHORD));
+ sendKeyGestureEventStart(KeyGestureEvent.KEY_GESTURE_TYPE_SCREENSHOT_CHORD);
mPhoneWindowManager.moveTimeForward(500);
- Assert.assertTrue(
- sendKeyGestureEventCancel(KeyGestureEvent.KEY_GESTURE_TYPE_SCREENSHOT_CHORD));
+ sendKeyGestureEventCancel(KeyGestureEvent.KEY_GESTURE_TYPE_SCREENSHOT_CHORD);
mPhoneWindowManager.assertTakeScreenshotCalled();
}
@Test
public void testKeyGestureScreenshotChordCancelled() {
- Assert.assertTrue(
- sendKeyGestureEventStart(KeyGestureEvent.KEY_GESTURE_TYPE_SCREENSHOT_CHORD));
- Assert.assertTrue(
- sendKeyGestureEventCancel(KeyGestureEvent.KEY_GESTURE_TYPE_SCREENSHOT_CHORD));
+ sendKeyGestureEventStart(KeyGestureEvent.KEY_GESTURE_TYPE_SCREENSHOT_CHORD);
+ sendKeyGestureEventCancel(KeyGestureEvent.KEY_GESTURE_TYPE_SCREENSHOT_CHORD);
mPhoneWindowManager.assertTakeScreenshotNotCalled();
}
@Test
public void testKeyGestureRingerToggleChord() {
mPhoneWindowManager.overridePowerVolumeUp(POWER_VOLUME_UP_BEHAVIOR_MUTE);
- Assert.assertTrue(
- sendKeyGestureEventStart(KeyGestureEvent.KEY_GESTURE_TYPE_RINGER_TOGGLE_CHORD));
+ sendKeyGestureEventStart(KeyGestureEvent.KEY_GESTURE_TYPE_RINGER_TOGGLE_CHORD);
mPhoneWindowManager.moveTimeForward(500);
- Assert.assertTrue(
- sendKeyGestureEventCancel(KeyGestureEvent.KEY_GESTURE_TYPE_RINGER_TOGGLE_CHORD));
+ sendKeyGestureEventCancel(KeyGestureEvent.KEY_GESTURE_TYPE_RINGER_TOGGLE_CHORD);
mPhoneWindowManager.assertVolumeMute();
}
@Test
public void testKeyGestureRingerToggleChordCancelled() {
mPhoneWindowManager.overridePowerVolumeUp(POWER_VOLUME_UP_BEHAVIOR_MUTE);
- Assert.assertTrue(
- sendKeyGestureEventStart(KeyGestureEvent.KEY_GESTURE_TYPE_RINGER_TOGGLE_CHORD));
- Assert.assertTrue(
- sendKeyGestureEventCancel(KeyGestureEvent.KEY_GESTURE_TYPE_RINGER_TOGGLE_CHORD));
+ sendKeyGestureEventStart(KeyGestureEvent.KEY_GESTURE_TYPE_RINGER_TOGGLE_CHORD);
+ sendKeyGestureEventCancel(KeyGestureEvent.KEY_GESTURE_TYPE_RINGER_TOGGLE_CHORD);
mPhoneWindowManager.assertVolumeNotMuted();
}
@Test
public void testKeyGestureGlobalAction() {
mPhoneWindowManager.overridePowerVolumeUp(POWER_VOLUME_UP_BEHAVIOR_GLOBAL_ACTIONS);
- Assert.assertTrue(
- sendKeyGestureEventStart(KeyGestureEvent.KEY_GESTURE_TYPE_GLOBAL_ACTIONS));
+ sendKeyGestureEventStart(KeyGestureEvent.KEY_GESTURE_TYPE_GLOBAL_ACTIONS);
mPhoneWindowManager.moveTimeForward(500);
- Assert.assertTrue(
- sendKeyGestureEventCancel(KeyGestureEvent.KEY_GESTURE_TYPE_GLOBAL_ACTIONS));
+ sendKeyGestureEventCancel(KeyGestureEvent.KEY_GESTURE_TYPE_GLOBAL_ACTIONS);
mPhoneWindowManager.assertShowGlobalActionsCalled();
}
@Test
public void testKeyGestureGlobalActionCancelled() {
mPhoneWindowManager.overridePowerVolumeUp(POWER_VOLUME_UP_BEHAVIOR_GLOBAL_ACTIONS);
- Assert.assertTrue(
- sendKeyGestureEventStart(KeyGestureEvent.KEY_GESTURE_TYPE_GLOBAL_ACTIONS));
- Assert.assertTrue(
- sendKeyGestureEventCancel(KeyGestureEvent.KEY_GESTURE_TYPE_GLOBAL_ACTIONS));
+ sendKeyGestureEventStart(KeyGestureEvent.KEY_GESTURE_TYPE_GLOBAL_ACTIONS);
+ sendKeyGestureEventCancel(KeyGestureEvent.KEY_GESTURE_TYPE_GLOBAL_ACTIONS);
mPhoneWindowManager.assertShowGlobalActionsNotCalled();
}
@Test
public void testKeyGestureTvTriggerBugReport() {
- Assert.assertTrue(
- sendKeyGestureEventStart(KeyGestureEvent.KEY_GESTURE_TYPE_TV_TRIGGER_BUG_REPORT));
+ sendKeyGestureEventStart(KeyGestureEvent.KEY_GESTURE_TYPE_TV_TRIGGER_BUG_REPORT);
mPhoneWindowManager.moveTimeForward(1000);
- Assert.assertTrue(
- sendKeyGestureEventCancel(KeyGestureEvent.KEY_GESTURE_TYPE_TV_TRIGGER_BUG_REPORT));
+ sendKeyGestureEventCancel(KeyGestureEvent.KEY_GESTURE_TYPE_TV_TRIGGER_BUG_REPORT);
mPhoneWindowManager.assertBugReportTakenForTv();
}
@Test
public void testKeyGestureTvTriggerBugReportCancelled() {
- Assert.assertTrue(
- sendKeyGestureEventStart(KeyGestureEvent.KEY_GESTURE_TYPE_TV_TRIGGER_BUG_REPORT));
- Assert.assertTrue(
- sendKeyGestureEventCancel(KeyGestureEvent.KEY_GESTURE_TYPE_TV_TRIGGER_BUG_REPORT));
+ sendKeyGestureEventStart(KeyGestureEvent.KEY_GESTURE_TYPE_TV_TRIGGER_BUG_REPORT);
+ sendKeyGestureEventCancel(KeyGestureEvent.KEY_GESTURE_TYPE_TV_TRIGGER_BUG_REPORT);
mPhoneWindowManager.assertBugReportNotTakenForTv();
}
@Test
public void testKeyGestureAccessibilityShortcut() {
- Assert.assertTrue(
- sendKeyGestureEventComplete(
- KeyGestureEvent.KEY_GESTURE_TYPE_ACCESSIBILITY_SHORTCUT));
+ sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_ACCESSIBILITY_SHORTCUT);
mPhoneWindowManager.assertAccessibilityKeychordCalled();
}
@Test
public void testKeyGestureCloseAllDialogs() {
- Assert.assertTrue(
- sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_CLOSE_ALL_DIALOGS));
+ sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_CLOSE_ALL_DIALOGS);
mPhoneWindowManager.assertCloseAllDialogs();
}
@Test
@EnableFlags(com.android.hardware.input.Flags.FLAG_ENABLE_TALKBACK_AND_MAGNIFIER_KEY_GESTURES)
public void testKeyGestureToggleTalkback() {
- Assert.assertTrue(
- sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_TALKBACK));
+ sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_TALKBACK);
mPhoneWindowManager.assertTalkBack(true);
- Assert.assertTrue(
- sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_TALKBACK));
+ sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_TALKBACK);
mPhoneWindowManager.assertTalkBack(false);
}
@Test
@EnableFlags(com.android.hardware.input.Flags.FLAG_ENABLE_VOICE_ACCESS_KEY_GESTURES)
public void testKeyGestureToggleVoiceAccess() {
- Assert.assertTrue(
- sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_VOICE_ACCESS));
+ sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_VOICE_ACCESS);
mPhoneWindowManager.assertVoiceAccess(true);
- Assert.assertTrue(
- sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_VOICE_ACCESS));
+ sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_VOICE_ACCESS);
mPhoneWindowManager.assertVoiceAccess(false);
}
@Test
public void testKeyGestureToggleDoNotDisturb() {
mPhoneWindowManager.overrideZenMode(Settings.Global.ZEN_MODE_OFF);
- Assert.assertTrue(
- sendKeyGestureEventComplete(
- KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_DO_NOT_DISTURB));
+ sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_DO_NOT_DISTURB);
mPhoneWindowManager.assertZenMode(Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS);
mPhoneWindowManager.overrideZenMode(Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS);
- Assert.assertTrue(
- sendKeyGestureEventComplete(
- KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_DO_NOT_DISTURB));
+ sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_DO_NOT_DISTURB);
mPhoneWindowManager.assertZenMode(Settings.Global.ZEN_MODE_OFF);
}
diff --git a/services/tests/wmtests/src/com/android/server/policy/PhoneWindowManagerTests.java b/services/tests/wmtests/src/com/android/server/policy/PhoneWindowManagerTests.java
index 32a3b7f2c9cc..8d164e1acf74 100644
--- a/services/tests/wmtests/src/com/android/server/policy/PhoneWindowManagerTests.java
+++ b/services/tests/wmtests/src/com/android/server/policy/PhoneWindowManagerTests.java
@@ -35,6 +35,7 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
import static com.android.server.policy.PhoneWindowManager.EXTRA_TRIGGER_HUB;
+import static com.android.server.policy.PhoneWindowManager.SHORT_PRESS_POWER_DREAM_OR_AWAKE_OR_SLEEP;
import static com.android.server.policy.PhoneWindowManager.SHORT_PRESS_POWER_HUB_OR_DREAM_OR_SLEEP;
import static com.google.common.truth.Truth.assertThat;
@@ -43,6 +44,7 @@ import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyList;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
@@ -51,18 +53,21 @@ import android.app.AppOpsManager;
import android.content.Context;
import android.hardware.input.InputManager;
import android.os.Bundle;
+import android.os.IBinder;
import android.os.PowerManager;
import android.os.PowerManagerInternal;
+import android.platform.test.annotations.EnableFlags;
import android.platform.test.annotations.Presubmit;
import android.platform.test.flag.junit.SetFlagsRule;
import android.provider.Settings;
import android.service.dreams.DreamManagerInternal;
import android.testing.TestableContext;
-import android.view.contentprotection.flags.Flags;
+import android.view.KeyEvent;
import androidx.test.filters.SmallTest;
import com.android.internal.util.test.LocalServiceKeeperRule;
+import com.android.internal.widget.LockPatternUtils;
import com.android.server.input.InputManagerInternal;
import com.android.server.pm.UserManagerInternal;
import com.android.server.policy.keyguard.KeyguardServiceDelegate;
@@ -99,6 +104,8 @@ public class PhoneWindowManagerTests {
public final TestableContext mContext = spy(
new TestableContext(getInstrumentation().getContext()));
+ @Mock private IBinder mInputToken;
+
PhoneWindowManager mPhoneWindowManager;
@Mock
private ActivityTaskManagerInternal mAtmInternal;
@@ -119,6 +126,10 @@ public class PhoneWindowManagerTests {
private DisplayPolicy mDisplayPolicy;
@Mock
private KeyguardServiceDelegate mKeyguardServiceDelegate;
+ @Mock
+ private LockPatternUtils mLockPatternUtils;
+
+ private static final int INTERCEPT_SYSTEM_KEY_NOT_CONSUMED_DELAY = 0;
@Before
public void setUp() {
@@ -146,7 +157,7 @@ public class PhoneWindowManagerTests {
mPhoneWindowManager.mKeyguardDelegate = mKeyguardServiceDelegate;
final InputManager im = mock(InputManager.class);
- doNothing().when(im).registerKeyGestureEventHandler(any());
+ doNothing().when(im).registerKeyGestureEventHandler(anyList(), any());
doReturn(im).when(mContext).getSystemService(eq(Context.INPUT_SERVICE));
}
@@ -211,7 +222,7 @@ public class PhoneWindowManagerTests {
@Test
public void testCheckAddPermission_withoutAccessibilityOverlay_noAccessibilityAppOpLogged() {
- mSetFlagsRule.enableFlags(Flags.FLAG_CREATE_ACCESSIBILITY_OVERLAY_APP_OP_ENABLED);
+ mSetFlagsRule.enableFlags(android.view.contentprotection.flags.Flags.FLAG_CREATE_ACCESSIBILITY_OVERLAY_APP_OP_ENABLED);
int[] outAppOp = new int[1];
assertEquals(ADD_OKAY, mPhoneWindowManager.checkAddPermission(TYPE_WALLPAPER,
/* isRoundedCornerOverlay= */ false, "test.pkg", outAppOp, DEFAULT_DISPLAY));
@@ -220,7 +231,7 @@ public class PhoneWindowManagerTests {
@Test
public void testCheckAddPermission_withAccessibilityOverlay() {
- mSetFlagsRule.enableFlags(Flags.FLAG_CREATE_ACCESSIBILITY_OVERLAY_APP_OP_ENABLED);
+ mSetFlagsRule.enableFlags(android.view.contentprotection.flags.Flags.FLAG_CREATE_ACCESSIBILITY_OVERLAY_APP_OP_ENABLED);
int[] outAppOp = new int[1];
assertEquals(ADD_OKAY, mPhoneWindowManager.checkAddPermission(TYPE_ACCESSIBILITY_OVERLAY,
/* isRoundedCornerOverlay= */ false, "test.pkg", outAppOp, DEFAULT_DISPLAY));
@@ -229,7 +240,7 @@ public class PhoneWindowManagerTests {
@Test
public void testCheckAddPermission_withAccessibilityOverlay_flagDisabled() {
- mSetFlagsRule.disableFlags(Flags.FLAG_CREATE_ACCESSIBILITY_OVERLAY_APP_OP_ENABLED);
+ mSetFlagsRule.disableFlags(android.view.contentprotection.flags.Flags.FLAG_CREATE_ACCESSIBILITY_OVERLAY_APP_OP_ENABLED);
int[] outAppOp = new int[1];
assertEquals(ADD_OKAY, mPhoneWindowManager.checkAddPermission(TYPE_ACCESSIBILITY_OVERLAY,
/* isRoundedCornerOverlay= */ false, "test.pkg", outAppOp, DEFAULT_DISPLAY));
@@ -253,6 +264,7 @@ public class PhoneWindowManagerTests {
@Test
public void powerPress_hubOrDreamOrSleep_goesToSleepFromDream() {
when(mDisplayPolicy.isAwake()).thenReturn(true);
+ when(mLockPatternUtils.isLockScreenDisabled(anyInt())).thenReturn(false);
initPhoneWindowManager();
// Set power button behavior.
@@ -274,6 +286,7 @@ public class PhoneWindowManagerTests {
@Test
public void powerPress_hubOrDreamOrSleep_hubAvailableLocks() {
when(mDisplayPolicy.isAwake()).thenReturn(true);
+ when(mLockPatternUtils.isLockScreenDisabled(anyInt())).thenReturn(false);
mContext.getTestablePermissions().setPermission(android.Manifest.permission.DEVICE_POWER,
PERMISSION_GRANTED);
initPhoneWindowManager();
@@ -302,6 +315,7 @@ public class PhoneWindowManagerTests {
@Test
public void powerPress_hubOrDreamOrSleep_hubNotAvailableDreams() {
when(mDisplayPolicy.isAwake()).thenReturn(true);
+ when(mLockPatternUtils.isLockScreenDisabled(anyInt())).thenReturn(false);
initPhoneWindowManager();
// Set power button behavior.
@@ -322,6 +336,106 @@ public class PhoneWindowManagerTests {
verify(mDreamManagerInternal).requestDream();
}
+ @Test
+ public void powerPress_dreamOrAwakeOrSleep_awakeFromDream() {
+ when(mDisplayPolicy.isAwake()).thenReturn(true);
+ initPhoneWindowManager();
+
+ // Set power button behavior.
+ Settings.Global.putInt(mContext.getContentResolver(),
+ Settings.Global.POWER_BUTTON_SHORT_PRESS,
+ SHORT_PRESS_POWER_DREAM_OR_AWAKE_OR_SLEEP);
+ mPhoneWindowManager.updateSettings(null);
+
+ // Can not dream when device is dreaming.
+ when(mDreamManagerInternal.canStartDreaming(any(Boolean.class))).thenReturn(false);
+ // Device is dreaming.
+ when(mDreamManagerInternal.isDreaming()).thenReturn(true);
+
+ // Power button pressed.
+ int eventTime = 0;
+ mPhoneWindowManager.powerPress(eventTime, 1, 0);
+
+ // Dream is stopped.
+ verify(mDreamManagerInternal)
+ .stopDream(false /*immediate*/, "short press power" /*reason*/);
+ }
+
+ @Test
+ public void powerPress_dreamOrAwakeOrSleep_canNotDreamGoToSleep() {
+ when(mDisplayPolicy.isAwake()).thenReturn(true);
+ initPhoneWindowManager();
+
+ // Set power button behavior.
+ Settings.Global.putInt(mContext.getContentResolver(),
+ Settings.Global.POWER_BUTTON_SHORT_PRESS,
+ SHORT_PRESS_POWER_DREAM_OR_AWAKE_OR_SLEEP);
+ mPhoneWindowManager.updateSettings(null);
+
+ // Can not dream for other reasons.
+ when(mDreamManagerInternal.canStartDreaming(any(Boolean.class))).thenReturn(false);
+ // Device is not dreaming.
+ when(mDreamManagerInternal.isDreaming()).thenReturn(false);
+
+ // Power button pressed.
+ int eventTime = 0;
+ mPhoneWindowManager.powerPress(eventTime, 1, 0);
+
+ // Device goes to sleep.
+ verify(mPowerManager).goToSleep(eventTime, PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON, 0);
+ }
+
+ @Test
+ public void powerPress_dreamOrAwakeOrSleep_dreamFromActive() {
+ when(mDisplayPolicy.isAwake()).thenReturn(true);
+ initPhoneWindowManager();
+
+ // Set power button behavior.
+ Settings.Global.putInt(mContext.getContentResolver(),
+ Settings.Global.POWER_BUTTON_SHORT_PRESS,
+ SHORT_PRESS_POWER_DREAM_OR_AWAKE_OR_SLEEP);
+ mPhoneWindowManager.updateSettings(null);
+
+ // Can dream when active.
+ when(mDreamManagerInternal.canStartDreaming(any(Boolean.class))).thenReturn(true);
+
+ // Power button pressed.
+ int eventTime = 0;
+ mPhoneWindowManager.powerPress(eventTime, 1, 0);
+
+ // Dream is requested.
+ verify(mDreamManagerInternal).requestDream();
+ }
+
+ @EnableFlags(com.android.hardware.input.Flags.FLAG_FIX_SEARCH_MODIFIER_FALLBACKS)
+ public void testInterceptKeyBeforeDispatching() {
+ // Handle sub-tasks of init().
+ doNothing().when(mPhoneWindowManager).updateSettings(any());
+ doNothing().when(mPhoneWindowManager).initializeHdmiState();
+ final DisplayPolicy displayPolicy = mock(DisplayPolicy.class);
+ mPhoneWindowManager.mDefaultDisplayPolicy = displayPolicy;
+ mPhoneWindowManager.mDefaultDisplayRotation = mock(DisplayRotation.class);
+ final PowerManager pm = mock(PowerManager.class);
+ doReturn(true).when(pm).isInteractive();
+ doReturn(pm).when(mContext).getSystemService(eq(Context.POWER_SERVICE));
+
+ mContext.getMainThreadHandler().runWithScissors(() -> mPhoneWindowManager.init(
+ new PhoneWindowManager.Injector(mContext,
+ mock(WindowManagerPolicy.WindowManagerFuncs.class))), 0);
+
+ // Case: KeyNotConsumed with meta key.
+ KeyEvent keyEvent = new KeyEvent(0, 0, KeyEvent.ACTION_DOWN,
+ KeyEvent.KEYCODE_A, 0, KeyEvent.META_META_ON);
+ long result = mPhoneWindowManager.interceptKeyBeforeDispatching(mInputToken, keyEvent, 0);
+ assertEquals(INTERCEPT_SYSTEM_KEY_NOT_CONSUMED_DELAY, result);
+
+ // Case: KeyNotConsumed without meta key.
+ KeyEvent keyEvent1 = new KeyEvent(0, 0, KeyEvent.ACTION_DOWN,
+ KeyEvent.KEYCODE_ESCAPE, 0, 0);
+ long result1 = mPhoneWindowManager.interceptKeyBeforeDispatching(mInputToken, keyEvent1, 0);
+ assertEquals(INTERCEPT_SYSTEM_KEY_NOT_CONSUMED_DELAY, result1);
+ }
+
private void initPhoneWindowManager() {
mPhoneWindowManager.mDefaultDisplayPolicy = mDisplayPolicy;
mPhoneWindowManager.mDefaultDisplayRotation = mock(DisplayRotation.class);
@@ -345,6 +459,11 @@ public class PhoneWindowManagerTests {
return mKeyguardServiceDelegate;
}
+ @Override
+ LockPatternUtils getLockPatternUtils() {
+ return mLockPatternUtils;
+ }
+
/**
* {@code WindowWakeUpPolicy} registers a local service in its constructor, easier to just
* mock it out so we don't have to unregister it after every test.
diff --git a/services/tests/wmtests/src/com/android/server/policy/ShortcutKeyTestBase.java b/services/tests/wmtests/src/com/android/server/policy/ShortcutKeyTestBase.java
index c57adfd69b06..f89c6f638384 100644
--- a/services/tests/wmtests/src/com/android/server/policy/ShortcutKeyTestBase.java
+++ b/services/tests/wmtests/src/com/android/server/policy/ShortcutKeyTestBase.java
@@ -238,33 +238,33 @@ class ShortcutKeyTestBase {
sendKeyCombination(new int[]{keyCode}, durationMillis, false, DEFAULT_DISPLAY);
}
- boolean sendKeyGestureEventStart(int gestureType) {
- return mPhoneWindowManager.sendKeyGestureEvent(
+ void sendKeyGestureEventStart(int gestureType) {
+ mPhoneWindowManager.sendKeyGestureEvent(
new KeyGestureEvent.Builder().setKeyGestureType(gestureType).setAction(
KeyGestureEvent.ACTION_GESTURE_START).build());
}
- boolean sendKeyGestureEventComplete(int gestureType) {
- return mPhoneWindowManager.sendKeyGestureEvent(
+ void sendKeyGestureEventComplete(int gestureType) {
+ mPhoneWindowManager.sendKeyGestureEvent(
new KeyGestureEvent.Builder().setKeyGestureType(gestureType).setAction(
KeyGestureEvent.ACTION_GESTURE_COMPLETE).build());
}
- boolean sendKeyGestureEventCancel(int gestureType) {
- return mPhoneWindowManager.sendKeyGestureEvent(
+ void sendKeyGestureEventCancel(int gestureType) {
+ mPhoneWindowManager.sendKeyGestureEvent(
new KeyGestureEvent.Builder().setKeyGestureType(gestureType).setAction(
KeyGestureEvent.ACTION_GESTURE_COMPLETE).setFlags(
KeyGestureEvent.FLAG_CANCELLED).build());
}
- boolean sendKeyGestureEventComplete(int gestureType, int modifierState) {
- return mPhoneWindowManager.sendKeyGestureEvent(
+ void sendKeyGestureEventComplete(int gestureType, int modifierState) {
+ mPhoneWindowManager.sendKeyGestureEvent(
new KeyGestureEvent.Builder().setModifierState(modifierState).setKeyGestureType(
gestureType).setAction(KeyGestureEvent.ACTION_GESTURE_COMPLETE).build());
}
- boolean sendKeyGestureEventComplete(int keycode, int modifierState, int gestureType) {
- return mPhoneWindowManager.sendKeyGestureEvent(
+ void sendKeyGestureEventComplete(int keycode, int modifierState, int gestureType) {
+ mPhoneWindowManager.sendKeyGestureEvent(
new KeyGestureEvent.Builder().setKeycodes(new int[]{keycode}).setModifierState(
modifierState).setKeyGestureType(gestureType).setAction(
KeyGestureEvent.ACTION_GESTURE_COMPLETE).build());
diff --git a/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java b/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
index e56fd3c6272d..7059c41898f3 100644
--- a/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
+++ b/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
@@ -49,6 +49,7 @@ import static com.android.server.policy.PhoneWindowManager.POWER_VOLUME_UP_BEHAV
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyList;
import static org.mockito.ArgumentMatchers.isNull;
import static org.mockito.Mockito.CALLS_REAL_METHODS;
import static org.mockito.Mockito.after;
@@ -201,6 +202,7 @@ class TestPhoneWindowManager {
private boolean mIsTalkBackEnabled;
private boolean mIsTalkBackShortcutGestureEnabled;
+ private boolean mDelegateBackGestureRemote;
private boolean mIsVoiceAccessEnabled;
private Intent mBrowserIntent;
@@ -353,7 +355,7 @@ class TestPhoneWindowManager {
doReturn(mAppOpsManager).when(mContext).getSystemService(eq(AppOpsManager.class));
doReturn(mDisplayManager).when(mContext).getSystemService(eq(DisplayManager.class));
doReturn(mInputManager).when(mContext).getSystemService(eq(InputManager.class));
- doNothing().when(mInputManager).registerKeyGestureEventHandler(any());
+ doNothing().when(mInputManager).registerKeyGestureEventHandler(anyList(), any());
doNothing().when(mInputManager).unregisterKeyGestureEventHandler(any());
doReturn(mPackageManager).when(mContext).getPackageManager();
doReturn(mSensorPrivacyManager).when(mContext).getSystemService(
@@ -476,8 +478,8 @@ class TestPhoneWindowManager {
mPhoneWindowManager.interceptUnhandledKey(event, mInputToken);
}
- boolean sendKeyGestureEvent(KeyGestureEvent event) {
- return mPhoneWindowManager.handleKeyGestureEvent(event, mInputToken);
+ void sendKeyGestureEvent(KeyGestureEvent event) {
+ mPhoneWindowManager.handleKeyGestureEvent(event, mInputToken);
}
/**
@@ -579,6 +581,12 @@ class TestPhoneWindowManager {
setPhoneCallIsInProgress();
}
+ void overrideDelegateBackGestureRemote(boolean isDelegating) {
+ mDelegateBackGestureRemote = isDelegating;
+ doReturn(mDelegateBackGestureRemote).when(mActivityTaskManagerInternal)
+ .requestBackGesture();
+ }
+
void prepareBrightnessDecrease(float currentBrightness) {
doReturn(0.0f).when(mPowerManager).getBrightnessConstraint(
DEFAULT_DISPLAY, PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_MINIMUM);
@@ -660,13 +668,21 @@ class TestPhoneWindowManager {
}
void assertBackEventInjected() {
- ArgumentCaptor<InputEvent> intentCaptor = ArgumentCaptor.forClass(InputEvent.class);
- verify(mInputManager, times(2)).injectInputEvent(intentCaptor.capture(), anyInt());
- List<InputEvent> inputEvents = intentCaptor.getAllValues();
- Assert.assertEquals(KeyEvent.KEYCODE_BACK, ((KeyEvent) inputEvents.get(0)).getKeyCode());
- Assert.assertEquals(KeyEvent.KEYCODE_BACK, ((KeyEvent) inputEvents.get(1)).getKeyCode());
- // Reset verifier for next call.
- Mockito.clearInvocations(mContext);
+ if (mDelegateBackGestureRemote) {
+ Mockito.verify(mActivityTaskManagerInternal).requestBackGesture();
+ ArgumentCaptor<InputEvent> intentCaptor = ArgumentCaptor.forClass(InputEvent.class);
+ verify(mInputManager, never()).injectInputEvent(intentCaptor.capture(), anyInt());
+ } else {
+ ArgumentCaptor<InputEvent> intentCaptor = ArgumentCaptor.forClass(InputEvent.class);
+ verify(mInputManager, times(2)).injectInputEvent(intentCaptor.capture(), anyInt());
+ List<InputEvent> inputEvents = intentCaptor.getAllValues();
+ Assert.assertEquals(KeyEvent.KEYCODE_BACK,
+ ((KeyEvent) inputEvents.get(0)).getKeyCode());
+ Assert.assertEquals(KeyEvent.KEYCODE_BACK,
+ ((KeyEvent) inputEvents.get(1)).getKeyCode());
+ // Reset verifier for next call.
+ Mockito.clearInvocations(mContext);
+ }
}
void overrideSearchKeyBehavior(int behavior) {
diff --git a/services/tests/wmtests/src/com/android/server/policy/WindowWakeUpPolicyTests.java b/services/tests/wmtests/src/com/android/server/policy/WindowWakeUpPolicyTests.java
index 9e59bced01f1..4ecf6abf7860 100644
--- a/services/tests/wmtests/src/com/android/server/policy/WindowWakeUpPolicyTests.java
+++ b/services/tests/wmtests/src/com/android/server/policy/WindowWakeUpPolicyTests.java
@@ -145,8 +145,8 @@ public final class WindowWakeUpPolicyTests {
// Verify the policy wake up call succeeds because of the call on the delegate, and not
// because of a PowerManager wake up.
assertThat(mPolicy.wakeUpFromMotion(
- mDefaultDisplay.getDisplayId(), 200, SOURCE_TOUCHSCREEN, true)).isTrue();
- verify(mInputWakeUpDelegate).wakeUpFromMotion(200, SOURCE_TOUCHSCREEN, true);
+ mDefaultDisplay.getDisplayId(), 200, SOURCE_TOUCHSCREEN, true, false)).isTrue();
+ verify(mInputWakeUpDelegate).wakeUpFromMotion(200, SOURCE_TOUCHSCREEN, true, false);
verifyNoPowerManagerWakeUp();
setDelegatedMotionWakeUpResult(false);
@@ -154,8 +154,8 @@ public final class WindowWakeUpPolicyTests {
// Verify the policy wake up call succeeds because of the PowerManager wake up, since the
// delegate would not handle the wake up request.
assertThat(mPolicy.wakeUpFromMotion(
- mDefaultDisplay.getDisplayId(), 300, SOURCE_ROTARY_ENCODER, false)).isTrue();
- verify(mInputWakeUpDelegate).wakeUpFromMotion(300, SOURCE_ROTARY_ENCODER, false);
+ mDefaultDisplay.getDisplayId(), 300, SOURCE_ROTARY_ENCODER, false, false)).isTrue();
+ verify(mInputWakeUpDelegate).wakeUpFromMotion(300, SOURCE_ROTARY_ENCODER, false, false);
verify(mPowerManager).wakeUp(300, WAKE_REASON_WAKE_MOTION, "android.policy:MOTION");
}
@@ -214,8 +214,9 @@ public final class WindowWakeUpPolicyTests {
// Check that the wake up does not happen because the theater mode policy check fails.
assertThat(mPolicy.wakeUpFromMotion(
- mDefaultDisplay.getDisplayId(), 200, SOURCE_TOUCHSCREEN, true)).isFalse();
- verify(mInputWakeUpDelegate, never()).wakeUpFromMotion(anyLong(), anyInt(), anyBoolean());
+ mDefaultDisplay.getDisplayId(), 200, SOURCE_TOUCHSCREEN, true, false)).isFalse();
+ verify(mInputWakeUpDelegate, never())
+ .wakeUpFromMotion(anyLong(), anyInt(), anyBoolean(), anyBoolean());
}
@Test
@@ -227,7 +228,8 @@ public final class WindowWakeUpPolicyTests {
setBooleanRes(config_allowTheaterModeWakeFromMotion, false);
mPolicy = new WindowWakeUpPolicy(mContextSpy, mClock);
- mPolicy.wakeUpFromMotion(mDefaultDisplay.getDisplayId(), 200L, SOURCE_TOUCHSCREEN, true);
+ mPolicy.wakeUpFromMotion(
+ mDefaultDisplay.getDisplayId(), 200L, SOURCE_TOUCHSCREEN, true, false);
verify(mPowerManager).wakeUp(200L, WAKE_REASON_WAKE_MOTION, "android.policy:MOTION");
}
@@ -237,7 +239,7 @@ public final class WindowWakeUpPolicyTests {
public void testWakeUpFromMotion() {
runPowerManagerUpChecks(
() -> mPolicy.wakeUpFromMotion(mDefaultDisplay.getDisplayId(),
- mClock.uptimeMillis(), SOURCE_TOUCHSCREEN, true),
+ mClock.uptimeMillis(), SOURCE_TOUCHSCREEN, true, false),
config_allowTheaterModeWakeFromMotion,
WAKE_REASON_WAKE_MOTION,
"android.policy:MOTION");
@@ -251,7 +253,8 @@ public final class WindowWakeUpPolicyTests {
mPolicy = new WindowWakeUpPolicy(mContextSpy, mClock);
boolean displayWokeUp = mPolicy.wakeUpFromMotion(
- displayId, mClock.uptimeMillis(), SOURCE_TOUCHSCREEN, /* isDown= */ true);
+ displayId, mClock.uptimeMillis(), SOURCE_TOUCHSCREEN, /* isDown= */ true,
+ /* deviceGoingToSleep= */ false);
// Verify that display is woken up
assertThat(displayWokeUp).isTrue();
@@ -267,7 +270,8 @@ public final class WindowWakeUpPolicyTests {
mPolicy = new WindowWakeUpPolicy(mContextSpy, mClock);
boolean displayWokeUp = mPolicy.wakeUpFromMotion(
- displayId, mClock.uptimeMillis(), SOURCE_TOUCHSCREEN, /* isDown= */ true);
+ displayId, mClock.uptimeMillis(), SOURCE_TOUCHSCREEN, /* isDown= */ true,
+ /* deviceGoingToSleep= */ false);
// Verify that power is woken up and display isn't woken up individually
assertThat(displayWokeUp).isTrue();
@@ -442,7 +446,7 @@ public final class WindowWakeUpPolicyTests {
}
private void setDelegatedMotionWakeUpResult(boolean result) {
- when(mInputWakeUpDelegate.wakeUpFromMotion(anyLong(), anyInt(), anyBoolean()))
+ when(mInputWakeUpDelegate.wakeUpFromMotion(anyLong(), anyInt(), anyBoolean(), anyBoolean()))
.thenReturn(result);
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
index 7f242dea9f45..773a566f6315 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -1459,21 +1459,6 @@ public class ActivityRecordTests extends WindowTestsBase {
}
/**
- * Verify that finish bottom activity from a task won't boost it to top.
- */
- @Test
- public void testFinishBottomActivityIfPossible_noZBoost() {
- final ActivityRecord bottomActivity = createActivityWithTask();
- final ActivityRecord topActivity = new ActivityBuilder(mAtm)
- .setTask(bottomActivity.getTask()).build();
- topActivity.setVisibleRequested(true);
- // simulating bottomActivity as a trampoline activity.
- bottomActivity.setState(RESUMED, "test");
- bottomActivity.finishIfPossible("test", false);
- assertFalse(bottomActivity.mNeedsZBoost);
- }
-
- /**
* Verify that complete finish request for visible activity must be delayed before the next one
* becomes visible.
*/
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenAnimationTests.java b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenAnimationTests.java
deleted file mode 100644
index e4628c45a15b..000000000000
--- a/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenAnimationTests.java
+++ /dev/null
@@ -1,142 +0,0 @@
-/*
- * Copyright (C) 2018 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.server.wm;
-
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
-import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_APP_TRANSITION;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.ArgumentMatchers.intThat;
-
-import android.platform.test.annotations.Presubmit;
-import android.view.SurfaceControl;
-
-import androidx.test.filters.SmallTest;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-
-/**
- * Animation related tests for the {@link ActivityRecord} class.
- *
- * Build/Install/Run:
- * atest AppWindowTokenAnimationTests
- */
-@SmallTest
-@Presubmit
-@RunWith(WindowTestRunner.class)
-public class AppWindowTokenAnimationTests extends WindowTestsBase {
-
- private ActivityRecord mActivity;
-
- @Mock
- private AnimationAdapter mSpec;
-
- @Before
- public void setUp() throws Exception {
- MockitoAnnotations.initMocks(this);
-
- mActivity = createActivityRecord(mDisplayContent);
- }
-
- @Test
- public void clipAfterAnim_boundsLayerIsCreated() {
- mActivity.mNeedsAnimationBoundsLayer = true;
-
- mActivity.mSurfaceAnimator.startAnimation(mTransaction, mSpec, true /* hidden */,
- ANIMATION_TYPE_APP_TRANSITION);
- verify(mTransaction).reparent(eq(mActivity.getSurfaceControl()),
- eq(mActivity.mSurfaceAnimator.mLeash));
- verify(mTransaction).reparent(eq(mActivity.mSurfaceAnimator.mLeash),
- eq(mActivity.mAnimationBoundsLayer));
- }
-
- @Test
- public void clipAfterAnim_boundsLayerZBoosted() {
- final Task task = mActivity.getTask();
- final ActivityRecord topActivity = createActivityRecord(task);
- task.assignChildLayers(mTransaction);
-
- assertThat(topActivity.getLastLayer()).isGreaterThan(mActivity.getLastLayer());
-
- mActivity.mNeedsAnimationBoundsLayer = true;
- mActivity.mNeedsZBoost = true;
- mActivity.mSurfaceAnimator.startAnimation(mTransaction, mSpec, true /* hidden */,
- ANIMATION_TYPE_APP_TRANSITION);
-
- verify(mTransaction).setLayer(eq(mActivity.mAnimationBoundsLayer),
- intThat(layer -> layer > topActivity.getLastLayer()));
-
- // The layer should be restored after the animation leash is removed.
- mActivity.onAnimationLeashLost(mTransaction);
- assertThat(mActivity.mNeedsZBoost).isFalse();
- assertThat(topActivity.getLastLayer()).isGreaterThan(mActivity.getLastLayer());
- }
-
- @Test
- public void clipAfterAnim_boundsLayerIsDestroyed() {
- mActivity.mNeedsAnimationBoundsLayer = true;
- mActivity.mSurfaceAnimator.startAnimation(mTransaction, mSpec, true /* hidden */,
- ANIMATION_TYPE_APP_TRANSITION);
- final SurfaceControl leash = mActivity.mSurfaceAnimator.mLeash;
- final SurfaceControl animationBoundsLayer = mActivity.mAnimationBoundsLayer;
- final ArgumentCaptor<SurfaceAnimator.OnAnimationFinishedCallback> callbackCaptor =
- ArgumentCaptor.forClass(
- SurfaceAnimator.OnAnimationFinishedCallback.class);
- verify(mSpec).startAnimation(any(), any(), eq(ANIMATION_TYPE_APP_TRANSITION),
- callbackCaptor.capture());
-
- callbackCaptor.getValue().onAnimationFinished(
- ANIMATION_TYPE_APP_TRANSITION, mSpec);
- verify(mTransaction).remove(eq(leash));
- verify(mTransaction).remove(eq(animationBoundsLayer));
- assertThat(mActivity.mNeedsAnimationBoundsLayer).isFalse();
- }
-
- @Test
- public void clipAfterAnimCancelled_boundsLayerIsDestroyed() {
- mActivity.mNeedsAnimationBoundsLayer = true;
- mActivity.mSurfaceAnimator.startAnimation(mTransaction, mSpec, true /* hidden */,
- ANIMATION_TYPE_APP_TRANSITION);
- final SurfaceControl leash = mActivity.mSurfaceAnimator.mLeash;
- final SurfaceControl animationBoundsLayer = mActivity.mAnimationBoundsLayer;
-
- mActivity.mSurfaceAnimator.cancelAnimation();
- verify(mTransaction).remove(eq(leash));
- verify(mTransaction).remove(eq(animationBoundsLayer));
- assertThat(mActivity.mNeedsAnimationBoundsLayer).isFalse();
- }
-
- @Test
- public void clipNoneAnim_boundsLayerIsNotCreated() {
- mActivity.mNeedsAnimationBoundsLayer = false;
-
- mActivity.mSurfaceAnimator.startAnimation(mTransaction, mSpec, true /* hidden */,
- ANIMATION_TYPE_APP_TRANSITION);
- verify(mTransaction).reparent(eq(mActivity.getSurfaceControl()),
- eq(mActivity.mSurfaceAnimator.mLeash));
- assertThat(mActivity.mAnimationBoundsLayer).isNull();
- }
-}
diff --git a/services/tests/wmtests/src/com/android/server/wm/BackgroundLaunchProcessControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/BackgroundLaunchProcessControllerTests.java
index 27e147d98b1f..747b09cb5660 100644
--- a/services/tests/wmtests/src/com/android/server/wm/BackgroundLaunchProcessControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/BackgroundLaunchProcessControllerTests.java
@@ -74,14 +74,14 @@ public class BackgroundLaunchProcessControllerTests {
BackgroundActivityStartCallback mCallback = new BackgroundActivityStartCallback() {
@Override
- public boolean isActivityStartAllowed(Collection<IBinder> tokens, int uid,
- String packageName) {
+ public BackgroundActivityStartCallbackResult isActivityStartAllowed(
+ Collection<IBinder> tokens, int uid, String packageName) {
for (IBinder token : tokens) {
if (token == null || mActivityStartAllowed.contains(token)) {
- return true;
+ return new BackgroundActivityStartCallbackResult(true, token);
}
}
- return false;
+ return RESULT_FALSE;
}
@Override
diff --git a/services/tests/wmtests/src/com/android/server/wm/DesktopAppCompatAspectRatioPolicyTests.java b/services/tests/wmtests/src/com/android/server/wm/DesktopAppCompatAspectRatioPolicyTests.java
index fa7dcc8ebbc7..81d753b8e079 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DesktopAppCompatAspectRatioPolicyTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DesktopAppCompatAspectRatioPolicyTests.java
@@ -35,6 +35,7 @@ import static com.android.server.wm.AppCompatConfiguration.DEFAULT_LETTERBOX_ASP
import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
import android.compat.testing.PlatformCompatChangeRule;
import android.content.pm.ActivityInfo;
@@ -413,7 +414,7 @@ public class DesktopAppCompatAspectRatioPolicyTests extends WindowTestsBase {
void setDesiredAspectRatio(float aspectRatio) {
doReturn(aspectRatio).when(getDesktopAppCompatAspectRatioPolicy())
- .getDesiredAspectRatio(any());
+ .getDesiredAspectRatio(any(), anyBoolean());
}
DesktopAppCompatAspectRatioPolicy getDesktopAppCompatAspectRatioPolicy() {
@@ -422,7 +423,7 @@ public class DesktopAppCompatAspectRatioPolicyTests extends WindowTestsBase {
float calculateAspectRatio() {
return getDesktopAppCompatAspectRatioPolicy().calculateAspectRatio(
- getTopActivity().getTask());
+ getTopActivity().getTask(), /* hasOrientationMismatch */ true);
}
ActivityRecord getTopActivity() {
diff --git a/services/tests/wmtests/src/com/android/server/wm/DesktopModeLaunchParamsModifierTests.java b/services/tests/wmtests/src/com/android/server/wm/DesktopModeLaunchParamsModifierTests.java
index 00b617e91bfd..f587d6e8c346 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DesktopModeLaunchParamsModifierTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DesktopModeLaunchParamsModifierTests.java
@@ -52,6 +52,7 @@ import static com.android.server.wm.LaunchParamsController.LaunchParamsModifier.
import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
@@ -437,7 +438,7 @@ public class DesktopModeLaunchParamsModifierTests extends
spyOn(activity.mAppCompatController.getDesktopAspectRatioPolicy());
doReturn(LETTERBOX_ASPECT_RATIO).when(activity.mAppCompatController
- .getDesktopAspectRatioPolicy()).calculateAspectRatio(any());
+ .getDesktopAspectRatioPolicy()).calculateAspectRatio(any(), anyBoolean());
final int desiredWidth =
(int) ((LANDSCAPE_DISPLAY_BOUNDS.height() / LETTERBOX_ASPECT_RATIO) + 0.5f);
@@ -933,7 +934,7 @@ public class DesktopModeLaunchParamsModifierTests extends
spyOn(activity.mAppCompatController.getDesktopAspectRatioPolicy());
doReturn(LETTERBOX_ASPECT_RATIO).when(activity.mAppCompatController
- .getDesktopAspectRatioPolicy()).calculateAspectRatio(any());
+ .getDesktopAspectRatioPolicy()).calculateAspectRatio(any(), anyBoolean());
final int desiredHeight =
(int) (LANDSCAPE_DISPLAY_BOUNDS.height() * DESKTOP_MODE_INITIAL_BOUNDS_SCALE);
@@ -1060,7 +1061,7 @@ public class DesktopModeLaunchParamsModifierTests extends
spyOn(activity.mAppCompatController.getDesktopAspectRatioPolicy());
doReturn(LETTERBOX_ASPECT_RATIO).when(activity.mAppCompatController
- .getDesktopAspectRatioPolicy()).calculateAspectRatio(any());
+ .getDesktopAspectRatioPolicy()).calculateAspectRatio(any(), anyBoolean());
final int desiredWidth = PORTRAIT_DISPLAY_BOUNDS.width()
- (DESKTOP_MODE_LANDSCAPE_APP_PADDING * 2);
@@ -1115,7 +1116,7 @@ public class DesktopModeLaunchParamsModifierTests extends
spyOn(activity.mAppCompatController.getDesktopAspectRatioPolicy());
doReturn(LETTERBOX_ASPECT_RATIO).when(activity.mAppCompatController
- .getDesktopAspectRatioPolicy()).calculateAspectRatio(any());
+ .getDesktopAspectRatioPolicy()).calculateAspectRatio(any(), anyBoolean());
final int desiredWidth = PORTRAIT_DISPLAY_BOUNDS.width()
- (DESKTOP_MODE_LANDSCAPE_APP_PADDING * 2);
@@ -1569,7 +1570,7 @@ public class DesktopModeLaunchParamsModifierTests extends
activity.mAppCompatController.getDesktopAspectRatioPolicy();
spyOn(desktopAppCompatAspectRatioPolicy);
doReturn(aspectRatio).when(desktopAppCompatAspectRatioPolicy)
- .getDesiredAspectRatio(any());
+ .getDesiredAspectRatio(any(), anyBoolean());
}
private void applyUserMinAspectRatioOverride(ActivityRecord activity, int overrideCode,
@@ -1579,7 +1580,7 @@ public class DesktopModeLaunchParamsModifierTests extends
activity.mAppCompatController.getDesktopAspectRatioPolicy();
spyOn(desktopAppCompatAspectRatioPolicy);
doReturn(1f).when(desktopAppCompatAspectRatioPolicy)
- .getDesiredAspectRatio(any());
+ .getDesiredAspectRatio(any(), anyBoolean());
// Enable user aspect ratio settings
final AppCompatConfiguration appCompatConfiguration =
diff --git a/services/tests/wmtests/src/com/android/server/wm/DeviceStateAutoRotateSettingIssueLoggerTests.java b/services/tests/wmtests/src/com/android/server/wm/DeviceStateAutoRotateSettingIssueLoggerTests.java
index f76a9cdbb894..ba9bf1bf8045 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DeviceStateAutoRotateSettingIssueLoggerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DeviceStateAutoRotateSettingIssueLoggerTests.java
@@ -24,6 +24,7 @@ import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
import android.platform.test.annotations.Presubmit;
@@ -143,4 +144,46 @@ public class DeviceStateAutoRotateSettingIssueLoggerTests {
anyInt(),
anyBoolean()), never());
}
+
+ @Test
+ public void onStateChange_issueOccurredSettingChangedTwice_reportOnlyOnce() {
+ mDeviceStateAutoRotateSettingIssueLogger.onDeviceStateAutoRotateSettingChange();
+ mDeviceStateAutoRotateSettingIssueLogger.onDeviceStateChange();
+ mDeviceStateAutoRotateSettingIssueLogger.onDeviceStateAutoRotateSettingChange();
+
+ verify(() ->
+ FrameworkStatsLog.write(
+ eq(FrameworkStatsLog.DEVICE_STATE_AUTO_ROTATE_SETTING_ISSUE_REPORTED),
+ anyInt(),
+ anyBoolean()), times(1));
+ }
+
+ @Test
+ public void onStateChange_issueOccurredDeviceStateChangedTwice_reportOnlyOnce() {
+ mDeviceStateAutoRotateSettingIssueLogger.onDeviceStateChange();
+ mDeviceStateAutoRotateSettingIssueLogger.onDeviceStateAutoRotateSettingChange();
+ mDeviceStateAutoRotateSettingIssueLogger.onDeviceStateChange();
+
+ verify(() ->
+ FrameworkStatsLog.write(
+ eq(FrameworkStatsLog.DEVICE_STATE_AUTO_ROTATE_SETTING_ISSUE_REPORTED),
+ anyInt(),
+ anyBoolean()), times(1));
+ }
+
+ @Test
+ public void onStateChange_issueOccurredAfterDelay_reportOnce() {
+ mDeviceStateAutoRotateSettingIssueLogger.onDeviceStateAutoRotateSettingChange();
+ mTestTimeSupplier.delay(
+ DEVICE_STATE_AUTO_ROTATE_SETTING_ISSUE_THRESHOLD_MILLIS + DELAY);
+ mDeviceStateAutoRotateSettingIssueLogger.onDeviceStateChange();
+ mTestTimeSupplier.delay(DELAY);
+ mDeviceStateAutoRotateSettingIssueLogger.onDeviceStateAutoRotateSettingChange();
+
+ verify(() ->
+ FrameworkStatsLog.write(
+ eq(FrameworkStatsLog.DEVICE_STATE_AUTO_ROTATE_SETTING_ISSUE_REPORTED),
+ eq(DELAY),
+ anyBoolean()), times(1));
+ }
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsTests.java
index 521d8364a31f..449ca867b987 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsTests.java
@@ -52,6 +52,8 @@ import android.view.Surface;
import androidx.test.filters.SmallTest;
import com.android.server.LocalServices;
+import com.android.server.UiThread;
+import com.android.server.notification.NotificationManagerInternal;
import com.android.server.policy.WindowManagerPolicy;
import com.android.server.wm.DisplayWindowSettings.SettingsProvider.SettingsEntry;
import com.android.window.flags.Flags;
@@ -59,6 +61,7 @@ import com.android.window.flags.Flags;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.Mockito;
import java.util.HashMap;
import java.util.Map;
@@ -387,6 +390,30 @@ public class DisplayWindowSettingsTests extends WindowTestsBase {
}
@Test
+ @EnableFlags(com.android.server.display.feature.flags.Flags
+ .FLAG_ENABLE_DISPLAY_CONTENT_MODE_MANAGEMENT)
+ public void testSetShouldShowSystemDecorsNotifyNotificationManager() {
+ final NotificationManagerInternal notificationManager = Mockito.mock(
+ NotificationManagerInternal.class);
+ LocalServices.addService(NotificationManagerInternal.class, notificationManager);
+ try {
+ // First show the decoration because setting false is noop if the decoration has already
+ // been hidden.
+ mDisplayWindowSettings.setShouldShowSystemDecorsLocked(
+ mSecondaryDisplay, /* shouldShow= */ true);
+
+ mDisplayWindowSettings.setShouldShowSystemDecorsLocked(
+ mSecondaryDisplay, /* shouldShow= */ false);
+
+ waitHandlerIdle(UiThread.getHandler());
+ Mockito.verify(notificationManager).onDisplayRemoveSystemDecorations(
+ mSecondaryDisplay.mDisplayId);
+ } finally {
+ LocalServices.removeServiceForTest(NotificationManagerInternal.class);
+ }
+ }
+
+ @Test
public void testPrimaryDisplayImePolicy() {
assertEquals(DISPLAY_IME_POLICY_LOCAL,
mDisplayWindowSettings.getImePolicyLocked(mPrimaryDisplay));
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java
index ec83c50e95aa..1aa8681c9bfd 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java
@@ -49,10 +49,8 @@ import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
-import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.clearInvocations;
-import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import android.app.ActivityOptions;
@@ -192,24 +190,6 @@ public class TaskDisplayAreaTests extends WindowTestsBase {
}
@Test
- public void testActivityWithZBoost_taskDisplayAreaDoesNotMoveUp() {
- final Task rootTask = createTask(mDisplayContent);
- final Task task = createTaskInRootTask(rootTask, 0 /* userId */);
- final ActivityRecord activity = createNonAttachedActivityRecord(mDisplayContent);
- task.addChild(activity, 0 /* addPos */);
- final TaskDisplayArea taskDisplayArea = activity.getDisplayArea();
- activity.mNeedsAnimationBoundsLayer = true;
- activity.mNeedsZBoost = true;
- spyOn(taskDisplayArea.mSurfaceAnimator);
-
- mDisplayContent.assignChildLayers(mTransaction);
-
- assertThat(activity.needsZBoost()).isTrue();
- assertThat(taskDisplayArea.needsZBoost()).isFalse();
- verify(taskDisplayArea.mSurfaceAnimator, never()).setLayer(eq(mTransaction), anyInt());
- }
-
- @Test
public void testRootTaskPositionChildAt() {
Task pinnedTask = createTask(
mDisplayContent, WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD);
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
index 044aacc4b988..b617f0285606 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
@@ -100,8 +100,6 @@ import androidx.test.filters.MediumTest;
import com.android.modules.utils.TypedXmlPullParser;
import com.android.modules.utils.TypedXmlSerializer;
-import libcore.junit.util.compat.CoreCompatChangeRule;
-
import org.junit.Assert;
import org.junit.Before;
import org.junit.Rule;
@@ -414,79 +412,96 @@ public class TaskTests extends WindowTestsBase {
}
@Test
- @CoreCompatChangeRule.EnableCompatChanges({ActivityInfo.FORCE_RESIZE_APP})
- public void testIsResizeable_nonResizeable_forceResize_overridesEnabled_Resizeable() {
+ public void testIsResizeable_nonResizeable_forceResize_overridesEnabled_resizeable() {
final Task task = new TaskBuilder(mSupervisor)
.setCreateActivity(true)
- .setComponent(
- ComponentName.createRelative(mContext, SizeCompatTests.class.getName()))
.build();
task.setResizeMode(RESIZE_MODE_UNRESIZEABLE);
+ final ActivityRecord activity = task.getRootActivity();
+ final AppCompatResizeOverrides resizeOverrides =
+ activity.mAppCompatController.getResizeOverrides();
+ spyOn(activity);
+ spyOn(resizeOverrides);
+ doReturn(true).when(resizeOverrides).shouldOverrideForceResizeApp();
+ task.intent = null;
+ task.setIntent(activity);
// Override should take effect and task should be resizeable.
assertTrue(task.getTaskInfo().isResizeable);
}
@Test
- @CoreCompatChangeRule.EnableCompatChanges({ActivityInfo.FORCE_RESIZE_APP})
- public void testIsResizeable_nonResizeable_forceResize_overridesDisabled_nonResizeable() {
+ public void testIsResizeable_resizeable_forceNonResize_overridesEnabled_nonResizeable() {
final Task task = new TaskBuilder(mSupervisor)
.setCreateActivity(true)
- .setComponent(
- ComponentName.createRelative(mContext, SizeCompatTests.class.getName()))
.build();
- task.setResizeMode(RESIZE_MODE_UNRESIZEABLE);
-
- // Disallow resize overrides.
- task.mAllowForceResizeOverride = false;
+ task.setResizeMode(RESIZE_MODE_RESIZEABLE);
+ final ActivityRecord activity = task.getRootActivity();
+ final AppCompatResizeOverrides resizeOverrides =
+ activity.mAppCompatController.getResizeOverrides();
+ spyOn(activity);
+ spyOn(resizeOverrides);
+ doReturn(true).when(resizeOverrides).shouldOverrideForceNonResizeApp();
+ task.intent = null;
+ task.setIntent(activity);
- // Override should not take effect and task should be un-resizeable.
+ // Override should take effect and task should be un-resizeable.
assertFalse(task.getTaskInfo().isResizeable);
}
@Test
- @CoreCompatChangeRule.EnableCompatChanges({ActivityInfo.FORCE_NON_RESIZE_APP})
- public void testIsResizeable_resizeable_forceNonResize_overridesEnabled_nonResizeable() {
+ public void testIsResizeable_resizeableTask_fullscreenOverride_resizeable() {
final Task task = new TaskBuilder(mSupervisor)
.setCreateActivity(true)
- .setComponent(
- ComponentName.createRelative(mContext, SizeCompatTests.class.getName()))
.build();
- task.setResizeMode(RESIZE_MODE_RESIZEABLE);
+ task.setResizeMode(RESIZE_MODE_UNRESIZEABLE);
+ final ActivityRecord activity = task.getRootActivity();
+ final AppCompatAspectRatioOverrides aspectRatioOverrides =
+ activity.mAppCompatController.getAspectRatioOverrides();
+ spyOn(aspectRatioOverrides);
+ doReturn(true).when(aspectRatioOverrides).hasFullscreenOverride();
+ task.intent = null;
+ task.setIntent(activity);
- // Override should take effect and task should be un-resizeable.
- assertFalse(task.getTaskInfo().isResizeable);
+ // Override should take effect and task should be resizeable.
+ assertTrue(task.getTaskInfo().isResizeable);
}
@Test
- @CoreCompatChangeRule.EnableCompatChanges({ActivityInfo.FORCE_NON_RESIZE_APP})
- public void testIsResizeable_resizeable_forceNonResize_overridesDisabled_Resizeable() {
+ public void testIsResizeable_resizeableTask_universalResizeable_resizeable() {
final Task task = new TaskBuilder(mSupervisor)
.setCreateActivity(true)
- .setComponent(
- ComponentName.createRelative(mContext, SizeCompatTests.class.getName()))
.build();
- task.setResizeMode(RESIZE_MODE_RESIZEABLE);
-
- // Disallow resize overrides.
- task.mAllowForceResizeOverride = false;
+ task.setResizeMode(RESIZE_MODE_UNRESIZEABLE);
+ final ActivityRecord activity = task.getRootActivity();
+ spyOn(activity);
+ doReturn(true).when(activity).isUniversalResizeable();
+ task.intent = null;
+ task.setIntent(activity);
- // Override should not take effect and task should be resizeable.
+ // Override should take effect and task should be resizeable.
assertTrue(task.getTaskInfo().isResizeable);
}
@Test
- @CoreCompatChangeRule.EnableCompatChanges({ActivityInfo.FORCE_NON_RESIZE_APP})
- public void testIsResizeable_systemWideForceResize_compatForceNonResize__Resizeable() {
+ public void testIsResizeable_systemWideForceResize_compatForceNonResize_resizeable() {
final Task task = new TaskBuilder(mSupervisor)
.setCreateActivity(true)
- .setComponent(
- ComponentName.createRelative(mContext, SizeCompatTests.class.getName()))
+ .setComponent(ComponentName.createRelative(mContext, TaskTests.class.getName()))
.build();
task.setResizeMode(RESIZE_MODE_RESIZEABLE);
// Set system-wide force resizeable override.
task.mAtmService.mForceResizableActivities = true;
+ final ActivityRecord activity = task.getRootActivity();
+ final AppCompatResizeOverrides resizeOverrides =
+ activity.mAppCompatController.getResizeOverrides();
+ spyOn(activity);
+ spyOn(resizeOverrides);
+ doReturn(true).when(resizeOverrides).shouldOverrideForceNonResizeApp();
+ task.intent = null;
+ task.setIntent(activity);
+
// System wide override should tak priority over app compat override so the task should
// remain resizeable.
assertTrue(task.getTaskInfo().isResizeable);
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
index 73102c4478d8..5427dc22e700 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
@@ -1486,6 +1486,46 @@ public class WindowManagerServiceTests extends WindowTestsBase {
}
@Test
+ @EnableFlags(Flags.FLAG_FIX_HIDE_OVERLAY_API)
+ public void testUpdateOverlayWindows_multipleWindowsFromSameUid_idempotent() {
+ // Deny INTERNAL_SYSTEM_WINDOW permission for WindowSession so that the saw isn't allowed to
+ // show despite hideNonSystemOverlayWindows.
+ doReturn(PackageManager.PERMISSION_DENIED).when(mWm.mContext).checkPermission(
+ eq(android.Manifest.permission.INTERNAL_SYSTEM_WINDOW), anyInt(), anyInt());
+
+ WindowState saw =
+ newWindowBuilder("saw", TYPE_APPLICATION_OVERLAY).setOwnerId(10123).build();
+ saw.mWinAnimator.mDrawState = WindowStateAnimator.HAS_DRAWN;
+ saw.mWinAnimator.mSurfaceControl = mock(SurfaceControl.class);
+ assertThat(saw.mSession.mCanAddInternalSystemWindow).isFalse();
+
+ WindowState app1 = newWindowBuilder("app1", TYPE_APPLICATION).setOwnerId(10456).build();
+ spyOn(app1);
+ doReturn(true).when(app1).hideNonSystemOverlayWindowsWhenVisible();
+
+ WindowState app2 = newWindowBuilder("app2", TYPE_APPLICATION).setOwnerId(10456).build();
+ spyOn(app2);
+ doReturn(true).when(app2).hideNonSystemOverlayWindowsWhenVisible();
+
+ makeWindowVisible(saw, app1, app2);
+ assertThat(saw.isVisibleByPolicy()).isTrue();
+
+ // Two hideNonSystemOverlayWindows windows: SAW is hidden.
+ mWm.updateNonSystemOverlayWindowsVisibilityIfNeeded(app1, true);
+ mWm.updateNonSystemOverlayWindowsVisibilityIfNeeded(app2, true);
+ assertThat(saw.isVisibleByPolicy()).isFalse();
+
+ // Marking the same window hidden twice: SAW is still hidden.
+ mWm.updateNonSystemOverlayWindowsVisibilityIfNeeded(app1, false);
+ mWm.updateNonSystemOverlayWindowsVisibilityIfNeeded(app1, false);
+ assertThat(saw.isVisibleByPolicy()).isFalse();
+
+ // Marking the remaining window hidden: SAW can be shown again.
+ mWm.updateNonSystemOverlayWindowsVisibilityIfNeeded(app2, false);
+ assertThat(saw.isVisibleByPolicy()).isTrue();
+ }
+
+ @Test
@EnableFlags(Flags.FLAG_REPARENT_WINDOW_TOKEN_API)
public void reparentWindowContextToDisplayArea_newDisplay_reparented() {
final WindowToken windowToken = createTestClientWindowToken(TYPE_NOTIFICATION_SHADE,
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java
index 9dc70266bf3d..5347f9a36652 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java
@@ -382,12 +382,17 @@ public class WindowProcessControllerTests extends WindowTestsBase {
assertFalse(tracker.hasResumedActivity(mWpc.mUid));
assertTrue(mWpc.hasForegroundActivities());
- activity.setVisibility(false);
activity.setVisibleRequested(false);
- activity.setState(STOPPED, "test");
-
+ if (com.android.window.flags.Flags.useVisibleRequestedForProcessTracker()) {
+ assertTrue("PAUSING is visible", mWpc.hasVisibleActivities());
+ activity.setState(PAUSED, "test");
+ } else {
+ activity.setVisible(false);
+ }
verify(tracker).onAllActivitiesInvisible(mWpc);
assertFalse(mWpc.hasVisibleActivities());
+
+ activity.setState(STOPPED, "test");
assertFalse(mWpc.hasForegroundActivities());
}
diff --git a/telecomm/java/android/telecom/Call.java b/telecomm/java/android/telecom/Call.java
index c244168c65fd..51ce144881b7 100644
--- a/telecomm/java/android/telecom/Call.java
+++ b/telecomm/java/android/telecom/Call.java
@@ -2912,38 +2912,48 @@ public final class Call {
if (bundle.size() != newBundle.size()) {
return false;
}
-
- for(String key : bundle.keySet()) {
- if (key != null) {
- if (!newBundle.containsKey(key)) {
- return false;
- }
- // In case new call extra contains non-framework class objects, return false to
- // force update the call extra
- try {
- final Object value = bundle.get(key);
- final Object newValue = newBundle.get(key);
- if (value instanceof Bundle && newValue instanceof Bundle) {
- if (!areBundlesEqual((Bundle) value, (Bundle) newValue)) {
- return false;
- }
+ try {
+ for (String key : bundle.keySet()) {
+ if (key != null) {
+ if (!newBundle.containsKey(key)) {
+ return false;
}
- if (value instanceof byte[] && newValue instanceof byte[]) {
- if (!Arrays.equals((byte[]) value, (byte[]) newValue)) {
+ // In case new call extra contains non-framework class objects, return false to
+ // force update the call extra
+ try {
+ final Object value = bundle.get(key);
+ final Object newValue = newBundle.get(key);
+ if (value instanceof Bundle && newValue instanceof Bundle) {
+ if (!areBundlesEqual((Bundle) value, (Bundle) newValue)) {
+ return false;
+ }
+ }
+ if (value instanceof byte[] && newValue instanceof byte[]) {
+ if (!Arrays.equals((byte[]) value, (byte[]) newValue)) {
+ return false;
+ }
+ } else if (!Objects.equals(value, newValue)) {
return false;
}
- } else if (!Objects.equals(value, newValue)) {
+ } catch (BadParcelableException e) {
return false;
}
- } catch (BadParcelableException e) {
- return false;
- } catch (ClassCastException e) {
- Log.e(LOG_TAG, e, "areBundlesEqual: failure comparing bundle key %s", key);
- // until we know what is causing this, we should rethrow -- this is still not
- // expected.
- throw e;
}
}
+ } catch (ClassCastException | ArrayIndexOutOfBoundsException e) {
+ // Unfortunately this may get raised when accessing the bundle's keyset, so we cannot
+ // determine WHY a class cast exception is happening. We had tried in the past to do
+ // this down in the for loop so we could figure out which key is causing an issue.
+ // Bundles are not thread safe, so the most likely issue here is that the InCallService
+ // implementation is accessing the Bundle WHILE an incoming Telecom update comes in to
+ // potentially replace the Bundle. We call "areBundlesEqual" to see if the newly
+ // unparceled Call.Details is the same as what is already in the current Call instance.
+ // If those two operations overleave, I can see the potential for concurrent
+ // modification and edit of the Bundle. So we'll just catch here and assume the Bundles
+ // are not the same. This means a Call.CallBack may fire the onCallDetails changed
+ // callback when the Bundle didn't actually change.
+ Log.e(LOG_TAG, e, "areBundlesEqual: failed!");
+ return false;
}
return true;
}
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 14915109999c..19574fd95ac7 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -11493,17 +11493,17 @@ public class CarrierConfigManager {
+ "target=GERAN|UTRAN|EUTRAN|NGRAN|IWLAN, type=allowed"});
PersistableBundle auto_data_switch_rat_signal_score_string_bundle = new PersistableBundle();
auto_data_switch_rat_signal_score_string_bundle.putIntArray(
- "NR_SA_MMWAVE", new int[]{10000, 13227, 16000, 18488, 20017});
+ "NR_SA_MMWAVE", new int[]{6300, 10227, 16000, 18488, 19017});
auto_data_switch_rat_signal_score_string_bundle.putIntArray(
- "NR_NSA_MMWAVE", new int[]{8000, 10227, 12488, 15017, 15278});
+ "NR_NSA_MMWAVE", new int[]{5700, 9227, 12488, 13517, 15978});
auto_data_switch_rat_signal_score_string_bundle.putIntArray(
"LTE", new int[]{3731, 5965, 8618, 11179, 13384});
auto_data_switch_rat_signal_score_string_bundle.putIntArray(
- "LTE_CA", new int[]{3831, 6065, 8718, 11379, 13484});
+ "LTE_CA", new int[]{3831, 6065, 8718, 11379, 14484});
auto_data_switch_rat_signal_score_string_bundle.putIntArray(
- "NR_SA", new int[]{5288, 6795, 6955, 7562, 9713});
+ "NR_SA", new int[]{2288, 6795, 6955, 7562, 15484});
auto_data_switch_rat_signal_score_string_bundle.putIntArray(
- "NR_NSA", new int[]{5463, 6827, 8029, 9007, 9428});
+ "NR_NSA", new int[]{2463, 6827, 8029, 9007, 15884});
auto_data_switch_rat_signal_score_string_bundle.putIntArray(
"UMTS", new int[]{100, 169, 183, 192, 300});
auto_data_switch_rat_signal_score_string_bundle.putIntArray(
diff --git a/telephony/java/android/telephony/satellite/SatelliteManager.java b/telephony/java/android/telephony/satellite/SatelliteManager.java
index 270d599e0a0a..6b4b0ee93684 100644
--- a/telephony/java/android/telephony/satellite/SatelliteManager.java
+++ b/telephony/java/android/telephony/satellite/SatelliteManager.java
@@ -700,6 +700,56 @@ public final class SatelliteManager {
public @interface DisplayMode {}
/**
+ * Unknown or unsupported value for data mode on satellite.
+ * @hide
+ */
+ @SystemApi
+ @FlaggedApi(Flags.FLAG_SATELLITE_25Q4_APIS)
+ public static final int SATELLITE_DATA_SUPPORT_UNKNOWN = -1;
+
+ /**
+ * Support only restricted data usecases like carrier messaging using RCS.
+ * @hide
+ */
+ @SystemApi
+ @FlaggedApi(Flags.FLAG_SATELLITE_25Q4_APIS)
+ public static final int SATELLITE_DATA_SUPPORT_RESTRICTED = 0;
+
+ /**
+ * Support constrained internet which would enable internet only for applications that are
+ * modified.
+ *
+ * <p>
+ * To get internet access, applications need to be modified to use the satellite data
+ * optimized network. This can be done by setting the {@link #PROPERTY_SATELLITE_DATA_OPTIMIZED}
+ * property to {@code true} in the manifest.
+ * </p>
+ *
+ * @hide
+ */
+ @SystemApi
+ @FlaggedApi(Flags.FLAG_SATELLITE_25Q4_APIS)
+ public static final int SATELLITE_DATA_SUPPORT_CONSTRAINED = 1;
+
+ /**
+ * Support default internet on satellite without any restrictions on any apps.
+ * @hide
+ */
+ @SystemApi
+ @FlaggedApi(Flags.FLAG_SATELLITE_25Q4_APIS)
+ public static final int SATELLITE_DATA_SUPPORT_UNCONSTRAINED = 2;
+
+ /** @hide */
+ @IntDef(prefix = {"SATELLITE_DATA_SUPPORT_"}, value = {
+ SATELLITE_DATA_SUPPORT_UNKNOWN,
+ SATELLITE_DATA_SUPPORT_RESTRICTED,
+ SATELLITE_DATA_SUPPORT_CONSTRAINED,
+ SATELLITE_DATA_SUPPORT_UNCONSTRAINED,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface SatelliteDataSupportMode {}
+
+ /**
* The emergency call is handed over to oem-enabled satellite SOS messaging. SOS messages are
* sent to SOS providers, which will then forward the messages to emergency providers.
* @hide
@@ -3788,6 +3838,39 @@ public final class SatelliteManager {
return appsNames;
}
+ /**
+ * Method to return the current satellite data service policy supported mode for the
+ * subscriptionId based on carrier config.
+ *
+ * @param subId current subscription id.
+ *
+ * @return Supported modes {@link SatelliteDataSupportMode}
+ * @throws IllegalArgumentException if the subscription is invalid.
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
+ @FlaggedApi(Flags.FLAG_SATELLITE_25Q4_APIS)
+ @SatelliteDataSupportMode
+ public int getSatelliteDataSupportMode(int subId) {
+ int satelliteMode = SATELLITE_DATA_SUPPORT_UNKNOWN;
+
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null) {
+ satelliteMode = telephony.getSatelliteDataSupportMode(subId);
+ } else {
+ throw new IllegalStateException("telephony service is null.");
+ }
+ } catch (RemoteException ex) {
+ loge("getSatelliteDataSupportMode() RemoteException:" + ex);
+ ex.rethrowAsRuntimeException();
+ }
+
+ return satelliteMode;
+ }
+
@Nullable
private static ITelephony getITelephony() {
ITelephony binder = ITelephony.Stub.asInterface(TelephonyFrameworkInitializer
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 0dd0a42d44b4..d7f80a94081a 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -3238,6 +3238,15 @@ interface ITelephony {
boolean setOemEnabledSatelliteProvisionStatus(in boolean reset, in boolean isProvisioned);
/**
+ * This API is used by CTS to override the version of the config data
+ *
+ * @param reset Whether to restore the original version
+ * @param version The overriding version
+ * @return {@code true} if successful, {@code false} otherwise
+ */
+ boolean overrideConfigDataVersion(in boolean reset, in int version);
+
+ /**
* Test method to confirm the file contents are not altered.
*/
@JavaPassthrough(annotation="@android.annotation.RequiresPermission("
@@ -3632,4 +3641,19 @@ interface ITelephony {
@JavaPassthrough(annotation="@android.annotation.RequiresPermission("
+ "android.Manifest.permission.SATELLITE_COMMUNICATION)")
List<String> getSatelliteDataOptimizedApps();
+
+ /**
+ * Method to return the current satellite data service policy supported mode for the
+ * subscriptionId based on subscription id. Note: Iif any error or invalid sub id
+ * {@Link SatelliteDataSupportMode#SATELLITE_DATA_SUPPORT_UNKNOWN} will be returned.
+ *
+ * @param subId current subscription id.
+ *
+ * @return Supported modes {@link SatelliteDataSupportMode}
+ * @throws IllegalArgumentException if the subscription is invalid.
+ * @hide
+ */
+ @JavaPassthrough(annotation="@android.annotation.RequiresPermission("
+ + "android.Manifest.permission.SATELLITE_COMMUNICATION)")
+ int getSatelliteDataSupportMode(in int subId);
}
diff --git a/tests/FsVerityTest/FsVerityTestApp/src/com/android/fsverity/Helper.java b/tests/FsVerityTest/FsVerityTestApp/src/com/android/fsverity/Helper.java
index c52be7c2b0c6..8e012598c9eb 100644
--- a/tests/FsVerityTest/FsVerityTestApp/src/com/android/fsverity/Helper.java
+++ b/tests/FsVerityTest/FsVerityTestApp/src/com/android/fsverity/Helper.java
@@ -21,7 +21,10 @@ import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertThrows;
import android.content.Context;
+import android.os.Bundle;
import android.security.FileIntegrityManager;
+import android.system.Os;
+import android.system.OsConstants;
import android.util.Log;
import androidx.test.core.app.ApplicationProvider;
@@ -35,8 +38,9 @@ import org.junit.Test;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.RandomAccessFile;
-import java.util.ArrayList;
import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
/**
* Test helper that works with the host-side test to set up a test file, and to verify fs-verity
@@ -47,8 +51,6 @@ public class Helper {
private static final String FILENAME = "test.file";
- private static final long BLOCK_SIZE = 4096;
-
@Rule
public final AdoptShellPermissionsRule mAdoptShellPermissionsRule =
new AdoptShellPermissionsRule(
@@ -58,7 +60,7 @@ public class Helper {
@Test
public void prepareTest() throws Exception {
Context context = ApplicationProvider.getApplicationContext();
- android.os.Bundle testArgs = InstrumentationRegistry.getArguments();
+ Bundle testArgs = InstrumentationRegistry.getArguments();
String basename = testArgs.getString("basename");
context.deleteFile(basename);
@@ -84,31 +86,52 @@ public class Helper {
fim.setupFsVerity(context.getFileStreamPath(basename));
}
+ private static long getPageSize() {
+ String arch = System.getProperty("os.arch");
+ Log.d(TAG, "os.arch=" + arch);
+ if ("x86_64".equals(arch)) {
+ // Override the fake 16K page size from cf_x86_64_pgagnostic. The real page size on
+ // x86_64 is always 4K. This test needs the real page size because it is testing I/O
+ // error reporting behavior that is dependent on the real page size.
+ return 4096;
+ }
+ return Os.sysconf(OsConstants._SC_PAGE_SIZE);
+ }
+
@Test
public void verifyFileRead() throws Exception {
Context context = ApplicationProvider.getApplicationContext();
- // Collect indices that the backing blocks are supposed to be corrupted.
- android.os.Bundle testArgs = InstrumentationRegistry.getArguments();
+ Bundle testArgs = InstrumentationRegistry.getArguments();
assertThat(testArgs).isNotNull();
String filePath = testArgs.getString("filePath");
- String csv = testArgs.getString("brokenBlockIndicesCsv");
- Log.d(TAG, "brokenBlockIndicesCsv: " + csv);
- String[] strings = csv.split(",");
- var corrupted = new ArrayList(strings.length);
- for (int i = 0; i < strings.length; i++) {
- corrupted.add(Integer.parseInt(strings[i]));
+ String csv = testArgs.getString("brokenByteIndicesCsv");
+ Log.d(TAG, "brokenByteIndicesCsv: " + csv);
+
+ // Build the set of pages that contain a corrupted byte.
+ final long pageSize = getPageSize();
+ Set<Long> corruptedPageIndices = new HashSet();
+ for (String s : csv.split(",")) {
+ long byteIndex = Long.parseLong(s);
+ long pageIndex = byteIndex / pageSize;
+ corruptedPageIndices.add(pageIndex);
}
-
- // Expect the read to succeed or fail per the prior.
- try (var file = new RandomAccessFile(filePath, "r")) {
- long total_blocks = (file.length() + BLOCK_SIZE - 1) / BLOCK_SIZE;
- for (int i = 0; i < (int) total_blocks; i++) {
- file.seek(i * BLOCK_SIZE);
- if (corrupted.contains(i)) {
- Log.d(TAG, "Expecting read at block #" + i + " to fail");
+ Log.d(TAG, "corruptedPageIndices=" + corruptedPageIndices);
+
+ // Read bytes from the file and verify the expected result based on the containing page.
+ // (The kernel reports fs-verity errors at page granularity.)
+ final long stride = 1024;
+ // Using a stride that is a divisor of the page size ensures that the last page is tested.
+ assertThat(pageSize % stride).isEqualTo(0);
+ try (RandomAccessFile file = new RandomAccessFile(filePath, "r")) {
+ for (long byteIndex = 0; byteIndex < file.length(); byteIndex += stride) {
+ file.seek(byteIndex);
+ long pageIndex = byteIndex / pageSize;
+ if (corruptedPageIndices.contains(pageIndex)) {
+ Log.d(TAG, "Expecting read at byte #" + byteIndex + " to fail");
assertThrows(IOException.class, () -> file.read());
} else {
+ Log.d(TAG, "Expecting read at byte #" + byteIndex + " to succeed");
assertThat(file.readByte()).isEqualTo('1');
}
}
diff --git a/tests/FsVerityTest/src/com/android/fsverity/FsVerityHostTest.java b/tests/FsVerityTest/src/com/android/fsverity/FsVerityHostTest.java
index b1d6e96dca9b..88d9e9e08dce 100644
--- a/tests/FsVerityTest/src/com/android/fsverity/FsVerityHostTest.java
+++ b/tests/FsVerityTest/src/com/android/fsverity/FsVerityHostTest.java
@@ -33,13 +33,12 @@ import org.junit.runner.RunWith;
/**
* This test verifies fs-verity works end-to-end. There is a corresponding helper app.
*
- * <p>The helper app uses a FileIntegrityManager API to enable fs-verity to a file. The host test
- * here * tampers with the file's backing storage, then tells the helper app to read and expect
+ * <p>The helper app uses a FileIntegrityManager API to enable fs-verity on a file. The host test
+ * here tampers with the file's backing storage, then tells the helper app to read and expect
* success/failure on read.
*
- * <p>In order to make sure a block of the file is readable only if the underlying block on disk
- * stay intact, the test needs to bypass the filesystem and tampers with the corresponding physical
- * address against the block device.
+ * <p>Since the filesystem by design provides no way to corrupt fs-verity files itself, the test
+ * needs to bypass the filesystem and write directly to the block device to corrupt the files.
*/
@RootPermissionTest
@RunWith(DeviceJUnit4ClassRunner.class)
@@ -57,7 +56,7 @@ public class FsVerityHostTest extends BaseHostJUnit4Test {
BlockDeviceWriter.damageFileAgainstBlockDevice(device, getTargetFilePath(), 8192);
BlockDeviceWriter.dropCaches(device);
- verifyRead(getTargetFilePath(), "0,2");
+ verifyRead(getTargetFilePath(), "0,8192");
}
@Test
@@ -70,7 +69,7 @@ public class FsVerityHostTest extends BaseHostJUnit4Test {
BlockDeviceWriter.damageFileAgainstBlockDevice(device, getTargetFilePath(), 128 * 4096 + 1);
BlockDeviceWriter.dropCaches(device);
- verifyRead(getTargetFilePath(), "1,100,128");
+ verifyRead(getTargetFilePath(), "4096,409600,524289");
}
private String getTargetFilePath() throws DeviceNotAvailableException {
@@ -87,11 +86,17 @@ public class FsVerityHostTest extends BaseHostJUnit4Test {
assertThat(runDeviceTests(options)).isTrue();
}
+ /**
+ * Verifies the read success/failure expectation given the corrupted byte indices in the file.
+ *
+ * @param path the remote file path to read.
+ * @param indicesCsv a comma-separated list of indices of bytes that were corrupted.
+ */
private void verifyRead(String path, String indicesCsv) throws Exception {
DeviceTestRunOptions options = new DeviceTestRunOptions(TARGET_PACKAGE);
options.setTestClassName(TARGET_PACKAGE + ".Helper");
options.setTestMethodName("verifyFileRead");
- options.addInstrumentationArg("brokenBlockIndicesCsv", indicesCsv);
+ options.addInstrumentationArg("brokenByteIndicesCsv", indicesCsv);
options.addInstrumentationArg("filePath", getTargetFilePath());
assertThat(runDeviceTests(options)).isTrue();
}
diff --git a/tests/Input/src/android/hardware/input/KeyGestureEventHandlerTest.kt b/tests/Input/src/android/hardware/input/KeyGestureEventHandlerTest.kt
index 794fd0255726..c62bd0b72584 100644
--- a/tests/Input/src/android/hardware/input/KeyGestureEventHandlerTest.kt
+++ b/tests/Input/src/android/hardware/input/KeyGestureEventHandlerTest.kt
@@ -18,12 +18,10 @@ package android.hardware.input
import android.content.Context
import android.content.ContextWrapper
-import android.os.IBinder
import android.platform.test.annotations.Presubmit
import android.platform.test.flag.junit.SetFlagsRule
import android.view.KeyEvent
import androidx.test.core.app.ApplicationProvider
-import com.android.server.testutils.any
import com.android.test.input.MockInputManagerRule
import org.junit.Before
import org.junit.Rule
@@ -37,6 +35,7 @@ import kotlin.test.assertEquals
import kotlin.test.assertNotNull
import kotlin.test.assertNull
import kotlin.test.fail
+import org.junit.Assert.assertThrows
/**
* Tests for [InputManager.KeyGestureEventHandler].
@@ -82,7 +81,7 @@ class KeyGestureEventHandlerTest {
// Handle key gesture handler registration.
doAnswer {
- val listener = it.getArgument(0) as IKeyGestureHandler
+ val listener = it.getArgument(1) as IKeyGestureHandler
if (registeredListener != null &&
registeredListener!!.asBinder() != listener.asBinder()) {
// There can only be one registered key gesture handler per process.
@@ -90,7 +89,7 @@ class KeyGestureEventHandlerTest {
}
registeredListener = listener
null
- }.`when`(inputManagerRule.mock).registerKeyGestureHandler(any())
+ }.`when`(inputManagerRule.mock).registerKeyGestureHandler(Mockito.any(), Mockito.any())
// Handle key gesture handler being unregistered.
doAnswer {
@@ -101,7 +100,7 @@ class KeyGestureEventHandlerTest {
}
registeredListener = null
null
- }.`when`(inputManagerRule.mock).unregisterKeyGestureHandler(any())
+ }.`when`(inputManagerRule.mock).unregisterKeyGestureHandler(Mockito.any())
}
private fun handleKeyGestureEvent(event: KeyGestureEvent) {
@@ -121,11 +120,12 @@ class KeyGestureEventHandlerTest {
var callbackCount = 0
// Add a key gesture event listener
- inputManager.registerKeyGestureEventHandler(KeyGestureHandler { event, _ ->
+ inputManager.registerKeyGestureEventHandler(
+ listOf(KeyGestureEvent.KEY_GESTURE_TYPE_HOME)
+ ) { event, _ ->
assertEquals(HOME_GESTURE_EVENT, event)
callbackCount++
- true
- })
+ }
// Request handling for key gesture event will notify the handler.
handleKeyGestureEvent(HOME_GESTURE_EVENT)
@@ -135,29 +135,41 @@ class KeyGestureEventHandlerTest {
@Test
fun testAddingHandlersRegistersInternalCallbackHandler() {
// Set up two callbacks.
- val callback1 = KeyGestureHandler { _, _ -> false }
- val callback2 = KeyGestureHandler { _, _ -> false }
+ val callback1 = InputManager.KeyGestureEventHandler { _, _ -> }
+ val callback2 = InputManager.KeyGestureEventHandler { _, _ -> }
assertNull(registeredListener)
// Adding the handler should register the callback with InputManagerService.
- inputManager.registerKeyGestureEventHandler(callback1)
+ inputManager.registerKeyGestureEventHandler(
+ listOf(KeyGestureEvent.KEY_GESTURE_TYPE_HOME),
+ callback1
+ )
assertNotNull(registeredListener)
// Adding another handler should not register new internal listener.
val currListener = registeredListener
- inputManager.registerKeyGestureEventHandler(callback2)
+ inputManager.registerKeyGestureEventHandler(
+ listOf(KeyGestureEvent.KEY_GESTURE_TYPE_BACK),
+ callback2
+ )
assertEquals(currListener, registeredListener)
}
@Test
fun testRemovingHandlersUnregistersInternalCallbackHandler() {
// Set up two callbacks.
- val callback1 = KeyGestureHandler { _, _ -> false }
- val callback2 = KeyGestureHandler { _, _ -> false }
+ val callback1 = InputManager.KeyGestureEventHandler { _, _ -> }
+ val callback2 = InputManager.KeyGestureEventHandler { _, _ -> }
- inputManager.registerKeyGestureEventHandler(callback1)
- inputManager.registerKeyGestureEventHandler(callback2)
+ inputManager.registerKeyGestureEventHandler(
+ listOf(KeyGestureEvent.KEY_GESTURE_TYPE_HOME),
+ callback1
+ )
+ inputManager.registerKeyGestureEventHandler(
+ listOf(KeyGestureEvent.KEY_GESTURE_TYPE_BACK),
+ callback2
+ )
// Only removing all handlers should remove the internal callback
inputManager.unregisterKeyGestureEventHandler(callback1)
@@ -172,47 +184,74 @@ class KeyGestureEventHandlerTest {
var callbackCount1 = 0
var callbackCount2 = 0
// Handler 1 captures all home gestures
- val callback1 = KeyGestureHandler { event, _ ->
+ val callback1 = InputManager.KeyGestureEventHandler { event, _ ->
callbackCount1++
- event.keyGestureType == KeyGestureEvent.KEY_GESTURE_TYPE_HOME
+ assertEquals(KeyGestureEvent.KEY_GESTURE_TYPE_HOME, event.keyGestureType)
}
- // Handler 2 captures all gestures
- val callback2 = KeyGestureHandler { _, _ ->
+ // Handler 2 captures all back gestures
+ val callback2 = InputManager.KeyGestureEventHandler { event, _ ->
callbackCount2++
- true
+ assertEquals(KeyGestureEvent.KEY_GESTURE_TYPE_BACK, event.keyGestureType)
}
// Add both key gesture event handlers
- inputManager.registerKeyGestureEventHandler(callback1)
- inputManager.registerKeyGestureEventHandler(callback2)
+ inputManager.registerKeyGestureEventHandler(
+ listOf(KeyGestureEvent.KEY_GESTURE_TYPE_HOME),
+ callback1
+ )
+ inputManager.registerKeyGestureEventHandler(
+ listOf(KeyGestureEvent.KEY_GESTURE_TYPE_BACK),
+ callback2
+ )
- // Request handling for key gesture event, should notify callbacks in order. So, only the
- // first handler should receive a callback since it captures the event.
+ // Request handling for home key gesture event, should notify only callback1
handleKeyGestureEvent(HOME_GESTURE_EVENT)
assertEquals(1, callbackCount1)
assertEquals(0, callbackCount2)
- // Second handler should receive the event since the first handler doesn't capture the event
+ // Request handling for back key gesture event, should notify only callback2
handleKeyGestureEvent(BACK_GESTURE_EVENT)
- assertEquals(2, callbackCount1)
+ assertEquals(1, callbackCount1)
assertEquals(1, callbackCount2)
inputManager.unregisterKeyGestureEventHandler(callback1)
- // Request handling for key gesture event, should still trigger callback2 but not callback1.
+
+ // Request handling for home key gesture event, should not trigger callback2
handleKeyGestureEvent(HOME_GESTURE_EVENT)
- assertEquals(2, callbackCount1)
- assertEquals(2, callbackCount2)
+ assertEquals(1, callbackCount1)
+ assertEquals(1, callbackCount2)
+ }
+
+ @Test
+ fun testUnableToRegisterSameHandlerTwice() {
+ val handler = InputManager.KeyGestureEventHandler { _, _ -> }
+
+ inputManager.registerKeyGestureEventHandler(
+ listOf(KeyGestureEvent.KEY_GESTURE_TYPE_HOME),
+ handler
+ )
+
+ assertThrows(IllegalArgumentException::class.java) {
+ inputManager.registerKeyGestureEventHandler(
+ listOf(KeyGestureEvent.KEY_GESTURE_TYPE_BACK), handler
+ )
+ }
}
- inner class KeyGestureHandler(
- private var handler: (event: KeyGestureEvent, token: IBinder?) -> Boolean
- ) : InputManager.KeyGestureEventHandler {
+ @Test
+ fun testUnableToRegisterSameGestureTwice() {
+ val handler1 = InputManager.KeyGestureEventHandler { _, _ -> }
+ val handler2 = InputManager.KeyGestureEventHandler { _, _ -> }
+
+ inputManager.registerKeyGestureEventHandler(
+ listOf(KeyGestureEvent.KEY_GESTURE_TYPE_HOME),
+ handler1
+ )
- override fun handleKeyGestureEvent(
- event: KeyGestureEvent,
- focusedToken: IBinder?
- ): Boolean {
- return handler(event, focusedToken)
+ assertThrows(IllegalArgumentException::class.java) {
+ inputManager.registerKeyGestureEventHandler(
+ listOf(KeyGestureEvent.KEY_GESTURE_TYPE_HOME), handler2
+ )
}
}
}
diff --git a/tests/Input/src/com/android/server/input/InputManagerServiceTests.kt b/tests/Input/src/com/android/server/input/InputManagerServiceTests.kt
index 7ec8f9ce9864..71c7a6b1119d 100644
--- a/tests/Input/src/com/android/server/input/InputManagerServiceTests.kt
+++ b/tests/Input/src/com/android/server/input/InputManagerServiceTests.kt
@@ -35,8 +35,8 @@ import android.os.PermissionEnforcer
import android.os.SystemClock
import android.os.test.FakePermissionEnforcer
import android.os.test.TestLooper
-import android.platform.test.annotations.Presubmit
import android.platform.test.annotations.EnableFlags
+import android.platform.test.annotations.Presubmit
import android.platform.test.flag.junit.SetFlagsRule
import android.provider.Settings
import android.view.View.OnKeyListener
@@ -373,6 +373,29 @@ class InputManagerServiceTests {
}
@Test
+ @EnableFlags(com.android.hardware.input.Flags.FLAG_FIX_SEARCH_MODIFIER_FALLBACKS)
+ fun testInterceptKeyBeforeDispatchingWithFallthroughEvent() {
+ service.systemRunning()
+ overrideSendActionKeyEventsToFocusedWindow(
+ /* hasPermission = */false,
+ /* hasPrivateFlag = */false
+ )
+ whenever(wmCallbacks.interceptKeyBeforeDispatching(any(), any(), anyInt())).thenReturn(0)
+
+ // Create a fallback for a key event with a meta modifier. Should result in -2,
+ // which represents the fallback event, which indicates that original key event will
+ // be ignored (not sent to app) and instead the fallback will be created and sent to the
+ // app.
+ val fallbackAction: KeyCharacterMap.FallbackAction = KeyCharacterMap.FallbackAction.obtain()
+ fallbackAction.keyCode = KeyEvent.KEYCODE_SEARCH
+ whenever(kcm.getFallbackAction(anyInt(), anyInt())).thenReturn(fallbackAction)
+
+ val event = KeyEvent( /* downTime= */0, /* eventTime= */0, KeyEvent.ACTION_DOWN,
+ KeyEvent.KEYCODE_SPACE, /* repeat= */0, KeyEvent.META_META_ON)
+ assertEquals(-2, service.interceptKeyBeforeDispatching(null, event, 0))
+ }
+
+ @Test
fun testKeyEventsNotForwardedToFocusedWindow_whenWmConsumes() {
service.systemRunning()
overrideSendActionKeyEventsToFocusedWindow(
diff --git a/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt b/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt
index 4f1fb6487b19..163dda84a71c 100644
--- a/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt
+++ b/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt
@@ -63,6 +63,7 @@ import org.junit.After
import org.junit.Assert.assertArrayEquals
import org.junit.Assert.assertEquals
import org.junit.Assert.assertNull
+import org.junit.Assert.assertThrows
import org.junit.Assert.assertTrue
import org.junit.Before
import org.junit.Rule
@@ -107,7 +108,10 @@ class KeyGestureControllerTests {
const val SETTINGS_KEY_BEHAVIOR_SETTINGS_ACTIVITY = 0
const val SETTINGS_KEY_BEHAVIOR_NOTIFICATION_PANEL = 1
const val SETTINGS_KEY_BEHAVIOR_NOTHING = 2
+ const val SYSTEM_PID = 0
const val TEST_PID = 10
+ const val RANDOM_PID1 = 11
+ const val RANDOM_PID2 = 12
}
@JvmField
@@ -170,6 +174,7 @@ class KeyGestureControllerTests {
return atomicFile
}
})
+ startNewInputGlobalTestSession()
}
@After
@@ -199,17 +204,22 @@ class KeyGestureControllerTests {
val correctIm = context.getSystemService(InputManager::class.java)!!
val virtualDevice = correctIm.getInputDevice(KeyCharacterMap.VIRTUAL_KEYBOARD)!!
val kcm = virtualDevice.keyCharacterMap!!
- inputManagerGlobalSession = InputManagerGlobal.createTestSession(iInputManager)
- val inputManager = InputManager(context)
- Mockito.`when`(context.getSystemService(Mockito.eq(Context.INPUT_SERVICE)))
- .thenReturn(inputManager)
-
val keyboardDevice = InputDevice.Builder().setId(DEVICE_ID).build()
Mockito.`when`(iInputManager.inputDeviceIds).thenReturn(intArrayOf(DEVICE_ID))
Mockito.`when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardDevice)
ExtendedMockito.`when`(KeyCharacterMap.load(Mockito.anyInt())).thenReturn(kcm)
}
+ private fun startNewInputGlobalTestSession() {
+ if (this::inputManagerGlobalSession.isInitialized) {
+ inputManagerGlobalSession.close()
+ }
+ inputManagerGlobalSession = InputManagerGlobal.createTestSession(iInputManager)
+ val inputManager = InputManager(context)
+ Mockito.`when`(context.getSystemService(Mockito.eq(Context.INPUT_SERVICE)))
+ .thenReturn(inputManager)
+ }
+
private fun setupKeyGestureController() {
keyGestureController =
KeyGestureController(
@@ -225,13 +235,14 @@ class KeyGestureControllerTests {
return accessibilityShortcutController
}
})
- Mockito.`when`(iInputManager.registerKeyGestureHandler(Mockito.any()))
+ Mockito.`when`(iInputManager.registerKeyGestureHandler(Mockito.any(), Mockito.any()))
.thenAnswer {
val args = it.arguments
if (args[0] != null) {
keyGestureController.registerKeyGestureHandler(
- args[0] as IKeyGestureHandler,
- TEST_PID
+ args[0] as IntArray,
+ args[1] as IKeyGestureHandler,
+ SYSTEM_PID
)
}
}
@@ -285,59 +296,6 @@ class KeyGestureControllerTests {
)
}
- @Test
- fun testKeyGestureEvent_multipleGestureHandlers() {
- setupKeyGestureController()
-
- // Set up two callbacks.
- var callbackCount1 = 0
- var callbackCount2 = 0
- var selfCallback = 0
- val externalHandler1 = KeyGestureHandler { _, _ ->
- callbackCount1++
- true
- }
- val externalHandler2 = KeyGestureHandler { _, _ ->
- callbackCount2++
- true
- }
- val selfHandler = KeyGestureHandler { _, _ ->
- selfCallback++
- false
- }
-
- // Register key gesture handler: External process (last in priority)
- keyGestureController.registerKeyGestureHandler(externalHandler1, currentPid + 1)
-
- // Register key gesture handler: External process (second in priority)
- keyGestureController.registerKeyGestureHandler(externalHandler2, currentPid - 1)
-
- // Register key gesture handler: Self process (first in priority)
- keyGestureController.registerKeyGestureHandler(selfHandler, currentPid)
-
- keyGestureController.handleKeyGesture(/* deviceId = */ 0, intArrayOf(KeyEvent.KEYCODE_HOME),
- /* modifierState = */ 0, KeyGestureEvent.KEY_GESTURE_TYPE_HOME,
- KeyGestureEvent.ACTION_GESTURE_COMPLETE, /* displayId */ 0,
- /* focusedToken = */ null, /* flags = */ 0, /* appLaunchData = */null
- )
-
- assertEquals(
- "Self handler should get callbacks first",
- 1,
- selfCallback
- )
- assertEquals(
- "Higher priority handler should get callbacks first",
- 1,
- callbackCount2
- )
- assertEquals(
- "Lower priority handler should not get callbacks if already handled",
- 0,
- callbackCount1
- )
- }
-
class TestData(
val name: String,
val keys: IntArray,
@@ -789,10 +747,6 @@ class KeyGestureControllerTests {
)
fun testCustomKeyGesturesNotAllowedForSystemGestures(test: TestData) {
setupKeyGestureController()
- // Need to re-init so that bookmarks are correctly blocklisted
- Mockito.`when`(iInputManager.getAppLaunchBookmarks())
- .thenReturn(keyGestureController.appLaunchBookmarks)
- keyGestureController.systemRunning()
val builder = InputGestureData.Builder()
.setKeyGestureType(test.expectedKeyGestureType)
@@ -1163,9 +1117,6 @@ class KeyGestureControllerTests {
KeyEvent.KEYCODE_FULLSCREEN
)
- val handler = KeyGestureHandler { _, _ -> false }
- keyGestureController.registerKeyGestureHandler(handler, 0)
-
for (key in testKeys) {
sendKeys(intArrayOf(key), assertNotSentToApps = true)
}
@@ -1179,6 +1130,7 @@ class KeyGestureControllerTests {
testKeyGestureNotProduced(
"SEARCH -> Default Search",
intArrayOf(KeyEvent.KEYCODE_SEARCH),
+ intArrayOf(KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_SEARCH)
)
}
@@ -1207,6 +1159,10 @@ class KeyGestureControllerTests {
testKeyGestureNotProduced(
"SETTINGS -> Do Nothing",
intArrayOf(KeyEvent.KEYCODE_SETTINGS),
+ intArrayOf(
+ KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_SEARCH,
+ KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_NOTIFICATION_PANEL
+ )
)
}
@@ -1290,28 +1246,6 @@ class KeyGestureControllerTests {
)
),
TestData(
- "VOLUME_DOWN + VOLUME_UP -> Accessibility Chord",
- intArrayOf(KeyEvent.KEYCODE_VOLUME_DOWN, KeyEvent.KEYCODE_VOLUME_UP),
- KeyGestureEvent.KEY_GESTURE_TYPE_ACCESSIBILITY_SHORTCUT_CHORD,
- intArrayOf(KeyEvent.KEYCODE_VOLUME_DOWN, KeyEvent.KEYCODE_VOLUME_UP),
- 0,
- intArrayOf(
- KeyGestureEvent.ACTION_GESTURE_START,
- KeyGestureEvent.ACTION_GESTURE_COMPLETE
- )
- ),
- TestData(
- "BACK + DPAD_DOWN -> Accessibility Chord(for TV)",
- intArrayOf(KeyEvent.KEYCODE_BACK, KeyEvent.KEYCODE_DPAD_DOWN),
- KeyGestureEvent.KEY_GESTURE_TYPE_ACCESSIBILITY_SHORTCUT_CHORD,
- intArrayOf(KeyEvent.KEYCODE_BACK, KeyEvent.KEYCODE_DPAD_DOWN),
- 0,
- intArrayOf(
- KeyGestureEvent.ACTION_GESTURE_START,
- KeyGestureEvent.ACTION_GESTURE_COMPLETE
- )
- ),
- TestData(
"BACK + DPAD_CENTER -> TV Trigger Bug Report",
intArrayOf(KeyEvent.KEYCODE_BACK, KeyEvent.KEYCODE_DPAD_CENTER),
KeyGestureEvent.KEY_GESTURE_TYPE_TV_TRIGGER_BUG_REPORT,
@@ -1428,9 +1362,11 @@ class KeyGestureControllerTests {
testLooper.dispatchAll()
// Reinitialize the gesture controller simulating a login/logout for the user.
+ startNewInputGlobalTestSession()
setupKeyGestureController()
keyGestureController.setCurrentUserId(userId)
testLooper.dispatchAll()
+
val savedInputGestures = keyGestureController.getCustomInputGestures(userId, null)
assertEquals(
"Test: $test doesn't produce correct number of saved input gestures",
@@ -1469,6 +1405,7 @@ class KeyGestureControllerTests {
// Delete the old data and reinitialize the controller simulating a "fresh" install.
tempFile.delete()
+ startNewInputGlobalTestSession()
setupKeyGestureController()
keyGestureController.setCurrentUserId(userId)
testLooper.dispatchAll()
@@ -1541,9 +1478,12 @@ class KeyGestureControllerTests {
val handledEvents = mutableListOf<KeyGestureEvent>()
val handler = KeyGestureHandler { event, _ ->
handledEvents.add(KeyGestureEvent(event))
- true
}
- keyGestureController.registerKeyGestureHandler(handler, 0)
+ keyGestureController.registerKeyGestureHandler(
+ intArrayOf(test.expectedKeyGestureType),
+ handler,
+ TEST_PID
+ )
handledEvents.clear()
keyGestureController.handleTouchpadGesture(test.touchpadGestureType)
@@ -1570,7 +1510,7 @@ class KeyGestureControllerTests {
event.appLaunchData
)
- keyGestureController.unregisterKeyGestureHandler(handler, 0)
+ keyGestureController.unregisterKeyGestureHandler(handler, TEST_PID)
}
@Test
@@ -1591,9 +1531,11 @@ class KeyGestureControllerTests {
testLooper.dispatchAll()
// Reinitialize the gesture controller simulating a login/logout for the user.
+ startNewInputGlobalTestSession()
setupKeyGestureController()
keyGestureController.setCurrentUserId(userId)
testLooper.dispatchAll()
+
val savedInputGestures = keyGestureController.getCustomInputGestures(userId, null)
assertEquals(
"Test: $test doesn't produce correct number of saved input gestures",
@@ -1627,6 +1569,7 @@ class KeyGestureControllerTests {
// Delete the old data and reinitialize the controller simulating a "fresh" install.
tempFile.delete()
+ startNewInputGlobalTestSession()
setupKeyGestureController()
keyGestureController.setCurrentUserId(userId)
testLooper.dispatchAll()
@@ -1699,13 +1642,97 @@ class KeyGestureControllerTests {
Mockito.verify(accessibilityShortcutController, never()).performAccessibilityShortcut()
}
+ @Test
+ fun testUnableToRegisterFromSamePidTwice() {
+ setupKeyGestureController()
+
+ val handler1 = KeyGestureHandler { _, _ -> }
+ val handler2 = KeyGestureHandler { _, _ -> }
+ keyGestureController.registerKeyGestureHandler(
+ intArrayOf(KeyGestureEvent.KEY_GESTURE_TYPE_HOME),
+ handler1,
+ RANDOM_PID1
+ )
+
+ assertThrows(IllegalStateException::class.java) {
+ keyGestureController.registerKeyGestureHandler(
+ intArrayOf(KeyGestureEvent.KEY_GESTURE_TYPE_BACK),
+ handler2,
+ RANDOM_PID1
+ )
+ }
+ }
+
+ @Test
+ fun testUnableToRegisterSameGestureTwice() {
+ setupKeyGestureController()
+
+ val handler1 = KeyGestureHandler { _, _ -> }
+ val handler2 = KeyGestureHandler { _, _ -> }
+ keyGestureController.registerKeyGestureHandler(
+ intArrayOf(KeyGestureEvent.KEY_GESTURE_TYPE_HOME),
+ handler1,
+ RANDOM_PID1
+ )
+
+ assertThrows(IllegalArgumentException::class.java) {
+ keyGestureController.registerKeyGestureHandler(
+ intArrayOf(KeyGestureEvent.KEY_GESTURE_TYPE_HOME),
+ handler2,
+ RANDOM_PID2
+ )
+ }
+ }
+
+ @Test
+ fun testUnableToRegisterEmptyListOfGestures() {
+ setupKeyGestureController()
+
+ val handler = KeyGestureHandler { _, _ -> }
+
+ assertThrows(IllegalArgumentException::class.java) {
+ keyGestureController.registerKeyGestureHandler(
+ intArrayOf(),
+ handler,
+ RANDOM_PID1
+ )
+ }
+ }
+
+ @Test
+ fun testGestureHandlerNotCalledOnceUnregistered() {
+ setupKeyGestureController()
+
+ var callbackCount = 0
+ val handler1 = KeyGestureHandler { _, _ -> callbackCount++ }
+ keyGestureController.registerKeyGestureHandler(
+ intArrayOf(KeyGestureEvent.KEY_GESTURE_TYPE_RECENT_APPS),
+ handler1,
+ TEST_PID
+ )
+ sendKeys(intArrayOf(KeyEvent.KEYCODE_RECENT_APPS))
+ assertEquals(1, callbackCount)
+
+ keyGestureController.unregisterKeyGestureHandler(
+ handler1,
+ TEST_PID
+ )
+
+ // Callback should not be sent after unregister
+ sendKeys(intArrayOf(KeyEvent.KEYCODE_RECENT_APPS))
+ assertEquals(1, callbackCount)
+ }
+
private fun testKeyGestureInternal(test: TestData) {
val handledEvents = mutableListOf<KeyGestureEvent>()
val handler = KeyGestureHandler { event, _ ->
handledEvents.add(KeyGestureEvent(event))
- true
}
- keyGestureController.registerKeyGestureHandler(handler, 0)
+ keyGestureController.registerKeyGestureHandler(
+ intArrayOf(test.expectedKeyGestureType),
+ handler,
+ TEST_PID
+ )
handledEvents.clear()
sendKeys(test.keys)
@@ -1744,16 +1771,19 @@ class KeyGestureControllerTests {
)
}
- keyGestureController.unregisterKeyGestureHandler(handler, 0)
+ keyGestureController.unregisterKeyGestureHandler(handler, TEST_PID)
}
- private fun testKeyGestureNotProduced(testName: String, testKeys: IntArray) {
+ private fun testKeyGestureNotProduced(
+ testName: String,
+ testKeys: IntArray,
+ possibleGestures: IntArray
+ ) {
var handledEvents = mutableListOf<KeyGestureEvent>()
val handler = KeyGestureHandler { event, _ ->
handledEvents.add(KeyGestureEvent(event))
- true
}
- keyGestureController.registerKeyGestureHandler(handler, 0)
+ keyGestureController.registerKeyGestureHandler(possibleGestures, handler, TEST_PID)
handledEvents.clear()
sendKeys(testKeys)
@@ -1823,10 +1853,10 @@ class KeyGestureControllerTests {
}
inner class KeyGestureHandler(
- private var handler: (event: AidlKeyGestureEvent, token: IBinder?) -> Boolean
+ private var handler: (event: AidlKeyGestureEvent, token: IBinder?) -> Unit
) : IKeyGestureHandler.Stub() {
- override fun handleKeyGesture(event: AidlKeyGestureEvent, token: IBinder?): Boolean {
- return handler(event, token)
+ override fun handleKeyGesture(event: AidlKeyGestureEvent, token: IBinder?) {
+ handler(event, token)
}
}
}
diff --git a/tests/Input/src/com/android/test/input/AnrTest.kt b/tests/Input/src/com/android/test/input/AnrTest.kt
index f8cb86b7b1fe..3ad3763a5d20 100644
--- a/tests/Input/src/com/android/test/input/AnrTest.kt
+++ b/tests/Input/src/com/android/test/input/AnrTest.kt
@@ -16,6 +16,7 @@
package com.android.test.input
import android.app.ActivityManager
+import android.app.ActivityTaskManager
import android.app.ApplicationExitInfo
import android.app.Instrumentation
import android.content.Intent
@@ -28,6 +29,7 @@ import android.os.SystemClock
import android.server.wm.CtsWindowInfoUtils.getWindowCenter
import android.server.wm.CtsWindowInfoUtils.waitForWindowOnTop
import android.testing.PollingCheck
+import android.util.Log
import android.view.InputEvent
import android.view.MotionEvent
import android.view.MotionEvent.ACTION_DOWN
@@ -46,21 +48,19 @@ import com.android.cts.input.inputeventmatchers.withMotionAction
import java.time.Duration
import java.util.concurrent.LinkedBlockingQueue
import java.util.function.Supplier
-import org.junit.After
import org.junit.Assert.assertEquals
import org.junit.Assert.assertTrue
import org.junit.Assert.fail
-import org.junit.Before
+import org.junit.Assume.assumeTrue
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
/**
- * Click on the center of the window identified by the provided window token.
- * The click is performed using "UinputTouchScreen" device.
- * If the touchscreen device is closed too soon, it may cause the click to be dropped. Therefore,
- * the provided runnable can ensure that the click is delivered before the device is closed, thus
- * avoiding this race.
+ * Click on the center of the window identified by the provided window token. The click is performed
+ * using "UinputTouchScreen" device. If the touchscreen device is closed too soon, it may cause the
+ * click to be dropped. Therefore, the provided runnable can ensure that the click is delivered
+ * before the device is closed, thus avoiding this race.
*/
private fun clickOnWindow(
token: IBinder,
@@ -104,6 +104,10 @@ class AnrTest {
private val remoteInputEvents = LinkedBlockingQueue<InputEvent>()
private val verifier = BlockingQueueEventVerifier(remoteInputEvents)
+ // Some devices don't support ANR error dialogs, such as cars, TVs, etc.
+ private val anrDialogsAreSupported =
+ ActivityTaskManager.currentUiModeSupportsErrorDialogs(instrumentation.targetContext)
+
val binder =
object : IAnrTestService.Stub() {
override fun provideActivityInfo(token: IBinder, displayId: Int, pid: Int) {
@@ -121,34 +125,37 @@ class AnrTest {
@get:Rule val debugInputRule = DebugInputRule()
- @Before
- fun setUp() {
- startUnresponsiveActivity()
- PACKAGE_NAME = UnresponsiveGestureMonitorActivity::class.java.getPackage()!!.getName()
- }
-
- @After fun tearDown() {}
-
@Test
@DebugInputRule.DebugInput(bug = 339924248)
fun testGestureMonitorAnr_Close() {
+ startUnresponsiveActivity()
+ val timestamp = System.currentTimeMillis()
triggerAnr()
- clickCloseAppOnAnrDialog()
+ if (anrDialogsAreSupported) {
+ clickCloseAppOnAnrDialog()
+ } else {
+ Log.i(TAG, "The device does not support ANR dialogs, skipping check for ANR window")
+ // We still want to wait for the app to get killed by the ActivityManager
+ }
+ waitForNewExitReasonAfter(timestamp)
}
@Test
@DebugInputRule.DebugInput(bug = 339924248)
fun testGestureMonitorAnr_Wait() {
+ assumeTrue(anrDialogsAreSupported)
+ startUnresponsiveActivity()
triggerAnr()
clickWaitOnAnrDialog()
SystemClock.sleep(500) // Wait at least 500ms after tapping on wait
// ANR dialog should reappear after a delay - find the close button on it to verify
+ val timestamp = System.currentTimeMillis()
clickCloseAppOnAnrDialog()
+ waitForNewExitReasonAfter(timestamp)
}
private fun clickCloseAppOnAnrDialog() {
// Find anr dialog and kill app
- val timestamp = System.currentTimeMillis()
val uiDevice: UiDevice = UiDevice.getInstance(instrumentation)
val closeAppButton: UiObject2? =
uiDevice.wait(Until.findObject(By.res("android:id/aerr_close")), 20000)
@@ -157,14 +164,6 @@ class AnrTest {
return
}
closeAppButton.click()
- /**
- * We must wait for the app to be fully closed before exiting this test. This is because
- * another test may again invoke 'am start' for the same activity. If the 1st process that
- * got ANRd isn't killed by the time second 'am start' runs, the killing logic will apply to
- * the newly launched 'am start' instance, and the second test will fail because the
- * unresponsive activity will never be launched.
- */
- waitForNewExitReasonAfter(timestamp)
}
private fun clickWaitOnAnrDialog() {
@@ -180,16 +179,27 @@ class AnrTest {
}
private fun getExitReasons(): List<ApplicationExitInfo> {
+ val packageName = UnresponsiveGestureMonitorActivity::class.java.getPackage()!!.name
lateinit var infos: List<ApplicationExitInfo>
instrumentation.runOnMainSync {
val am = instrumentation.getContext().getSystemService(ActivityManager::class.java)!!
- infos = am.getHistoricalProcessExitReasons(PACKAGE_NAME, remotePid!!, NO_MAX)
+ infos = am.getHistoricalProcessExitReasons(packageName, remotePid!!, NO_MAX)
}
return infos
}
+ /**
+ * We must wait for the app to be fully closed before exiting this test. This is because another
+ * test may again invoke 'am start' for the same activity. If the 1st process that got ANRd
+ * isn't killed by the time second 'am start' runs, the killing logic will apply to the newly
+ * launched 'am start' instance, and the second test will fail because the unresponsive activity
+ * will never be launched.
+ *
+ * Also, we must ensure that we wait until it's killed, so that the next test can launch this
+ * activity again.
+ */
private fun waitForNewExitReasonAfter(timestamp: Long) {
- PollingCheck.waitFor {
+ PollingCheck.waitFor(Duration.ofSeconds(20).toMillis() * Build.HW_TIMEOUT_MULTIPLIER) {
val reasons = getExitReasons()
!reasons.isEmpty() && reasons[0].timestamp >= timestamp
}
@@ -199,16 +209,15 @@ class AnrTest {
}
private fun triggerAnr() {
- clickOnWindow(
- remoteWindowToken!!,
- remoteDisplayId!!,
- instrumentation,
- ) { verifier.assertReceivedMotion(withMotionAction(ACTION_DOWN)) }
+ clickOnWindow(remoteWindowToken!!, remoteDisplayId!!, instrumentation) {
+ verifier.assertReceivedMotion(withMotionAction(ACTION_DOWN))
+ }
SystemClock.sleep(DISPATCHING_TIMEOUT.toLong()) // default ANR timeout for gesture monitors
}
private fun startUnresponsiveActivity() {
+ remoteWindowToken = null
val intent =
Intent(instrumentation.targetContext, UnresponsiveGestureMonitorActivity::class.java)
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_NEW_DOCUMENT
@@ -218,12 +227,17 @@ class AnrTest {
instrumentation.targetContext.startActivity(intent)
// first, wait for the token to become valid
PollingCheck.check(
- "UnresponsiveGestureMonitorActivity failed to call 'provideActivityInfo'",
- Duration.ofSeconds(5).toMillis()) { remoteWindowToken != null }
+ "UnresponsiveGestureMonitorActivity failed to call 'provideActivityInfo'",
+ Duration.ofSeconds(10).toMillis() * Build.HW_TIMEOUT_MULTIPLIER,
+ ) {
+ remoteWindowToken != null
+ }
// next, wait for the window of the activity to get on top
// we could combine the two checks above, but the current setup makes it easier to detect
// errors
- assertTrue("Remote activity window did not become visible",
- waitForWindowOnTop(Duration.ofSeconds(5), Supplier { remoteWindowToken }))
+ assertTrue(
+ "Remote activity window did not become visible",
+ waitForWindowOnTop(Duration.ofSeconds(5), Supplier { remoteWindowToken }),
+ )
}
}
diff --git a/tests/TelephonyCommonTests/src/com/android/internal/telephony/tests/SmsApplicationTest.java b/tests/TelephonyCommonTests/src/com/android/internal/telephony/tests/SmsApplicationTest.java
index adefac64dbae..6846d489ecaa 100644
--- a/tests/TelephonyCommonTests/src/com/android/internal/telephony/tests/SmsApplicationTest.java
+++ b/tests/TelephonyCommonTests/src/com/android/internal/telephony/tests/SmsApplicationTest.java
@@ -231,7 +231,7 @@ public class SmsApplicationTest {
.replacePreferredActivity(intentFilterCaptor.capture(),
eq(IntentFilter.MATCH_CATEGORY_SCHEME
| IntentFilter.MATCH_ADJUSTMENT_NORMAL),
- isNotNull(List.class),
+ (List<ComponentName>)isNotNull(),
eq(new ComponentName(TEST_COMPONENT_NAME.getPackageName(), SEND_TO_NAME)));
Set<String> capturedSchemes = intentFilterCaptor.getAllValues().stream()
diff --git a/tests/TtsTests/src/com/android/speech/tts/TextToSpeechTests.java b/tests/TtsTests/src/com/android/speech/tts/TextToSpeechTests.java
index d9bb7db17685..5419d9444abd 100644
--- a/tests/TtsTests/src/com/android/speech/tts/TextToSpeechTests.java
+++ b/tests/TtsTests/src/com/android/speech/tts/TextToSpeechTests.java
@@ -110,7 +110,7 @@ public class TextToSpeechTests extends InstrumentationTestCase {
blockingCallSpeak("foo bar", delegate);
ArgumentCaptor<SynthesisRequest> req = ArgumentCaptor.forClass(SynthesisRequest.class);
Mockito.verify(delegate, Mockito.times(1)).onSynthesizeText(req.capture(),
- Mockito.<SynthesisCallback>anyObject());
+ Mockito.<SynthesisCallback>any());
assertEquals("eng", req.getValue().getLanguage());
assertEquals("USA", req.getValue().getCountry());
@@ -133,7 +133,7 @@ public class TextToSpeechTests extends InstrumentationTestCase {
blockingCallSpeak("le fou barre", delegate);
ArgumentCaptor<SynthesisRequest> req2 = ArgumentCaptor.forClass(SynthesisRequest.class);
Mockito.verify(delegate, Mockito.times(1)).onSynthesizeText(req2.capture(),
- Mockito.<SynthesisCallback>anyObject());
+ Mockito.<SynthesisCallback>any());
// The params are basically unchanged.
assertEquals("eng", req2.getValue().getLanguage());
@@ -177,7 +177,7 @@ public class TextToSpeechTests extends InstrumentationTestCase {
blockingCallSpeak("foo bar", delegate);
ArgumentCaptor<SynthesisRequest> req = ArgumentCaptor.forClass(SynthesisRequest.class);
Mockito.verify(delegate, Mockito.times(1)).onSynthesizeText(req.capture(),
- Mockito.<SynthesisCallback>anyObject());
+ Mockito.<SynthesisCallback>any());
assertEquals(defaultLocale.getISO3Language(), req.getValue().getLanguage());
assertEquals(defaultLocale.getISO3Country(), req.getValue().getCountry());
@@ -189,8 +189,8 @@ public class TextToSpeechTests extends InstrumentationTestCase {
private void blockingCallSpeak(String speech, IDelegate mock) throws
InterruptedException {
final CountDownLatch latch = new CountDownLatch(1);
- doCountDown(latch).when(mock).onSynthesizeText(Mockito.<SynthesisRequest>anyObject(),
- Mockito.<SynthesisCallback>anyObject());
+ doCountDown(latch).when(mock).onSynthesizeText(Mockito.<SynthesisRequest>any(),
+ Mockito.<SynthesisCallback>any());
mTts.speak(speech, TextToSpeech.QUEUE_ADD, null);
awaitCountDown(latch, 5, TimeUnit.SECONDS);
diff --git a/tests/vcn/Android.bp b/tests/vcn/Android.bp
index 5ad1d1dce324..205fdb119a78 100644
--- a/tests/vcn/Android.bp
+++ b/tests/vcn/Android.bp
@@ -17,9 +17,7 @@ android_test {
// For access hidden connectivity methods in tests
defaults: ["framework-connectivity-test-defaults"],
- // Tethering module is released in R so this test needs to be installable
- // on R
- min_sdk_version: "30",
+ min_sdk_version: "36",
srcs: [
"java/**/*.java",
diff --git a/tests/vcn/AndroidManifest.xml b/tests/vcn/AndroidManifest.xml
index 6e8b4ac48816..c940eeff8948 100644
--- a/tests/vcn/AndroidManifest.xml
+++ b/tests/vcn/AndroidManifest.xml
@@ -16,8 +16,8 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.frameworks.tests.vcn">
- <!-- TODO: b/374174952 Use 36 after Android B finalization -->
- <uses-sdk android:minSdkVersion="30" android:targetSdkVersion="35" />
+
+ <uses-sdk android:minSdkVersion="36" android:targetSdkVersion="36" />
<application>
<uses-library android:name="android.test.runner" />
diff --git a/tests/vcn/AndroidTest.xml b/tests/vcn/AndroidTest.xml
index 9c8362f36cb2..ffb79adf906b 100644
--- a/tests/vcn/AndroidTest.xml
+++ b/tests/vcn/AndroidTest.xml
@@ -27,6 +27,8 @@
class="com.android.tradefed.testtype.suite.module.MainlineTestModuleController">
<option name="mainline-module-package-name" value="com.google.android.tethering" />
</object>
+ <object type="module_controller"
+ class="com.android.tradefed.testtype.suite.module.Sdk36ModuleController" />
<test class="com.android.tradefed.testtype.AndroidJUnitTest" >
<option name="package" value="com.android.frameworks.tests.vcn" />
diff --git a/tests/vcn/java/android/net/vcn/VcnCellUnderlyingNetworkTemplateTest.java b/tests/vcn/java/android/net/vcn/VcnCellUnderlyingNetworkTemplateTest.java
index 0fa11ae1fe7d..95205f4fd790 100644
--- a/tests/vcn/java/android/net/vcn/VcnCellUnderlyingNetworkTemplateTest.java
+++ b/tests/vcn/java/android/net/vcn/VcnCellUnderlyingNetworkTemplateTest.java
@@ -23,12 +23,8 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.fail;
-import android.os.Build;
-
import androidx.test.filters.SmallTest;
-
-import com.android.testutils.DevSdkIgnoreRule;
-import com.android.testutils.DevSdkIgnoreRunner;
+import androidx.test.runner.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -36,10 +32,7 @@ import org.junit.runner.RunWith;
import java.util.HashSet;
import java.util.Set;
-// TODO: b/374174952 After B finalization, use Sdk36ModuleController to ensure VCN tests only run on
-// Android B/B+
-@RunWith(DevSdkIgnoreRunner.class)
-@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.VANILLA_ICE_CREAM)
+@RunWith(AndroidJUnit4.class)
@SmallTest
public class VcnCellUnderlyingNetworkTemplateTest extends VcnUnderlyingNetworkTemplateTestBase {
private static final Set<String> ALLOWED_PLMN_IDS = new HashSet<>();
diff --git a/tests/vcn/java/android/net/vcn/VcnConfigTest.java b/tests/vcn/java/android/net/vcn/VcnConfigTest.java
index fa97de0aff45..73a0a6183cb6 100644
--- a/tests/vcn/java/android/net/vcn/VcnConfigTest.java
+++ b/tests/vcn/java/android/net/vcn/VcnConfigTest.java
@@ -29,14 +29,11 @@ import static org.mockito.Mockito.mock;
import android.annotation.NonNull;
import android.content.Context;
-import android.os.Build;
import android.os.Parcel;
import android.util.ArraySet;
import androidx.test.filters.SmallTest;
-
-import com.android.testutils.DevSdkIgnoreRule;
-import com.android.testutils.DevSdkIgnoreRunner;
+import androidx.test.runner.AndroidJUnit4;
import org.junit.Before;
import org.junit.Test;
@@ -45,10 +42,7 @@ import org.junit.runner.RunWith;
import java.util.Collections;
import java.util.Set;
-// TODO: b/374174952 After B finalization, use Sdk36ModuleController to ensure VCN tests only run on
-// Android B/B+
-@RunWith(DevSdkIgnoreRunner.class)
-@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.VANILLA_ICE_CREAM)
+@RunWith(AndroidJUnit4.class)
@SmallTest
public class VcnConfigTest {
private static final String TEST_PACKAGE_NAME = VcnConfigTest.class.getPackage().getName();
diff --git a/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java b/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java
index 990cc74caf6c..59dc68900100 100644
--- a/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java
+++ b/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java
@@ -34,13 +34,10 @@ import android.net.ipsec.ike.IkeSessionParams;
import android.net.ipsec.ike.IkeTunnelConnectionParams;
import android.net.vcn.persistablebundleutils.IkeSessionParamsUtilsTest;
import android.net.vcn.persistablebundleutils.TunnelConnectionParamsUtilsTest;
-import android.os.Build;
import android.os.PersistableBundle;
import androidx.test.filters.SmallTest;
-
-import com.android.testutils.DevSdkIgnoreRule;
-import com.android.testutils.DevSdkIgnoreRunner;
+import androidx.test.runner.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -52,10 +49,7 @@ import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;
-// TODO: b/374174952 After B finalization, use Sdk36ModuleController to ensure VCN tests only run on
-// Android B/B+
-@RunWith(DevSdkIgnoreRunner.class)
-@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.VANILLA_ICE_CREAM)
+@RunWith(AndroidJUnit4.class)
@SmallTest
public class VcnGatewayConnectionConfigTest {
// Public for use in VcnGatewayConnectionTest
diff --git a/tests/vcn/java/android/net/vcn/VcnManagerTest.java b/tests/vcn/java/android/net/vcn/VcnManagerTest.java
index 1739fbc0fa6d..ecb177e81ef3 100644
--- a/tests/vcn/java/android/net/vcn/VcnManagerTest.java
+++ b/tests/vcn/java/android/net/vcn/VcnManagerTest.java
@@ -38,13 +38,10 @@ import android.net.NetworkCapabilities;
import android.net.vcn.VcnManager.VcnStatusCallback;
import android.net.vcn.VcnManager.VcnStatusCallbackBinder;
import android.net.vcn.VcnManager.VcnUnderlyingNetworkPolicyListener;
-import android.os.Build;
import android.os.ParcelUuid;
import androidx.test.filters.SmallTest;
-
-import com.android.testutils.DevSdkIgnoreRule;
-import com.android.testutils.DevSdkIgnoreRunner;
+import androidx.test.runner.AndroidJUnit4;
import org.junit.Before;
import org.junit.Test;
@@ -55,10 +52,7 @@ import java.net.UnknownHostException;
import java.util.UUID;
import java.util.concurrent.Executor;
-// TODO: b/374174952 After B finalization, use Sdk36ModuleController to ensure VCN tests only run on
-// Android B/B+
-@RunWith(DevSdkIgnoreRunner.class)
-@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.VANILLA_ICE_CREAM)
+@RunWith(AndroidJUnit4.class)
@SmallTest
public class VcnManagerTest {
private static final ParcelUuid SUB_GROUP = new ParcelUuid(new UUID(0, 0));
diff --git a/tests/vcn/java/android/net/vcn/VcnTransportInfoTest.java b/tests/vcn/java/android/net/vcn/VcnTransportInfoTest.java
index 52952eb3f2cc..1d57cf2bfbd5 100644
--- a/tests/vcn/java/android/net/vcn/VcnTransportInfoTest.java
+++ b/tests/vcn/java/android/net/vcn/VcnTransportInfoTest.java
@@ -30,23 +30,17 @@ import static org.junit.Assert.fail;
import android.net.NetworkCapabilities;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiInfo;
-import android.os.Build;
import android.os.Parcel;
import androidx.test.filters.SmallTest;
-
-import com.android.testutils.DevSdkIgnoreRule;
-import com.android.testutils.DevSdkIgnoreRunner;
+import androidx.test.runner.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.util.Arrays;
-// TODO: b/374174952 After B finalization, use Sdk36ModuleController to ensure VCN tests only run on
-// Android B/B+
-@RunWith(DevSdkIgnoreRunner.class)
-@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.VANILLA_ICE_CREAM)
+@RunWith(AndroidJUnit4.class)
@SmallTest
public class VcnTransportInfoTest {
private static final int SUB_ID = 1;
diff --git a/tests/vcn/java/android/net/vcn/VcnUnderlyingNetworkPolicyTest.java b/tests/vcn/java/android/net/vcn/VcnUnderlyingNetworkPolicyTest.java
index c82d2003dbf6..891298a32e7d 100644
--- a/tests/vcn/java/android/net/vcn/VcnUnderlyingNetworkPolicyTest.java
+++ b/tests/vcn/java/android/net/vcn/VcnUnderlyingNetworkPolicyTest.java
@@ -22,20 +22,14 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
import android.net.NetworkCapabilities;
-import android.os.Build;
import androidx.test.filters.SmallTest;
-
-import com.android.testutils.DevSdkIgnoreRule;
-import com.android.testutils.DevSdkIgnoreRunner;
+import androidx.test.runner.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
-// TODO: b/374174952 After B finalization, use Sdk36ModuleController to ensure VCN tests only run on
-// Android B/B+
-@RunWith(DevSdkIgnoreRunner.class)
-@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.VANILLA_ICE_CREAM)
+@RunWith(AndroidJUnit4.class)
@SmallTest
public class VcnUnderlyingNetworkPolicyTest {
private static final VcnUnderlyingNetworkPolicy DEFAULT_NETWORK_POLICY =
diff --git a/tests/vcn/java/android/net/vcn/VcnUnderlyingNetworkSpecifierTest.java b/tests/vcn/java/android/net/vcn/VcnUnderlyingNetworkSpecifierTest.java
index 22361cc71f12..2110d6ee7c86 100644
--- a/tests/vcn/java/android/net/vcn/VcnUnderlyingNetworkSpecifierTest.java
+++ b/tests/vcn/java/android/net/vcn/VcnUnderlyingNetworkSpecifierTest.java
@@ -22,20 +22,14 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import android.net.TelephonyNetworkSpecifier;
-import android.os.Build;
import androidx.test.filters.SmallTest;
-
-import com.android.testutils.DevSdkIgnoreRule;
-import com.android.testutils.DevSdkIgnoreRunner;
+import androidx.test.runner.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
-// TODO: b/374174952 After B finalization, use Sdk36ModuleController to ensure VCN tests only run on
-// Android B/B+
-@RunWith(DevSdkIgnoreRunner.class)
-@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.VANILLA_ICE_CREAM)
+@RunWith(AndroidJUnit4.class)
@SmallTest
public class VcnUnderlyingNetworkSpecifierTest {
private static final int[] TEST_SUB_IDS = new int[] {1, 2, 3, 5};
diff --git a/tests/vcn/java/android/net/vcn/VcnUtilsTest.java b/tests/vcn/java/android/net/vcn/VcnUtilsTest.java
index fb040d8f9b91..0c3f9fedac05 100644
--- a/tests/vcn/java/android/net/vcn/VcnUtilsTest.java
+++ b/tests/vcn/java/android/net/vcn/VcnUtilsTest.java
@@ -30,12 +30,9 @@ import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.TelephonyNetworkSpecifier;
import android.net.wifi.WifiInfo;
-import android.os.Build;
import androidx.test.filters.SmallTest;
-
-import com.android.testutils.DevSdkIgnoreRule;
-import com.android.testutils.DevSdkIgnoreRunner;
+import androidx.test.runner.AndroidJUnit4;
import org.junit.Before;
import org.junit.Test;
@@ -44,10 +41,7 @@ import org.junit.runner.RunWith;
import java.util.Arrays;
import java.util.Collections;
-// TODO: b/374174952 After B finalization, use Sdk36ModuleController to ensure VCN tests only run on
-// Android B/B+
-@RunWith(DevSdkIgnoreRunner.class)
-@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.VANILLA_ICE_CREAM)
+@RunWith(AndroidJUnit4.class)
@SmallTest
public class VcnUtilsTest {
private static final int SUB_ID = 1;
diff --git a/tests/vcn/java/android/net/vcn/VcnWifiUnderlyingNetworkTemplateTest.java b/tests/vcn/java/android/net/vcn/VcnWifiUnderlyingNetworkTemplateTest.java
index 2c072e1cbc88..dbbfd83eab59 100644
--- a/tests/vcn/java/android/net/vcn/VcnWifiUnderlyingNetworkTemplateTest.java
+++ b/tests/vcn/java/android/net/vcn/VcnWifiUnderlyingNetworkTemplateTest.java
@@ -22,22 +22,15 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
-import android.os.Build;
-
import androidx.test.filters.SmallTest;
-
-import com.android.testutils.DevSdkIgnoreRule;
-import com.android.testutils.DevSdkIgnoreRunner;
+import androidx.test.runner.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.util.Set;
-// TODO: b/374174952 After B finalization, use Sdk36ModuleController to ensure VCN tests only run on
-// Android B/B+
-@RunWith(DevSdkIgnoreRunner.class)
-@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.VANILLA_ICE_CREAM)
+@RunWith(AndroidJUnit4.class)
@SmallTest
public class VcnWifiUnderlyingNetworkTemplateTest extends VcnUnderlyingNetworkTemplateTestBase {
private static final String SSID = "TestWifi";
diff --git a/tests/vcn/java/android/net/vcn/persistablebundleutils/EapSessionConfigUtilsTest.java b/tests/vcn/java/android/net/vcn/persistablebundleutils/EapSessionConfigUtilsTest.java
index 01e9ac2ac3cf..bc8e9d3200b6 100644
--- a/tests/vcn/java/android/net/vcn/persistablebundleutils/EapSessionConfigUtilsTest.java
+++ b/tests/vcn/java/android/net/vcn/persistablebundleutils/EapSessionConfigUtilsTest.java
@@ -21,14 +21,11 @@ import static android.telephony.TelephonyManager.APPTYPE_USIM;
import static org.junit.Assert.assertEquals;
import android.net.eap.EapSessionConfig;
-import android.os.Build;
import android.os.PersistableBundle;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
-
-import com.android.testutils.DevSdkIgnoreRule;
-import com.android.testutils.DevSdkIgnoreRunner;
+import androidx.test.runner.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -38,10 +35,7 @@ import java.nio.charset.StandardCharsets;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
-// TODO: b/374174952 After B finalization, use Sdk36ModuleController to ensure VCN tests only run on
-// Android B/B+
-@RunWith(DevSdkIgnoreRunner.class)
-@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.VANILLA_ICE_CREAM)
+@RunWith(AndroidJUnit4.class)
@SmallTest
public class EapSessionConfigUtilsTest {
private static final byte[] EAP_ID = "test@android.net".getBytes(StandardCharsets.US_ASCII);
diff --git a/tests/vcn/java/android/net/vcn/persistablebundleutils/IkeIdentificationUtilsTest.java b/tests/vcn/java/android/net/vcn/persistablebundleutils/IkeIdentificationUtilsTest.java
index 821e5a6c94cb..4f3930f9b5af 100644
--- a/tests/vcn/java/android/net/vcn/persistablebundleutils/IkeIdentificationUtilsTest.java
+++ b/tests/vcn/java/android/net/vcn/persistablebundleutils/IkeIdentificationUtilsTest.java
@@ -25,13 +25,10 @@ import android.net.ipsec.ike.IkeIpv4AddrIdentification;
import android.net.ipsec.ike.IkeIpv6AddrIdentification;
import android.net.ipsec.ike.IkeKeyIdIdentification;
import android.net.ipsec.ike.IkeRfc822AddrIdentification;
-import android.os.Build;
import android.os.PersistableBundle;
import androidx.test.filters.SmallTest;
-
-import com.android.testutils.DevSdkIgnoreRule;
-import com.android.testutils.DevSdkIgnoreRunner;
+import androidx.test.runner.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -42,10 +39,7 @@ import java.net.InetAddress;
import javax.security.auth.x500.X500Principal;
-// TODO: b/374174952 After B finalization, use Sdk36ModuleController to ensure VCN tests only run on
-// Android B/B+
-@RunWith(DevSdkIgnoreRunner.class)
-@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.VANILLA_ICE_CREAM)
+@RunWith(AndroidJUnit4.class)
@SmallTest
public class IkeIdentificationUtilsTest {
private static void verifyPersistableBundleEncodeDecodeIsLossless(IkeIdentification id) {
diff --git a/tests/vcn/java/android/net/vcn/persistablebundleutils/IkeSessionParamsUtilsTest.java b/tests/vcn/java/android/net/vcn/persistablebundleutils/IkeSessionParamsUtilsTest.java
index 7200aee1c012..9f7d2390938f 100644
--- a/tests/vcn/java/android/net/vcn/persistablebundleutils/IkeSessionParamsUtilsTest.java
+++ b/tests/vcn/java/android/net/vcn/persistablebundleutils/IkeSessionParamsUtilsTest.java
@@ -29,16 +29,14 @@ import android.net.InetAddresses;
import android.net.eap.EapSessionConfig;
import android.net.ipsec.ike.IkeFqdnIdentification;
import android.net.ipsec.ike.IkeSessionParams;
-import android.os.Build;
import android.os.PersistableBundle;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
import com.android.internal.org.bouncycastle.util.io.pem.PemObject;
import com.android.internal.org.bouncycastle.util.io.pem.PemReader;
-import com.android.testutils.DevSdkIgnoreRule;
-import com.android.testutils.DevSdkIgnoreRunner;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -54,10 +52,7 @@ import java.security.cert.X509Certificate;
import java.security.interfaces.RSAPrivateKey;
import java.util.concurrent.TimeUnit;
-// TODO: b/374174952 After B finalization, use Sdk36ModuleController to ensure VCN tests only run on
-// Android B/B+
-@RunWith(DevSdkIgnoreRunner.class)
-@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.VANILLA_ICE_CREAM)
+@RunWith(AndroidJUnit4.class)
@SmallTest
public class IkeSessionParamsUtilsTest {
// Public for use in VcnGatewayConnectionConfigTest, EncryptedTunnelParamsUtilsTest
diff --git a/tests/vcn/java/android/net/vcn/persistablebundleutils/IkeTrafficSelectorUtilsTest.java b/tests/vcn/java/android/net/vcn/persistablebundleutils/IkeTrafficSelectorUtilsTest.java
index 957e785d70c0..28cf38a2a583 100644
--- a/tests/vcn/java/android/net/vcn/persistablebundleutils/IkeTrafficSelectorUtilsTest.java
+++ b/tests/vcn/java/android/net/vcn/persistablebundleutils/IkeTrafficSelectorUtilsTest.java
@@ -20,23 +20,17 @@ import static org.junit.Assert.assertEquals;
import android.net.InetAddresses;
import android.net.ipsec.ike.IkeTrafficSelector;
-import android.os.Build;
import android.os.PersistableBundle;
import androidx.test.filters.SmallTest;
-
-import com.android.testutils.DevSdkIgnoreRule;
-import com.android.testutils.DevSdkIgnoreRunner;
+import androidx.test.runner.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.net.InetAddress;
-// TODO: b/374174952 After B finalization, use Sdk36ModuleController to ensure VCN tests only run on
-// Android B/B+
-@RunWith(DevSdkIgnoreRunner.class)
-@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.VANILLA_ICE_CREAM)
+@RunWith(AndroidJUnit4.class)
@SmallTest
public class IkeTrafficSelectorUtilsTest {
private static final int START_PORT = 16;
diff --git a/tests/vcn/java/android/net/vcn/persistablebundleutils/SaProposalUtilsTest.java b/tests/vcn/java/android/net/vcn/persistablebundleutils/SaProposalUtilsTest.java
index 1e8f5ff2dc07..664044a9e7d4 100644
--- a/tests/vcn/java/android/net/vcn/persistablebundleutils/SaProposalUtilsTest.java
+++ b/tests/vcn/java/android/net/vcn/persistablebundleutils/SaProposalUtilsTest.java
@@ -21,21 +21,15 @@ import static org.junit.Assert.assertEquals;
import android.net.ipsec.ike.ChildSaProposal;
import android.net.ipsec.ike.IkeSaProposal;
import android.net.ipsec.ike.SaProposal;
-import android.os.Build;
import android.os.PersistableBundle;
import androidx.test.filters.SmallTest;
-
-import com.android.testutils.DevSdkIgnoreRule;
-import com.android.testutils.DevSdkIgnoreRunner;
+import androidx.test.runner.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
-// TODO: b/374174952 After B finalization, use Sdk36ModuleController to ensure VCN tests only run on
-// Android B/B+
-@RunWith(DevSdkIgnoreRunner.class)
-@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.VANILLA_ICE_CREAM)
+@RunWith(AndroidJUnit4.class)
@SmallTest
public class SaProposalUtilsTest {
/** Package private so that IkeSessionParamsUtilsTest can use it */
diff --git a/tests/vcn/java/android/net/vcn/persistablebundleutils/TunnelConnectionParamsUtilsTest.java b/tests/vcn/java/android/net/vcn/persistablebundleutils/TunnelConnectionParamsUtilsTest.java
index 7d17724112ec..f9dc9eb4d5ae 100644
--- a/tests/vcn/java/android/net/vcn/persistablebundleutils/TunnelConnectionParamsUtilsTest.java
+++ b/tests/vcn/java/android/net/vcn/persistablebundleutils/TunnelConnectionParamsUtilsTest.java
@@ -20,20 +20,14 @@ import static org.junit.Assert.assertEquals;
import android.net.ipsec.ike.IkeSessionParams;
import android.net.ipsec.ike.IkeTunnelConnectionParams;
-import android.os.Build;
import androidx.test.filters.SmallTest;
-
-import com.android.testutils.DevSdkIgnoreRule;
-import com.android.testutils.DevSdkIgnoreRunner;
+import androidx.test.runner.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
-// TODO: b/374174952 After B finalization, use Sdk36ModuleController to ensure VCN tests only run on
-// Android B/B+
-@RunWith(DevSdkIgnoreRunner.class)
-@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.VANILLA_ICE_CREAM)
+@RunWith(AndroidJUnit4.class)
@SmallTest
public class TunnelConnectionParamsUtilsTest {
// Public for use in VcnGatewayConnectionConfigTest
diff --git a/tests/vcn/java/android/net/vcn/persistablebundleutils/TunnelModeChildSessionParamsUtilsTest.java b/tests/vcn/java/android/net/vcn/persistablebundleutils/TunnelModeChildSessionParamsUtilsTest.java
index 3d7348a79b8c..e0b5f0ef0381 100644
--- a/tests/vcn/java/android/net/vcn/persistablebundleutils/TunnelModeChildSessionParamsUtilsTest.java
+++ b/tests/vcn/java/android/net/vcn/persistablebundleutils/TunnelModeChildSessionParamsUtilsTest.java
@@ -25,13 +25,10 @@ import android.net.InetAddresses;
import android.net.ipsec.ike.ChildSaProposal;
import android.net.ipsec.ike.IkeTrafficSelector;
import android.net.ipsec.ike.TunnelModeChildSessionParams;
-import android.os.Build;
import android.os.PersistableBundle;
import androidx.test.filters.SmallTest;
-
-import com.android.testutils.DevSdkIgnoreRule;
-import com.android.testutils.DevSdkIgnoreRunner;
+import androidx.test.runner.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -40,10 +37,7 @@ import java.net.Inet4Address;
import java.net.Inet6Address;
import java.util.concurrent.TimeUnit;
-// TODO: b/374174952 After B finalization, use Sdk36ModuleController to ensure VCN tests only run on
-// Android B/B+
-@RunWith(DevSdkIgnoreRunner.class)
-@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.VANILLA_ICE_CREAM)
+@RunWith(AndroidJUnit4.class)
@SmallTest
public class TunnelModeChildSessionParamsUtilsTest {
// Package private for use in EncryptedTunnelParamsUtilsTest
diff --git a/tests/vcn/java/android/net/vcn/util/MtuUtilsTest.java b/tests/vcn/java/android/net/vcn/util/MtuUtilsTest.java
index 99c7aa72146b..47638b002f37 100644
--- a/tests/vcn/java/android/net/vcn/util/MtuUtilsTest.java
+++ b/tests/vcn/java/android/net/vcn/util/MtuUtilsTest.java
@@ -33,12 +33,9 @@ import static org.junit.Assert.assertTrue;
import static java.util.Collections.emptyList;
import android.net.ipsec.ike.ChildSaProposal;
-import android.os.Build;
import androidx.test.filters.SmallTest;
-
-import com.android.testutils.DevSdkIgnoreRule;
-import com.android.testutils.DevSdkIgnoreRunner;
+import androidx.test.runner.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -46,10 +43,7 @@ import org.junit.runner.RunWith;
import java.util.Arrays;
import java.util.List;
-// TODO: b/374174952 After B finalization, use Sdk36ModuleController to ensure VCN tests only run on
-// Android B/B+
-@RunWith(DevSdkIgnoreRunner.class)
-@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.VANILLA_ICE_CREAM)
+@RunWith(AndroidJUnit4.class)
@SmallTest
public class MtuUtilsTest {
private void verifyUnderlyingMtuZero(boolean isIpv4) {
diff --git a/tests/vcn/java/android/net/vcn/util/PersistableBundleUtilsTest.java b/tests/vcn/java/android/net/vcn/util/PersistableBundleUtilsTest.java
index f7786af840ee..c84e60086b37 100644
--- a/tests/vcn/java/android/net/vcn/util/PersistableBundleUtilsTest.java
+++ b/tests/vcn/java/android/net/vcn/util/PersistableBundleUtilsTest.java
@@ -21,13 +21,10 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
-import android.os.Build;
import android.os.PersistableBundle;
import androidx.test.filters.SmallTest;
-
-import com.android.testutils.DevSdkIgnoreRule;
-import com.android.testutils.DevSdkIgnoreRunner;
+import androidx.test.runner.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -38,10 +35,7 @@ import java.util.LinkedHashMap;
import java.util.List;
import java.util.Objects;
-// TODO: b/374174952 After B finalization, use Sdk36ModuleController to ensure VCN tests only run on
-// Android B/B+
-@RunWith(DevSdkIgnoreRunner.class)
-@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.VANILLA_ICE_CREAM)
+@RunWith(AndroidJUnit4.class)
@SmallTest
public class PersistableBundleUtilsTest {
private static final String TEST_KEY = "testKey";
diff --git a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
index 3cccbc419425..02400ead5146 100644
--- a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
+++ b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
@@ -95,6 +95,7 @@ import android.telephony.TelephonyManager;
import android.util.ArraySet;
import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
import com.android.server.VcnManagementService.VcnCallback;
import com.android.server.VcnManagementService.VcnStatusCallbackInfo;
@@ -102,8 +103,6 @@ import com.android.server.vcn.TelephonySubscriptionTracker;
import com.android.server.vcn.Vcn;
import com.android.server.vcn.VcnContext;
import com.android.server.vcn.VcnNetworkProvider;
-import com.android.testutils.DevSdkIgnoreRule;
-import com.android.testutils.DevSdkIgnoreRunner;
import org.junit.Before;
import org.junit.Rule;
@@ -120,10 +119,7 @@ import java.util.Map.Entry;
import java.util.Set;
import java.util.UUID;
-// TODO: b/374174952 After B finalization, use Sdk36ModuleController to ensure VCN tests only run on
-// Android B/B+
-@RunWith(DevSdkIgnoreRunner.class)
-@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.VANILLA_ICE_CREAM)
+@RunWith(AndroidJUnit4.class)
@SmallTest
public class VcnManagementServiceTest {
@Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
diff --git a/tests/vcn/java/com/android/server/vcn/TelephonySubscriptionTrackerTest.java b/tests/vcn/java/com/android/server/vcn/TelephonySubscriptionTrackerTest.java
index 6276be27fbf5..6a4a1bd3aba3 100644
--- a/tests/vcn/java/com/android/server/vcn/TelephonySubscriptionTrackerTest.java
+++ b/tests/vcn/java/com/android/server/vcn/TelephonySubscriptionTrackerTest.java
@@ -54,7 +54,6 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.net.vcn.VcnManager;
-import android.os.Build;
import android.os.Handler;
import android.os.ParcelUuid;
import android.os.PersistableBundle;
@@ -70,10 +69,9 @@ import android.util.ArrayMap;
import android.util.ArraySet;
import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
import com.android.modules.utils.HandlerExecutor;
-import com.android.testutils.DevSdkIgnoreRule;
-import com.android.testutils.DevSdkIgnoreRunner;
import org.junit.Before;
import org.junit.Test;
@@ -89,10 +87,7 @@ import java.util.Map;
import java.util.Set;
import java.util.UUID;
-// TODO: b/374174952 After B finalization, use Sdk36ModuleController to ensure VCN tests only run on
-// Android B/B+
-@RunWith(DevSdkIgnoreRunner.class)
-@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.VANILLA_ICE_CREAM)
+@RunWith(AndroidJUnit4.class)
@SmallTest
public class TelephonySubscriptionTrackerTest {
private static final String PACKAGE_NAME =
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java
index a34908051360..bf0c46c60f68 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java
@@ -70,18 +70,16 @@ import android.net.vcn.VcnGatewayConnectionConfigTest;
import android.net.vcn.VcnManager.VcnErrorCode;
import android.net.vcn.VcnTransportInfo;
import android.net.vcn.util.MtuUtils;
-import android.os.Build;
import android.os.PersistableBundle;
import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
import com.android.server.vcn.VcnGatewayConnection.VcnChildSessionCallback;
import com.android.server.vcn.VcnGatewayConnection.VcnChildSessionConfiguration;
import com.android.server.vcn.VcnGatewayConnection.VcnIkeSession;
import com.android.server.vcn.VcnGatewayConnection.VcnNetworkAgent;
import com.android.server.vcn.routeselection.UnderlyingNetworkRecord;
-import com.android.testutils.DevSdkIgnoreRule;
-import com.android.testutils.DevSdkIgnoreRunner;
import org.junit.Before;
import org.junit.Test;
@@ -96,10 +94,7 @@ import java.util.Collections;
import java.util.List;
import java.util.function.Consumer;
-// TODO: b/374174952 After B finalization, use Sdk36ModuleController to ensure VCN tests only run on
-// Android B/B+
-@RunWith(DevSdkIgnoreRunner.class)
-@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.VANILLA_ICE_CREAM)
+@RunWith(AndroidJUnit4.class)
@SmallTest
public class VcnGatewayConnectionConnectedStateTest extends VcnGatewayConnectionTestBase {
private static final int PARALLEL_SA_COUNT = 4;
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectingStateTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectingStateTest.java
index e1a572e7a481..c946680db73f 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectingStateTest.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectingStateTest.java
@@ -26,22 +26,16 @@ import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import android.net.ipsec.ike.IkeSessionParams;
-import android.os.Build;
import androidx.test.filters.SmallTest;
-
-import com.android.testutils.DevSdkIgnoreRule;
-import com.android.testutils.DevSdkIgnoreRunner;
+import androidx.test.runner.AndroidJUnit4;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
-// TODO: b/374174952 After B finalization, use Sdk36ModuleController to ensure VCN tests only run on
-// Android B/B+
-@RunWith(DevSdkIgnoreRunner.class)
-@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.VANILLA_ICE_CREAM)
+@RunWith(AndroidJUnit4.class)
@SmallTest
public class VcnGatewayConnectionConnectingStateTest extends VcnGatewayConnectionTestBase {
private VcnIkeSession mIkeSession;
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectedStateTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectedStateTest.java
index 7cfaf5be5111..0b470b9a35d0 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectedStateTest.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectedStateTest.java
@@ -30,21 +30,15 @@ import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import android.net.IpSecManager;
-import android.os.Build;
import androidx.test.filters.SmallTest;
-
-import com.android.testutils.DevSdkIgnoreRule;
-import com.android.testutils.DevSdkIgnoreRunner;
+import androidx.test.runner.AndroidJUnit4;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
-// TODO: b/374174952 After B finalization, use Sdk36ModuleController to ensure VCN tests only run on
-// Android B/B+
-@RunWith(DevSdkIgnoreRunner.class)
-@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.VANILLA_ICE_CREAM)
+@RunWith(AndroidJUnit4.class)
@SmallTest
public class VcnGatewayConnectionDisconnectedStateTest extends VcnGatewayConnectionTestBase {
@Before
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectingStateTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectingStateTest.java
index 9132d830c54e..bfcc4ca48313 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectingStateTest.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectingStateTest.java
@@ -23,21 +23,14 @@ import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
-import android.os.Build;
-
import androidx.test.filters.SmallTest;
-
-import com.android.testutils.DevSdkIgnoreRule;
-import com.android.testutils.DevSdkIgnoreRunner;
+import androidx.test.runner.AndroidJUnit4;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
-// TODO: b/374174952 After B finalization, use Sdk36ModuleController to ensure VCN tests only run on
-// Android B/B+
-@RunWith(DevSdkIgnoreRunner.class)
-@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.VANILLA_ICE_CREAM)
+@RunWith(AndroidJUnit4.class)
@SmallTest
public class VcnGatewayConnectionDisconnectingStateTest extends VcnGatewayConnectionTestBase {
@Before
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionRetryTimeoutStateTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionRetryTimeoutStateTest.java
index d5ef4e028709..e6fe509d1665 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionRetryTimeoutStateTest.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionRetryTimeoutStateTest.java
@@ -27,21 +27,14 @@ import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
-import android.os.Build;
-
import androidx.test.filters.SmallTest;
-
-import com.android.testutils.DevSdkIgnoreRule;
-import com.android.testutils.DevSdkIgnoreRunner;
+import androidx.test.runner.AndroidJUnit4;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
-// TODO: b/374174952 After B finalization, use Sdk36ModuleController to ensure VCN tests only run on
-// Android B/B+
-@RunWith(DevSdkIgnoreRunner.class)
-@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.VANILLA_ICE_CREAM)
+@RunWith(AndroidJUnit4.class)
@SmallTest
public class VcnGatewayConnectionRetryTimeoutStateTest extends VcnGatewayConnectionTestBase {
private long mFirstRetryInterval;
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTest.java
index 5283322682ee..2a2d5ba2ef4d 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTest.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTest.java
@@ -61,17 +61,15 @@ import android.net.vcn.VcnGatewayConnectionConfigTest;
import android.net.vcn.VcnManager;
import android.net.vcn.VcnTransportInfo;
import android.net.wifi.WifiInfo;
-import android.os.Build;
import android.os.ParcelUuid;
import android.os.Process;
import android.telephony.SubscriptionInfo;
import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot;
import com.android.server.vcn.routeselection.UnderlyingNetworkRecord;
-import com.android.testutils.DevSdkIgnoreRule;
-import com.android.testutils.DevSdkIgnoreRunner;
import org.junit.Before;
import org.junit.Test;
@@ -89,10 +87,7 @@ import java.util.UUID;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
-// TODO: b/374174952 After B finalization, use Sdk36ModuleController to ensure VCN tests only run on
-// Android B/B+
-@RunWith(DevSdkIgnoreRunner.class)
-@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.VANILLA_ICE_CREAM)
+@RunWith(AndroidJUnit4.class)
@SmallTest
public class VcnGatewayConnectionTest extends VcnGatewayConnectionTestBase {
private static final int TEST_UID = Process.myUid() + 1;
diff --git a/tests/vcn/java/com/android/server/vcn/VcnNetworkProviderTest.java b/tests/vcn/java/com/android/server/vcn/VcnNetworkProviderTest.java
index 0185931d628f..4f705d62f155 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnNetworkProviderTest.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnNetworkProviderTest.java
@@ -29,14 +29,12 @@ import android.annotation.NonNull;
import android.content.Context;
import android.net.ConnectivityManager;
import android.net.NetworkRequest;
-import android.os.Build;
import android.os.test.TestLooper;
import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
import com.android.server.vcn.VcnNetworkProvider.NetworkRequestListener;
-import com.android.testutils.DevSdkIgnoreRule;
-import com.android.testutils.DevSdkIgnoreRunner;
import org.junit.Before;
import org.junit.Test;
@@ -46,10 +44,7 @@ import org.mockito.ArgumentCaptor;
import java.util.ArrayList;
import java.util.List;
-// TODO: b/374174952 After B finalization, use Sdk36ModuleController to ensure VCN tests only run on
-// Android B/B+
-@RunWith(DevSdkIgnoreRunner.class)
-@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.VANILLA_ICE_CREAM)
+@RunWith(AndroidJUnit4.class)
@SmallTest
public class VcnNetworkProviderTest {
private static final int TEST_SCORE_UNSATISFIED = 0;
diff --git a/tests/vcn/java/com/android/server/vcn/VcnTest.java b/tests/vcn/java/com/android/server/vcn/VcnTest.java
index c12adcbab08a..1853b6b59f5d 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnTest.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnTest.java
@@ -49,7 +49,6 @@ import android.net.Uri;
import android.net.vcn.VcnConfig;
import android.net.vcn.VcnGatewayConnectionConfig;
import android.net.vcn.VcnGatewayConnectionConfigTest;
-import android.os.Build;
import android.os.ParcelUuid;
import android.os.test.TestLooper;
import android.provider.Settings;
@@ -57,14 +56,13 @@ import android.telephony.TelephonyManager;
import android.util.ArraySet;
import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
import com.android.server.VcnManagementService.VcnCallback;
import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot;
import com.android.server.vcn.Vcn.VcnGatewayStatusCallback;
import com.android.server.vcn.Vcn.VcnUserMobileDataStateListener;
import com.android.server.vcn.VcnNetworkProvider.NetworkRequestListener;
-import com.android.testutils.DevSdkIgnoreRule;
-import com.android.testutils.DevSdkIgnoreRunner;
import org.junit.Before;
import org.junit.Test;
@@ -79,10 +77,7 @@ import java.util.Map.Entry;
import java.util.Set;
import java.util.UUID;
-// TODO: b/374174952 After B finalization, use Sdk36ModuleController to ensure VCN tests only run on
-// Android B/B+
-@RunWith(DevSdkIgnoreRunner.class)
-@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.VANILLA_ICE_CREAM)
+@RunWith(AndroidJUnit4.class)
@SmallTest
public class VcnTest {
private static final String PKG_NAME = VcnTest.class.getPackage().getName();
diff --git a/tests/vcn/java/com/android/server/vcn/routeselection/IpSecPacketLossDetectorTest.java b/tests/vcn/java/com/android/server/vcn/routeselection/IpSecPacketLossDetectorTest.java
index 53a36d3e4d6a..224b45ced965 100644
--- a/tests/vcn/java/com/android/server/vcn/routeselection/IpSecPacketLossDetectorTest.java
+++ b/tests/vcn/java/com/android/server/vcn/routeselection/IpSecPacketLossDetectorTest.java
@@ -44,18 +44,16 @@ import static org.mockito.Mockito.when;
import android.content.BroadcastReceiver;
import android.content.Intent;
import android.net.IpSecTransformState;
-import android.os.Build;
import android.os.OutcomeReceiver;
import android.os.PowerManager;
import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
import com.android.server.vcn.routeselection.IpSecPacketLossDetector.PacketLossCalculationResult;
import com.android.server.vcn.routeselection.IpSecPacketLossDetector.PacketLossCalculator;
import com.android.server.vcn.routeselection.NetworkMetricMonitor.IpSecTransformWrapper;
import com.android.server.vcn.routeselection.NetworkMetricMonitor.NetworkMetricMonitorCallback;
-import com.android.testutils.DevSdkIgnoreRule;
-import com.android.testutils.DevSdkIgnoreRunner;
import org.junit.Before;
import org.junit.Test;
@@ -69,10 +67,7 @@ import java.util.Arrays;
import java.util.BitSet;
import java.util.concurrent.TimeUnit;
-// TODO: b/374174952 After B finalization, use Sdk36ModuleController to ensure VCN tests only run on
-// Android B/B+
-@RunWith(DevSdkIgnoreRunner.class)
-@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.VANILLA_ICE_CREAM)
+@RunWith(AndroidJUnit4.class)
@SmallTest
public class IpSecPacketLossDetectorTest extends NetworkEvaluationTestBase {
private static final String TAG = IpSecPacketLossDetectorTest.class.getSimpleName();
diff --git a/tests/vcn/java/com/android/server/vcn/routeselection/NetworkPriorityClassifierTest.java b/tests/vcn/java/com/android/server/vcn/routeselection/NetworkPriorityClassifierTest.java
index a9c637f7c943..16dbab9d7a61 100644
--- a/tests/vcn/java/com/android/server/vcn/routeselection/NetworkPriorityClassifierTest.java
+++ b/tests/vcn/java/com/android/server/vcn/routeselection/NetworkPriorityClassifierTest.java
@@ -42,14 +42,11 @@ import android.net.vcn.VcnGatewayConnectionConfig;
import android.net.vcn.VcnManager;
import android.net.vcn.VcnUnderlyingNetworkTemplate;
import android.net.vcn.VcnWifiUnderlyingNetworkTemplate;
-import android.os.Build;
import android.os.PersistableBundle;
import android.util.ArraySet;
import androidx.test.filters.SmallTest;
-
-import com.android.testutils.DevSdkIgnoreRule;
-import com.android.testutils.DevSdkIgnoreRunner;
+import androidx.test.runner.AndroidJUnit4;
import org.junit.Before;
import org.junit.Test;
@@ -59,10 +56,7 @@ import java.util.Collections;
import java.util.List;
import java.util.Set;
-// TODO: b/374174952 After B finalization, use Sdk36ModuleController to ensure VCN tests only run on
-// Android B/B+
-@RunWith(DevSdkIgnoreRunner.class)
-@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.VANILLA_ICE_CREAM)
+@RunWith(AndroidJUnit4.class)
@SmallTest
public class NetworkPriorityClassifierTest extends NetworkEvaluationTestBase {
private UnderlyingNetworkRecord mWifiNetworkRecord;
diff --git a/tests/vcn/java/com/android/server/vcn/routeselection/UnderlyingNetworkControllerTest.java b/tests/vcn/java/com/android/server/vcn/routeselection/UnderlyingNetworkControllerTest.java
index 99c508c139ec..b67a2fd21b40 100644
--- a/tests/vcn/java/com/android/server/vcn/routeselection/UnderlyingNetworkControllerTest.java
+++ b/tests/vcn/java/com/android/server/vcn/routeselection/UnderlyingNetworkControllerTest.java
@@ -58,7 +58,6 @@ import android.net.vcn.VcnCellUnderlyingNetworkTemplate;
import android.net.vcn.VcnCellUnderlyingNetworkTemplateTest;
import android.net.vcn.VcnGatewayConnectionConfigTest;
import android.net.vcn.VcnUnderlyingNetworkTemplate;
-import android.os.Build;
import android.os.ParcelUuid;
import android.os.test.TestLooper;
import android.telephony.CarrierConfigManager;
@@ -67,6 +66,7 @@ import android.telephony.TelephonyManager;
import android.util.ArraySet;
import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot;
import com.android.server.vcn.VcnContext;
@@ -76,8 +76,6 @@ import com.android.server.vcn.routeselection.UnderlyingNetworkController.Network
import com.android.server.vcn.routeselection.UnderlyingNetworkController.UnderlyingNetworkControllerCallback;
import com.android.server.vcn.routeselection.UnderlyingNetworkController.UnderlyingNetworkListener;
import com.android.server.vcn.routeselection.UnderlyingNetworkEvaluator.NetworkEvaluatorCallback;
-import com.android.testutils.DevSdkIgnoreRule;
-import com.android.testutils.DevSdkIgnoreRunner;
import org.junit.Before;
import org.junit.Test;
@@ -95,10 +93,7 @@ import java.util.List;
import java.util.Set;
import java.util.UUID;
-// TODO: b/374174952 After B finalization, use Sdk36ModuleController to ensure VCN tests only run on
-// Android B/B+
-@RunWith(DevSdkIgnoreRunner.class)
-@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.VANILLA_ICE_CREAM)
+@RunWith(AndroidJUnit4.class)
@SmallTest
public class UnderlyingNetworkControllerTest {
private static final ParcelUuid SUB_GROUP = new ParcelUuid(new UUID(0, 0));
diff --git a/tests/vcn/java/com/android/server/vcn/routeselection/UnderlyingNetworkEvaluatorTest.java b/tests/vcn/java/com/android/server/vcn/routeselection/UnderlyingNetworkEvaluatorTest.java
index 3ca84cf05cd1..850f9aa963d0 100644
--- a/tests/vcn/java/com/android/server/vcn/routeselection/UnderlyingNetworkEvaluatorTest.java
+++ b/tests/vcn/java/com/android/server/vcn/routeselection/UnderlyingNetworkEvaluatorTest.java
@@ -37,15 +37,13 @@ import static org.mockito.Mockito.when;
import android.net.IpSecTransform;
import android.net.vcn.VcnGatewayConnectionConfig;
-import android.os.Build;
import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
import com.android.server.vcn.routeselection.NetworkMetricMonitor.NetworkMetricMonitorCallback;
import com.android.server.vcn.routeselection.UnderlyingNetworkEvaluator.Dependencies;
import com.android.server.vcn.routeselection.UnderlyingNetworkEvaluator.NetworkEvaluatorCallback;
-import com.android.testutils.DevSdkIgnoreRule;
-import com.android.testutils.DevSdkIgnoreRunner;
import org.junit.Before;
import org.junit.Test;
@@ -56,10 +54,7 @@ import org.mockito.Mock;
import java.util.concurrent.TimeUnit;
-// TODO: b/374174952 After B finalization, use Sdk36ModuleController to ensure VCN tests only run on
-// Android B/B+
-@RunWith(DevSdkIgnoreRunner.class)
-@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.VANILLA_ICE_CREAM)
+@RunWith(AndroidJUnit4.class)
@SmallTest
public class UnderlyingNetworkEvaluatorTest extends NetworkEvaluationTestBase {
private static final int PENALTY_TIMEOUT_MIN = 10;
diff --git a/tools/aapt2/ResourcesInternal.proto b/tools/aapt2/ResourcesInternal.proto
index f4735a2f6ce7..380c5f21103c 100644
--- a/tools/aapt2/ResourcesInternal.proto
+++ b/tools/aapt2/ResourcesInternal.proto
@@ -50,8 +50,11 @@ message CompiledFile {
// Any symbols this file auto-generates/exports (eg. @+id/foo in an XML file).
repeated Symbol exported_symbol = 5;
- // The status of the flag the file is behind if any
+ // The status of the read only flag the file is behind if any
uint32 flag_status = 6;
bool flag_negated = 7;
string flag_name = 8;
+
+ // Whether the file uses read/write feature flags
+ bool uses_readwrite_feature_flags = 9;
}
diff --git a/tools/aapt2/cmd/Compile.cpp b/tools/aapt2/cmd/Compile.cpp
index a5e18d35a256..3b4f5429f254 100644
--- a/tools/aapt2/cmd/Compile.cpp
+++ b/tools/aapt2/cmd/Compile.cpp
@@ -407,6 +407,45 @@ static bool IsValidFile(IAaptContext* context, const std::string& input_path) {
return true;
}
+class FindReadWriteFlagsVisitor : public xml::Visitor {
+ public:
+ FindReadWriteFlagsVisitor(const FeatureFlagValues& feature_flag_values)
+ : feature_flag_values_(feature_flag_values) {
+ }
+
+ void Visit(xml::Element* node) override {
+ if (had_flags_) {
+ return;
+ }
+ auto* attr = node->FindAttribute(xml::kSchemaAndroid, xml::kAttrFeatureFlag);
+ if (attr != nullptr) {
+ std::string_view flag_name = util::TrimWhitespace(attr->value);
+ if (flag_name.starts_with('!')) {
+ flag_name = flag_name.substr(1);
+ }
+ if (auto it = feature_flag_values_.find(flag_name); it != feature_flag_values_.end()) {
+ if (!it->second.read_only) {
+ had_flags_ = true;
+ return;
+ }
+ } else {
+ // Flag not passed to aapt2, must evaluate at runtime
+ had_flags_ = true;
+ return;
+ }
+ }
+ VisitChildren(node);
+ }
+
+ bool HadFlags() const {
+ return had_flags_;
+ }
+
+ private:
+ bool had_flags_ = false;
+ const FeatureFlagValues& feature_flag_values_;
+};
+
static bool CompileXml(IAaptContext* context, const CompileOptions& options,
const ResourcePathData& path_data, io::IFile* file, IArchiveWriter* writer,
const std::string& output_path) {
@@ -436,6 +475,10 @@ static bool CompileXml(IAaptContext* context, const CompileOptions& options,
xmlres->file.type = ResourceFile::Type::kProtoXml;
xmlres->file.flag = ParseFlag(path_data.flag_name);
+ FindReadWriteFlagsVisitor visitor(options.feature_flag_values);
+ xmlres->root->Accept(&visitor);
+ xmlres->file.uses_readwrite_feature_flags = visitor.HadFlags();
+
if (xmlres->file.flag) {
std::string error;
auto flag_status = GetFlagStatus(xmlres->file.flag, options.feature_flag_values, &error);
diff --git a/tools/aapt2/cmd/Compile.h b/tools/aapt2/cmd/Compile.h
index e244546476b0..88d61b534209 100644
--- a/tools/aapt2/cmd/Compile.h
+++ b/tools/aapt2/cmd/Compile.h
@@ -75,7 +75,7 @@ class CompileCommand : public Command {
AddOptionalSwitch("--preserve-visibility-of-styleables",
"If specified, apply the same visibility rules for\n"
"styleables as are used for all other resources.\n"
- "Otherwise, all stylesables will be made public.",
+ "Otherwise, all styleables will be made public.",
&options_.preserve_visibility_of_styleables);
AddOptionalFlag("--visibility",
"Sets the visibility of the compiled resources to the specified\n"
diff --git a/tools/aapt2/cmd/Convert.h b/tools/aapt2/cmd/Convert.h
index 9452e588953e..5576ec0b882f 100644
--- a/tools/aapt2/cmd/Convert.h
+++ b/tools/aapt2/cmd/Convert.h
@@ -38,14 +38,14 @@ class ConvertCommand : public Command {
"--enable-sparse-encoding",
"Enables encoding sparse entries using a binary search tree.\n"
"This decreases APK size at the cost of resource retrieval performance.\n"
- "Only applies sparse encoding to Android O+ resources or all resources if minSdk of "
- "the APK is O+",
+ "Only applies sparse encoding if minSdk of the APK is >= 32",
&enable_sparse_encoding_);
- AddOptionalSwitch("--force-sparse-encoding",
- "Enables encoding sparse entries using a binary search tree.\n"
- "This decreases APK size at the cost of resource retrieval performance.\n"
- "Applies sparse encoding to all resources regardless of minSdk.",
- &force_sparse_encoding_);
+ AddOptionalSwitch(
+ "--force-sparse-encoding",
+ "Enables encoding sparse entries using a binary search tree.\n"
+ "This decreases APK size at the cost of resource retrieval performance.\n"
+ "Only applies sparse encoding if minSdk of the APK is >= 32 or is not set",
+ &force_sparse_encoding_);
AddOptionalSwitch(
"--enable-compact-entries",
"This decreases APK size by using compact resource entries for simple data types.",
diff --git a/tools/aapt2/cmd/Link.cpp b/tools/aapt2/cmd/Link.cpp
index 755dbb6f8e42..0e18ee250993 100644
--- a/tools/aapt2/cmd/Link.cpp
+++ b/tools/aapt2/cmd/Link.cpp
@@ -615,6 +615,8 @@ bool ResourceFileFlattener::Flatten(ResourceTable* table, IArchiveWriter* archiv
file_op.xml_to_flatten->file.source = file_ref->GetSource();
file_op.xml_to_flatten->file.name =
ResourceName(pkg->name, type->named_type, entry->name);
+ file_op.xml_to_flatten->file.uses_readwrite_feature_flags =
+ config_value->uses_readwrite_feature_flags;
}
// NOTE(adamlesinski): Explicitly construct a StringPiece here, or
@@ -647,6 +649,17 @@ bool ResourceFileFlattener::Flatten(ResourceTable* table, IArchiveWriter* archiv
}
}
+ FeatureFlagsFilterOptions flags_filter_options;
+ // Don't fail on unrecognized flags or flags without values as these flags might be
+ // defined and have a value by the time they are evaluated at runtime.
+ flags_filter_options.fail_on_unrecognized_flags = false;
+ flags_filter_options.flags_must_have_value = false;
+ flags_filter_options.remove_disabled_elements = true;
+ FeatureFlagsFilter flags_filter(options_.feature_flag_values, flags_filter_options);
+ if (!flags_filter.Consume(context_, file_op.xml_to_flatten.get())) {
+ return 1;
+ }
+
std::vector<std::unique_ptr<xml::XmlResource>> versioned_docs =
LinkAndVersionXmlFile(table, &file_op);
if (versioned_docs.empty()) {
@@ -673,6 +686,7 @@ bool ResourceFileFlattener::Flatten(ResourceTable* table, IArchiveWriter* archiv
// Update the output format of this XML file.
file_ref->type = XmlFileTypeForOutputFormat(options_.output_format);
+
bool result = table->AddResource(
NewResourceBuilder(file.name)
.SetValue(std::move(file_ref), file.config)
@@ -685,14 +699,6 @@ bool ResourceFileFlattener::Flatten(ResourceTable* table, IArchiveWriter* archiv
}
}
- FeatureFlagsFilterOptions flags_filter_options;
- flags_filter_options.fail_on_unrecognized_flags = false;
- flags_filter_options.flags_must_have_value = false;
- FeatureFlagsFilter flags_filter(options_.feature_flag_values, flags_filter_options);
- if (!flags_filter.Consume(context_, doc.get())) {
- return 1;
- }
-
error |= !FlattenXml(context_, *doc, dst_path, options_.keep_raw_values,
false /*utf16*/, options_.output_format, archive_writer);
}
diff --git a/tools/aapt2/cmd/Link.h b/tools/aapt2/cmd/Link.h
index 977978834fcd..54a8c8625eb5 100644
--- a/tools/aapt2/cmd/Link.h
+++ b/tools/aapt2/cmd/Link.h
@@ -164,9 +164,12 @@ class LinkCommand : public Command {
AddOptionalSwitch("--no-resource-removal", "Disables automatic removal of resources without\n"
"defaults. Use this only when building runtime resource overlay packages.",
&options_.no_resource_removal);
- AddOptionalSwitch("--enable-sparse-encoding",
- "This decreases APK size at the cost of resource retrieval performance.",
- &options_.use_sparse_encoding);
+ AddOptionalSwitch(
+ "--enable-sparse-encoding",
+ "Enables encoding sparse entries using a binary search tree.\n"
+ "This decreases APK size at the cost of resource retrieval performance.\n"
+ "Only applies sparse encoding if minSdk of the APK is >= 32",
+ &options_.use_sparse_encoding);
AddOptionalSwitch("--enable-compact-entries",
"This decreases APK size by using compact resource entries for simple data types.",
&options_.table_flattener_options.use_compact_entries);
diff --git a/tools/aapt2/cmd/Optimize.h b/tools/aapt2/cmd/Optimize.h
index 012b0f230ca2..a8f547e3d96c 100644
--- a/tools/aapt2/cmd/Optimize.h
+++ b/tools/aapt2/cmd/Optimize.h
@@ -108,14 +108,14 @@ class OptimizeCommand : public Command {
"--enable-sparse-encoding",
"Enables encoding sparse entries using a binary search tree.\n"
"This decreases APK size at the cost of resource retrieval performance.\n"
- "Only applies sparse encoding to Android O+ resources or all resources if minSdk of "
- "the APK is O+",
+ "Only applies sparse encoding if minSdk of the APK is >= 32",
&options_.enable_sparse_encoding);
- AddOptionalSwitch("--force-sparse-encoding",
- "Enables encoding sparse entries using a binary search tree.\n"
- "This decreases APK size at the cost of resource retrieval performance.\n"
- "Applies sparse encoding to all resources regardless of minSdk.",
- &options_.force_sparse_encoding);
+ AddOptionalSwitch(
+ "--force-sparse-encoding",
+ "Enables encoding sparse entries using a binary search tree.\n"
+ "This decreases APK size at the cost of resource retrieval performance.\n"
+ "Only applies sparse encoding if minSdk of the APK is >= 32 or is not set",
+ &options_.force_sparse_encoding);
AddOptionalSwitch(
"--enable-compact-entries",
"This decreases APK size by using compact resource entries for simple data types.",
diff --git a/tools/aapt2/format/binary/TableFlattener.cpp b/tools/aapt2/format/binary/TableFlattener.cpp
index 50144ae816b6..d19c9f2d5d75 100644
--- a/tools/aapt2/format/binary/TableFlattener.cpp
+++ b/tools/aapt2/format/binary/TableFlattener.cpp
@@ -197,13 +197,16 @@ class PackageFlattener {
bool sparse_encode = sparse_entries_ == SparseEntriesMode::Enabled ||
sparse_entries_ == SparseEntriesMode::Forced;
- if (sparse_entries_ == SparseEntriesMode::Forced ||
- (context_->GetMinSdkVersion() == 0 && config.sdkVersion == 0)) {
- // Sparse encode if forced or sdk version is not set in context and config.
- } else {
- // Otherwise, only sparse encode if the entries will be read on platforms S_V2+.
- sparse_encode = sparse_encode && (context_->GetMinSdkVersion() >= SDK_S_V2);
- }
+ // Only sparse encode if the entries will be read on platforms S_V2+. Sparse encoding
+ // is not supported on older platforms (b/197642721, b/197976367).
+ //
+ // We also allow sparse encoding for minSdk is 0 (not set) if sparse encoding is forced,
+ // in order to support Bundletool's usage of aapt2 where minSdk is not set in splits.
+ bool meets_min_sdk_requirement_for_sparse_encoding =
+ (context_->GetMinSdkVersion() >= SDK_S_V2) ||
+ (context_->GetMinSdkVersion() == 0 && sparse_entries_ == SparseEntriesMode::Forced);
+
+ sparse_encode = sparse_encode && meets_min_sdk_requirement_for_sparse_encoding;
// Only sparse encode if the offsets are representable in 2 bytes.
sparse_encode = sparse_encode && short_offsets;
diff --git a/tools/aapt2/format/binary/TableFlattener_test.cpp b/tools/aapt2/format/binary/TableFlattener_test.cpp
index 9156b96b67ec..0e8aae14a350 100644
--- a/tools/aapt2/format/binary/TableFlattener_test.cpp
+++ b/tools/aapt2/format/binary/TableFlattener_test.cpp
@@ -15,6 +15,7 @@
*/
#include "format/binary/TableFlattener.h"
+#include <string>
#include "android-base/stringprintf.h"
#include "androidfw/TypeWrappers.h"
@@ -326,6 +327,28 @@ static std::unique_ptr<ResourceTable> BuildTableWithSparseEntries(
return table;
}
+static void CheckSparseEntries(IAaptContext* context, const ConfigDescription& sparse_config,
+ const std::string& sparse_contents) {
+ ResourceTable sparse_table;
+ BinaryResourceParser parser(context->GetDiagnostics(), &sparse_table, Source("test.arsc"),
+ sparse_contents.data(), sparse_contents.size());
+ ASSERT_TRUE(parser.Parse());
+
+ auto value = test::GetValueForConfig<BinaryPrimitive>(&sparse_table, "android:string/foo_0",
+ sparse_config);
+ ASSERT_THAT(value, NotNull());
+ EXPECT_EQ(0u, value->value.data);
+
+ ASSERT_THAT(test::GetValueForConfig<BinaryPrimitive>(&sparse_table, "android:string/foo_1",
+ sparse_config),
+ IsNull());
+
+ value = test::GetValueForConfig<BinaryPrimitive>(&sparse_table, "android:string/foo_4",
+ sparse_config);
+ ASSERT_THAT(value, NotNull());
+ EXPECT_EQ(4u, value->value.data);
+}
+
TEST_F(TableFlattenerTest, FlattenSparseEntryWithMinSdkSV2) {
std::unique_ptr<IAaptContext> context = test::ContextBuilder()
.SetCompilationPackage("android")
@@ -347,29 +370,56 @@ TEST_F(TableFlattenerTest, FlattenSparseEntryWithMinSdkSV2) {
EXPECT_GT(no_sparse_contents.size(), sparse_contents.size());
- // Attempt to parse the sparse contents.
+ CheckSparseEntries(context.get(), sparse_config, sparse_contents);
+}
- ResourceTable sparse_table;
- BinaryResourceParser parser(context->GetDiagnostics(), &sparse_table, Source("test.arsc"),
- sparse_contents.data(), sparse_contents.size());
- ASSERT_TRUE(parser.Parse());
+TEST_F(TableFlattenerTest, FlattenSparseEntryWithMinSdkSV2AndForced) {
+ std::unique_ptr<IAaptContext> context = test::ContextBuilder()
+ .SetCompilationPackage("android")
+ .SetPackageId(0x01)
+ .SetMinSdkVersion(SDK_S_V2)
+ .Build();
- auto value = test::GetValueForConfig<BinaryPrimitive>(&sparse_table, "android:string/foo_0",
- sparse_config);
- ASSERT_THAT(value, NotNull());
- EXPECT_EQ(0u, value->value.data);
+ const ConfigDescription sparse_config = test::ParseConfigOrDie("en-rGB");
+ auto table_in = BuildTableWithSparseEntries(context.get(), sparse_config, 0.25f);
- ASSERT_THAT(test::GetValueForConfig<BinaryPrimitive>(&sparse_table, "android:string/foo_1",
- sparse_config),
- IsNull());
+ TableFlattenerOptions options;
+ options.sparse_entries = SparseEntriesMode::Forced;
- value = test::GetValueForConfig<BinaryPrimitive>(&sparse_table, "android:string/foo_4",
- sparse_config);
- ASSERT_THAT(value, NotNull());
- EXPECT_EQ(4u, value->value.data);
+ std::string no_sparse_contents;
+ ASSERT_TRUE(Flatten(context.get(), {}, table_in.get(), &no_sparse_contents));
+
+ std::string sparse_contents;
+ ASSERT_TRUE(Flatten(context.get(), options, table_in.get(), &sparse_contents));
+
+ EXPECT_GT(no_sparse_contents.size(), sparse_contents.size());
+
+ CheckSparseEntries(context.get(), sparse_config, sparse_contents);
}
-TEST_F(TableFlattenerTest, FlattenSparseEntryWithConfigSdkVersionSV2) {
+TEST_F(TableFlattenerTest, FlattenSparseEntryWithMinSdkBeforeSV2) {
+ std::unique_ptr<IAaptContext> context = test::ContextBuilder()
+ .SetCompilationPackage("android")
+ .SetPackageId(0x01)
+ .SetMinSdkVersion(SDK_LOLLIPOP)
+ .Build();
+
+ const ConfigDescription sparse_config = test::ParseConfigOrDie("en-rGB");
+ auto table_in = BuildTableWithSparseEntries(context.get(), sparse_config, 0.25f);
+
+ TableFlattenerOptions options;
+ options.sparse_entries = SparseEntriesMode::Enabled;
+
+ std::string no_sparse_contents;
+ ASSERT_TRUE(Flatten(context.get(), {}, table_in.get(), &no_sparse_contents));
+
+ std::string sparse_contents;
+ ASSERT_TRUE(Flatten(context.get(), options, table_in.get(), &sparse_contents));
+
+ EXPECT_EQ(no_sparse_contents.size(), sparse_contents.size());
+}
+
+TEST_F(TableFlattenerTest, FlattenSparseEntryWithMinSdkBeforeSV2AndConfigSdkVersionSV2) {
std::unique_ptr<IAaptContext> context = test::ContextBuilder()
.SetCompilationPackage("android")
.SetPackageId(0x01)
@@ -391,7 +441,7 @@ TEST_F(TableFlattenerTest, FlattenSparseEntryWithConfigSdkVersionSV2) {
EXPECT_EQ(no_sparse_contents.size(), sparse_contents.size());
}
-TEST_F(TableFlattenerTest, FlattenSparseEntryRegardlessOfMinSdkWhenForced) {
+TEST_F(TableFlattenerTest, FlattenSparseEntryWithMinSdkBeforeSV2AndForced) {
std::unique_ptr<IAaptContext> context = test::ContextBuilder()
.SetCompilationPackage("android")
.SetPackageId(0x01)
@@ -410,7 +460,7 @@ TEST_F(TableFlattenerTest, FlattenSparseEntryRegardlessOfMinSdkWhenForced) {
std::string sparse_contents;
ASSERT_TRUE(Flatten(context.get(), options, table_in.get(), &sparse_contents));
- EXPECT_GT(no_sparse_contents.size(), sparse_contents.size());
+ EXPECT_EQ(no_sparse_contents.size(), sparse_contents.size());
}
TEST_F(TableFlattenerTest, FlattenSparseEntryWithSdkVersionNotSet) {
@@ -429,28 +479,28 @@ TEST_F(TableFlattenerTest, FlattenSparseEntryWithSdkVersionNotSet) {
std::string sparse_contents;
ASSERT_TRUE(Flatten(context.get(), options, table_in.get(), &sparse_contents));
- EXPECT_GT(no_sparse_contents.size(), sparse_contents.size());
+ EXPECT_EQ(no_sparse_contents.size(), sparse_contents.size());
+}
- // Attempt to parse the sparse contents.
+TEST_F(TableFlattenerTest, FlattenSparseEntryWithSdkVersionNotSetAndForced) {
+ std::unique_ptr<IAaptContext> context =
+ test::ContextBuilder().SetCompilationPackage("android").SetPackageId(0x01).Build();
- ResourceTable sparse_table;
- BinaryResourceParser parser(context->GetDiagnostics(), &sparse_table, Source("test.arsc"),
- sparse_contents.data(), sparse_contents.size());
- ASSERT_TRUE(parser.Parse());
+ const ConfigDescription sparse_config = test::ParseConfigOrDie("en-rGB");
+ auto table_in = BuildTableWithSparseEntries(context.get(), sparse_config, 0.25f);
- auto value = test::GetValueForConfig<BinaryPrimitive>(&sparse_table, "android:string/foo_0",
- sparse_config);
- ASSERT_THAT(value, NotNull());
- EXPECT_EQ(0u, value->value.data);
+ TableFlattenerOptions options;
+ options.sparse_entries = SparseEntriesMode::Forced;
- ASSERT_THAT(test::GetValueForConfig<BinaryPrimitive>(&sparse_table, "android:string/foo_1",
- sparse_config),
- IsNull());
+ std::string no_sparse_contents;
+ ASSERT_TRUE(Flatten(context.get(), {}, table_in.get(), &no_sparse_contents));
- value = test::GetValueForConfig<BinaryPrimitive>(&sparse_table, "android:string/foo_4",
- sparse_config);
- ASSERT_THAT(value, NotNull());
- EXPECT_EQ(4u, value->value.data);
+ std::string sparse_contents;
+ ASSERT_TRUE(Flatten(context.get(), options, table_in.get(), &sparse_contents));
+
+ EXPECT_GT(no_sparse_contents.size(), sparse_contents.size());
+
+ CheckSparseEntries(context.get(), sparse_config, sparse_contents);
}
TEST_F(TableFlattenerTest, DoNotUseSparseEntryForDenseConfig) {
diff --git a/tools/aapt2/format/proto/ProtoDeserialize.cpp b/tools/aapt2/format/proto/ProtoDeserialize.cpp
index 91ec3485ac3b..b8936553a193 100644
--- a/tools/aapt2/format/proto/ProtoDeserialize.cpp
+++ b/tools/aapt2/format/proto/ProtoDeserialize.cpp
@@ -640,6 +640,7 @@ bool DeserializeCompiledFileFromPb(const pb::internal::CompiledFile& pb_file,
out_file->name = name_ref.ToResourceName();
out_file->source.path = pb_file.source_path();
out_file->type = DeserializeFileReferenceTypeFromPb(pb_file.type());
+ out_file->uses_readwrite_feature_flags = pb_file.uses_readwrite_feature_flags();
out_file->flag_status = (FlagStatus)pb_file.flag_status();
if (!pb_file.flag_name().empty()) {
diff --git a/tools/aapt2/format/proto/ProtoSerialize.cpp b/tools/aapt2/format/proto/ProtoSerialize.cpp
index fcc77d5a9d6d..da99c4f5917c 100644
--- a/tools/aapt2/format/proto/ProtoSerialize.cpp
+++ b/tools/aapt2/format/proto/ProtoSerialize.cpp
@@ -767,6 +767,7 @@ void SerializeCompiledFileToPb(const ResourceFile& file, pb::internal::CompiledF
out_file->set_flag_negated(file.flag->negated);
out_file->set_flag_name(file.flag->name);
}
+ out_file->set_uses_readwrite_feature_flags(file.uses_readwrite_feature_flags);
for (const SourcedResourceName& exported : file.exported_symbols) {
pb::internal::CompiledFile_Symbol* pb_symbol = out_file->add_exported_symbol();
diff --git a/tools/aapt2/link/FlaggedResources_test.cpp b/tools/aapt2/link/FlaggedResources_test.cpp
index 47a71fe36e9f..4dcb8507fa45 100644
--- a/tools/aapt2/link/FlaggedResources_test.cpp
+++ b/tools/aapt2/link/FlaggedResources_test.cpp
@@ -226,9 +226,11 @@ TEST_F(FlaggedResourcesTest, ReadWriteFlagInXmlGetsFlagged) {
}
}
}
+
ASSERT_TRUE(found) << "No entry for layout1 at v36 with FLAG_USES_FEATURE_FLAGS bit set";
- // There should only be 1 entry that has the FLAG_USES_FEATURE_FLAGS bit of flags set to 1
- ASSERT_EQ(fields_flagged, 1);
+ // There should only be 2 entry that has the FLAG_USES_FEATURE_FLAGS bit of flags set to 1, the
+ // three versions of the layout file that has flags
+ ASSERT_EQ(fields_flagged, 3);
}
} // namespace aapt
diff --git a/tools/aapt2/link/FlaggedXmlVersioner.cpp b/tools/aapt2/link/FlaggedXmlVersioner.cpp
index 8a3337c446cb..626cae73bfa2 100644
--- a/tools/aapt2/link/FlaggedXmlVersioner.cpp
+++ b/tools/aapt2/link/FlaggedXmlVersioner.cpp
@@ -35,10 +35,6 @@ class AllDisabledFlagsVisitor : public xml::Visitor {
VisitChildren(node);
}
- bool HadFlags() const {
- return had_flags_;
- }
-
private:
bool FixupOrShouldRemove(const std::unique_ptr<xml::Node>& node) {
if (auto* el = NodeCast<Element>(node.get())) {
@@ -47,7 +43,6 @@ class AllDisabledFlagsVisitor : public xml::Visitor {
return false;
}
- had_flags_ = true;
// This class assumes all flags are disabled so we want to remove any elements behind flags
// unless the flag specification is negated. In the negated case we remove the featureFlag
// attribute because we have already determined whether we are keeping the element or not.
@@ -62,56 +57,27 @@ class AllDisabledFlagsVisitor : public xml::Visitor {
return false;
}
-
- bool had_flags_ = false;
-};
-
-// An xml visitor that goes through the a doc and determines if any elements are behind a flag.
-class FindFlagsVisitor : public xml::Visitor {
- public:
- void Visit(xml::Element* node) override {
- if (had_flags_) {
- return;
- }
- auto* attr = node->FindAttribute(xml::kSchemaAndroid, xml::kAttrFeatureFlag);
- if (attr != nullptr) {
- had_flags_ = true;
- return;
- }
- VisitChildren(node);
- }
-
- bool HadFlags() const {
- return had_flags_;
- }
-
- bool had_flags_ = false;
};
std::vector<std::unique_ptr<xml::XmlResource>> FlaggedXmlVersioner::Process(IAaptContext* context,
xml::XmlResource* doc) {
std::vector<std::unique_ptr<xml::XmlResource>> docs;
- if ((static_cast<ApiVersion>(doc->file.config.sdkVersion) >= SDK_BAKLAVA) ||
- (static_cast<ApiVersion>(context->GetMinSdkVersion()) >= SDK_BAKLAVA)) {
+ if (!doc->file.uses_readwrite_feature_flags) {
+ docs.push_back(doc->Clone());
+ } else if ((static_cast<ApiVersion>(doc->file.config.sdkVersion) >= SDK_BAKLAVA) ||
+ (static_cast<ApiVersion>(context->GetMinSdkVersion()) >= SDK_BAKLAVA)) {
// Support for read/write flags was added in baklava so if the doc will only get used on
// baklava or later we can just return the original doc.
docs.push_back(doc->Clone());
- FindFlagsVisitor visitor;
- doc->root->Accept(&visitor);
- docs.back()->file.uses_readwrite_feature_flags = visitor.HadFlags();
} else {
auto preBaklavaVersion = doc->Clone();
AllDisabledFlagsVisitor visitor;
preBaklavaVersion->root->Accept(&visitor);
- preBaklavaVersion->file.uses_readwrite_feature_flags = false;
docs.push_back(std::move(preBaklavaVersion));
- if (visitor.HadFlags()) {
- auto baklavaVersion = doc->Clone();
- baklavaVersion->file.config.sdkVersion = SDK_BAKLAVA;
- baklavaVersion->file.uses_readwrite_feature_flags = true;
- docs.push_back(std::move(baklavaVersion));
- }
+ auto baklavaVersion = doc->Clone();
+ baklavaVersion->file.config.sdkVersion = SDK_BAKLAVA;
+ docs.push_back(std::move(baklavaVersion));
}
return docs;
}
diff --git a/tools/aapt2/link/FlaggedXmlVersioner_test.cpp b/tools/aapt2/link/FlaggedXmlVersioner_test.cpp
index 0c1314f165cc..0dc464253385 100644
--- a/tools/aapt2/link/FlaggedXmlVersioner_test.cpp
+++ b/tools/aapt2/link/FlaggedXmlVersioner_test.cpp
@@ -101,6 +101,7 @@ TEST_F(FlaggedXmlVersionerTest, PreBaklavaGetsSplit) {
<TextView android:featureFlag="package.flag" /><TextView /><TextView />
</LinearLayout>)");
doc->file.config.sdkVersion = SDK_GINGERBREAD;
+ doc->file.uses_readwrite_feature_flags = true;
FlaggedXmlVersioner versioner;
auto results = versioner.Process(context_.get(), doc.get());
@@ -131,6 +132,7 @@ TEST_F(FlaggedXmlVersionerTest, NoVersionGetsSplit) {
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android">
<TextView android:featureFlag="package.flag" /><TextView /><TextView />
</LinearLayout>)");
+ doc->file.uses_readwrite_feature_flags = true;
FlaggedXmlVersioner versioner;
auto results = versioner.Process(context_.get(), doc.get());
@@ -162,6 +164,7 @@ TEST_F(FlaggedXmlVersionerTest, NegatedFlagAttributeRemoved) {
<TextView android:featureFlag="!package.flag" /><TextView /><TextView />
</LinearLayout>)");
doc->file.config.sdkVersion = SDK_GINGERBREAD;
+ doc->file.uses_readwrite_feature_flags = true;
FlaggedXmlVersioner versioner;
auto results = versioner.Process(context_.get(), doc.get());
@@ -192,6 +195,7 @@ TEST_F(FlaggedXmlVersionerTest, NegatedFlagAttributeRemovedNoSpecifiedVersion) {
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android">
<TextView android:featureFlag="!package.flag" /><TextView /><TextView />
</LinearLayout>)");
+ doc->file.uses_readwrite_feature_flags = true;
FlaggedXmlVersioner versioner;
auto results = versioner.Process(context_.get(), doc.get());
diff --git a/tools/aapt2/link/TableMerger.cpp b/tools/aapt2/link/TableMerger.cpp
index 1d4adc4a57d8..17f332397317 100644
--- a/tools/aapt2/link/TableMerger.cpp
+++ b/tools/aapt2/link/TableMerger.cpp
@@ -295,6 +295,8 @@ bool TableMerger::DoMerge(const android::Source& src, ResourceTablePackage* src_
dst_config_value =
dst_entry->FindOrCreateValue(src_config_value->config, src_config_value->product);
}
+ dst_config_value->uses_readwrite_feature_flags |=
+ src_config_value->uses_readwrite_feature_flags;
// Continue if we're taking the new resource.
CloningValueTransformer cloner(&main_table_->string_pool);
@@ -378,12 +380,13 @@ bool TableMerger::MergeFile(const ResourceFile& file_desc, bool overlay, io::IFi
file_ref->file = file;
file_ref->SetFlagStatus(file_desc.flag_status);
file_ref->SetFlag(file_desc.flag);
-
ResourceTablePackage* pkg = table.FindOrCreatePackage(file_desc.name.package);
- pkg->FindOrCreateType(file_desc.name.type)
- ->FindOrCreateEntry(file_desc.name.entry)
- ->FindOrCreateValue(file_desc.config, {})
- ->value = std::move(file_ref);
+ ResourceConfigValue* config_value = pkg->FindOrCreateType(file_desc.name.type)
+ ->FindOrCreateEntry(file_desc.name.entry)
+ ->FindOrCreateValue(file_desc.config, {});
+
+ config_value->value = std::move(file_ref);
+ config_value->uses_readwrite_feature_flags = file_desc.uses_readwrite_feature_flags;
return DoMerge(file->GetSource(), pkg, false /*mangle*/, overlay /*overlay*/, true /*allow_new*/);
}
diff --git a/tools/aapt2/readme.md b/tools/aapt2/readme.md
index 6bdbaaed9858..413f817ea8fd 100644
--- a/tools/aapt2/readme.md
+++ b/tools/aapt2/readme.md
@@ -5,6 +5,10 @@
2017. This README will be updated more frequently in the future.
- Added a new flag `--no-compress-fonts`. This can significantly speed up loading fonts from APK
assets, at the cost of increasing the storage size of the APK.
+- Changed the behavior of `--enable-sparse-encoding`. Sparse encoding is only applied if the
+ minSdkVersion is >= 32.
+- Changed the behavior of `--force-sparse-encoding`. Sparse encoding is only applied if the
+ minSdkVersion is >= 32 or is not set.
## Version 2.19
- Added navigation resource type.
diff --git a/tools/protologtool/Android.bp b/tools/protologtool/Android.bp
index 8fbc3e8a78db..a103d03af3c3 100644
--- a/tools/protologtool/Android.bp
+++ b/tools/protologtool/Android.bp
@@ -11,13 +11,13 @@ java_library_host {
name: "protologtool-lib",
srcs: [
"src/com/android/protolog/tool/**/*.kt",
- ":protolog-common-src",
],
static_libs: [
"javaparser",
- "platformprotos",
"jsonlib",
"perfetto_trace-full",
+ "platformprotos",
+ "protolog-common-lib",
],
}
@@ -39,10 +39,10 @@ java_test_host {
unit_test: true,
},
static_libs: [
- "protologtool-lib",
"junit",
"mockito",
"objenesis",
+ "protologtool-lib",
"truth",
],
}
diff --git a/tools/protologtool/src/com/android/protolog/tool/exceptions.kt b/tools/protologtool/src/com/android/protolog/tool/Exceptions.kt
index ae00df123353..4f7e8d13c4ba 100644
--- a/tools/protologtool/src/com/android/protolog/tool/exceptions.kt
+++ b/tools/protologtool/src/com/android/protolog/tool/Exceptions.kt
@@ -18,21 +18,24 @@ package com.android.protolog.tool
import java.lang.Exception
-open class CodeProcessingException(message: String, context: ParsingContext)
- : Exception("Code processing error in ${context.filePath}:${context.lineNumber}:\n" +
- " $message")
+open class CodeProcessingException(
+ message: String, context: ParsingContext, cause: Throwable? = null
+) : Exception("Code processing error in ${context.filePath}:${context.lineNumber}:\n" +
+ " $message", cause)
-class HashCollisionException(message: String, context: ParsingContext) :
- CodeProcessingException(message, context)
+class HashCollisionException(
+ message: String, context: ParsingContext, cause: Throwable? = null
+) : CodeProcessingException(message, context, cause)
-class IllegalImportException(message: String, context: ParsingContext) :
- CodeProcessingException("Illegal import: $message", context)
+class IllegalImportException(message: String, context: ParsingContext, cause: Throwable? = null) :
+ CodeProcessingException("Illegal import: $message", context, cause)
-class InvalidProtoLogCallException(message: String, context: ParsingContext)
- : CodeProcessingException("InvalidProtoLogCall: $message", context)
+class InvalidProtoLogCallException(
+ message: String, context: ParsingContext, cause: Throwable? = null
+) : CodeProcessingException("InvalidProtoLogCall: $message", context, cause)
-class ParsingException(message: String, context: ParsingContext)
- : CodeProcessingException(message, context)
+class ParsingException(message: String, context: ParsingContext, cause: Throwable? = null) :
+ CodeProcessingException(message, context, cause)
class InvalidViewerConfigException(message: String) : Exception(message)
diff --git a/tools/protologtool/src/com/android/protolog/tool/ProtoLogCallProcessor.kt b/tools/protologtool/src/com/android/protolog/tool/ProtoLogCallProcessor.kt
index 47724b7a9e1d..03c3a15562a9 100644
--- a/tools/protologtool/src/com/android/protolog/tool/ProtoLogCallProcessor.kt
+++ b/tools/protologtool/src/com/android/protolog/tool/ProtoLogCallProcessor.kt
@@ -24,5 +24,5 @@ interface ProtoLogCallProcessor {
logCallVisitor: ProtoLogCallVisitor?,
otherCallVisitor: MethodCallVisitor?,
fileName: String
- ): CompilationUnit
+ ): Collection<CodeProcessingException>
}
diff --git a/tools/protologtool/src/com/android/protolog/tool/ProtoLogCallProcessorImpl.kt b/tools/protologtool/src/com/android/protolog/tool/ProtoLogCallProcessorImpl.kt
index 272d8bb1793d..44db2ba45845 100644
--- a/tools/protologtool/src/com/android/protolog/tool/ProtoLogCallProcessorImpl.kt
+++ b/tools/protologtool/src/com/android/protolog/tool/ProtoLogCallProcessorImpl.kt
@@ -74,7 +74,7 @@ class ProtoLogCallProcessorImpl(
}
fun process(code: CompilationUnit, logCallVisitor: ProtoLogCallVisitor?, fileName: String):
- CompilationUnit {
+ Collection<CodeProcessingException> {
return process(code, logCallVisitor, null, fileName)
}
@@ -83,7 +83,7 @@ class ProtoLogCallProcessorImpl(
logCallVisitor: ProtoLogCallVisitor?,
otherCallVisitor: MethodCallVisitor?,
fileName: String
- ): CompilationUnit {
+ ): Collection<CodeProcessingException> {
CodeUtils.checkWildcardStaticImported(code, protoLogClassName, fileName)
CodeUtils.checkWildcardStaticImported(code, protoLogGroupClassName, fileName)
@@ -93,42 +93,63 @@ class ProtoLogCallProcessorImpl(
protoLogGroupClassName)
val staticGroupImports = CodeUtils.staticallyImportedMethods(code, protoLogGroupClassName)
+ val errors = mutableListOf<CodeProcessingException>()
code.findAll(MethodCallExpr::class.java)
.filter { call ->
isProtoCall(call, isLogClassImported, staticLogImports)
}.forEach { call ->
val context = ParsingContext(fileName, call)
- val logMethods = LogLevel.entries.map { it.shortCode }
- if (logMethods.contains(call.name.id)) {
- // Process a log call
- if (call.arguments.size < 2) {
- throw InvalidProtoLogCallException("Method signature does not match " +
- "any ProtoLog method: $call", context)
- }
+ try {
+ val logMethods = LogLevel.entries.map { it.shortCode }
+ if (logMethods.contains(call.name.id)) {
+ // Process a log call
+ if (call.arguments.size < 2) {
+ errors.add(InvalidProtoLogCallException(
+ "Method signature does not match " +
+ "any ProtoLog method: $call", context
+ ))
+ return@forEach
+ }
- val messageString = CodeUtils.concatMultilineString(call.getArgument(1),
- context)
- val groupNameArg = call.getArgument(0)
- val groupName =
- getLogGroupName(groupNameArg, isGroupClassImported,
- staticGroupImports, fileName)
- if (groupName !in groupMap) {
- throw InvalidProtoLogCallException("Unknown group argument " +
- "- not a ProtoLogGroup enum member: $call", context)
- }
+ val messageString = CodeUtils.concatMultilineString(
+ call.getArgument(1),
+ context
+ )
+ val groupNameArg = call.getArgument(0)
+ val groupName =
+ getLogGroupName(
+ groupNameArg, isGroupClassImported,
+ staticGroupImports, fileName
+ )
+ if (groupName !in groupMap) {
+ errors.add(InvalidProtoLogCallException(
+ "Unknown group argument " +
+ "- not a ProtoLogGroup enum member: $call", context
+ ))
+ return@forEach
+ }
- logCallVisitor?.processCall(call, messageString, getLevelForMethodName(
- call.name.toString(), call, context), groupMap.getValue(groupName),
- context.lineNumber)
- } else if (call.name.id == "init") {
- // No processing
- } else {
- // Process non-log message calls
- otherCallVisitor?.processCall(call)
+ logCallVisitor?.processCall(
+ call, messageString, getLevelForMethodName(
+ call.name.toString(), call, context
+ ), groupMap.getValue(groupName),
+ context.lineNumber
+ )
+ } else if (call.name.id == "init") {
+ // No processing
+ } else {
+ // Process non-log message calls
+ otherCallVisitor?.processCall(call)
+ }
+ } catch (e: Throwable) {
+ errors.add(InvalidProtoLogCallException(
+ "Error processing log call: $call",
+ context, e
+ ))
}
}
- return code
+ return errors
}
private fun getLevelForMethodName(
diff --git a/tools/protologtool/src/com/android/protolog/tool/ProtoLogTool.kt b/tools/protologtool/src/com/android/protolog/tool/ProtoLogTool.kt
index d8a2954545bb..e9f2e81b69db 100644
--- a/tools/protologtool/src/com/android/protolog/tool/ProtoLogTool.kt
+++ b/tools/protologtool/src/com/android/protolog/tool/ProtoLogTool.kt
@@ -130,14 +130,16 @@ object ProtoLogTool {
val outSrc = try {
val code = tryParse(text, path)
if (containsProtoLogText(text, PROTOLOG_CLASS_NAME)) {
- transformer.processClass(text, path, packagePath(file, code), code)
+ val (processedText, errors) = transformer.processClass(text, path, packagePath(file, code), code)
+ errors.forEach { injector.reportProcessingError(it) }
+ processedText
} else {
text
}
} catch (ex: ParsingException) {
// If we cannot parse this file, skip it (and log why). Compilation will
// fail in a subsequent build step.
- injector.reportParseError(ex)
+ injector.reportProcessingError(ex)
text
}
path to outSrc
@@ -405,7 +407,7 @@ object ProtoLogTool {
} catch (ex: ParsingException) {
// If we cannot parse this file, skip it (and log why). Compilation will
// fail in a subsequent build step.
- injector.reportParseError(ex)
+ injector.reportProcessingError(ex)
null
}
} else {
@@ -466,6 +468,14 @@ object ProtoLogTool {
try {
val command = CommandOptions(args)
invoke(command)
+
+ if (injector.processingErrors.isNotEmpty()) {
+ injector.processingErrors.forEachIndexed { index, it ->
+ println("CodeProcessingException " +
+ "(${index + 1}/${injector.processingErrors.size}): \n${it.message}\n")
+ }
+ exitProcess(1)
+ }
} catch (ex: InvalidCommandException) {
println("InvalidCommandException: \n${ex.message}\n")
showHelpAndExit()
@@ -489,12 +499,14 @@ object ProtoLogTool {
}
var injector = object : Injector {
+ override val processingErrors: MutableList<CodeProcessingException>
+ get() = mutableListOf()
override fun fileOutputStream(file: String) = FileOutputStream(file)
override fun readText(file: File) = file.readText()
override fun readLogGroups(jarPath: String, className: String) =
ProtoLogGroupReader().loadFromJar(jarPath, className)
- override fun reportParseError(ex: ParsingException) {
- println("\n${ex.message}\n")
+ override fun reportProcessingError(ex: CodeProcessingException) {
+ processingErrors.add(ex)
}
}
@@ -502,7 +514,8 @@ object ProtoLogTool {
fun fileOutputStream(file: String): OutputStream
fun readText(file: File): String
fun readLogGroups(jarPath: String, className: String): Map<String, LogGroup>
- fun reportParseError(ex: ParsingException)
+ fun reportProcessingError(ex: CodeProcessingException)
+ val processingErrors: Collection<CodeProcessingException>
}
}
diff --git a/tools/protologtool/src/com/android/protolog/tool/SourceTransformer.kt b/tools/protologtool/src/com/android/protolog/tool/SourceTransformer.kt
index 76df0674df65..e97b0dd9a04b 100644
--- a/tools/protologtool/src/com/android/protolog/tool/SourceTransformer.kt
+++ b/tools/protologtool/src/com/android/protolog/tool/SourceTransformer.kt
@@ -66,13 +66,13 @@ class SourceTransformer(
packagePath: String,
compilationUnit: CompilationUnit =
StaticJavaParser.parse(code)
- ): String {
+ ): Pair<String, Collection<CodeProcessingException>> {
this.path = path
this.packagePath = packagePath
processedCode = code.split('\n').toMutableList()
offsets = IntArray(processedCode.size)
- protoLogCallProcessor.process(compilationUnit, protoLogCallVisitor, otherCallVisitor, path)
- return processedCode.joinToString("\n")
+ val processingErrors = protoLogCallProcessor.process(compilationUnit, protoLogCallVisitor, otherCallVisitor, path)
+ return Pair(processedCode.joinToString("\n"), processingErrors)
}
private val protoLogImplClassNode =
diff --git a/tools/protologtool/tests/com/android/protolog/tool/EndToEndTest.kt b/tools/protologtool/tests/com/android/protolog/tool/EndToEndTest.kt
index 0cbbd483fe59..71bab3ebf2db 100644
--- a/tools/protologtool/tests/com/android/protolog/tool/EndToEndTest.kt
+++ b/tools/protologtool/tests/com/android/protolog/tool/EndToEndTest.kt
@@ -17,6 +17,7 @@
package com.android.protolog.tool
import com.android.protolog.tool.ProtoLogTool.PROTOLOG_IMPL_SRC_PATH
+import com.android.protolog.tool.ProtoLogTool.injector
import com.google.common.truth.Truth
import java.io.ByteArrayInputStream
import java.io.ByteArrayOutputStream
@@ -102,6 +103,42 @@ class EndToEndTest {
""".trimIndent())
}
+ @Test
+ fun e2e_transform_withErrors() {
+ val srcs = mapOf(
+ "frameworks/base/org/example/Example.java" to """
+ package org.example;
+ import com.android.internal.protolog.ProtoLog;
+ import static com.android.internal.protolog.ProtoLogGroup.GROUP;
+
+ class Example {
+ void method() {
+ String argString = "hello";
+ int argInt = 123;
+ ProtoLog.d(GROUP, "Invalid format: %s %d %9 %", argString, argInt);
+ }
+ }
+ """.trimIndent())
+ val output = run(
+ srcs = srcs,
+ logGroup = LogGroup("GROUP", true, false, "TAG_GROUP"),
+ commandOptions = CommandOptions(arrayOf("transform-protolog-calls",
+ "--protolog-class", "com.android.internal.protolog.ProtoLog",
+ "--loggroups-class", "com.android.internal.protolog.ProtoLogGroup",
+ "--loggroups-jar", "not_required.jar",
+ "--viewer-config-file-path", "not_required.pb",
+ "--output-srcjar", "out.srcjar",
+ "frameworks/base/org/example/Example.java"))
+ )
+ val outSrcJar = assertLoadSrcJar(output, "out.srcjar")
+ // No change to source code on failure to process
+ Truth.assertThat(outSrcJar["frameworks/base/org/example/Example.java"])
+ .contains(srcs["frameworks/base/org/example/Example.java"])
+
+ Truth.assertThat(injector.processingErrors).hasSize(1)
+ Truth.assertThat(injector.processingErrors.first().message).contains("Invalid format")
+ }
+
private fun assertLoadSrcJar(
outputs: Map<String, ByteArray>,
path: String
@@ -172,7 +209,11 @@ class EndToEndTest {
override fun readLogGroups(jarPath: String, className: String) = mapOf(
logGroup.name to logGroup)
- override fun reportParseError(ex: ParsingException) = throw AssertionError(ex)
+ override fun reportProcessingError(ex: CodeProcessingException) {
+ processingErrors.add(ex)
+ }
+
+ override val processingErrors: MutableList<CodeProcessingException> = mutableListOf()
}
ProtoLogTool.invoke(commandOptions)
diff --git a/tools/protologtool/tests/com/android/protolog/tool/ProtoLogCallProcessorImplTest.kt b/tools/protologtool/tests/com/android/protolog/tool/ProtoLogCallProcessorImplTest.kt
index 004d97babbad..8f765aecb431 100644
--- a/tools/protologtool/tests/com/android/protolog/tool/ProtoLogCallProcessorImplTest.kt
+++ b/tools/protologtool/tests/com/android/protolog/tool/ProtoLogCallProcessorImplTest.kt
@@ -16,11 +16,13 @@
package com.android.protolog.tool
+import com.android.internal.protolog.common.InvalidFormatStringException
import com.android.internal.protolog.common.LogLevel
import com.github.javaparser.StaticJavaParser
import com.github.javaparser.ast.expr.MethodCallExpr
import org.junit.Assert.assertEquals
import org.junit.Test
+import com.google.common.truth.Truth
class ProtoLogCallProcessorImplTest {
private data class LogCall(
@@ -121,7 +123,7 @@ class ProtoLogCallProcessorImplTest {
checkCalls()
}
- @Test(expected = InvalidProtoLogCallException::class)
+ @Test
fun process_groupNotImported() {
val code = """
package org.example2;
@@ -135,7 +137,10 @@ class ProtoLogCallProcessorImplTest {
}
"""
groupMap["TEST"] = LogGroup("TEST", true, true, "WindowManager")
- visitor.process(StaticJavaParser.parse(code), processor, "")
+ val errors = visitor.process(StaticJavaParser.parse(code), processor, "")
+
+ Truth.assertThat(errors).hasSize(1)
+ Truth.assertThat(errors.first()).isInstanceOf(InvalidProtoLogCallException::class.java)
}
@Test
@@ -156,7 +161,7 @@ class ProtoLogCallProcessorImplTest {
assertEquals(0, calls.size)
}
- @Test(expected = InvalidProtoLogCallException::class)
+ @Test
fun process_unknownGroup() {
val code = """
package org.example;
@@ -167,10 +172,13 @@ class ProtoLogCallProcessorImplTest {
}
}
"""
- visitor.process(StaticJavaParser.parse(code), processor, "")
+ val errors = visitor.process(StaticJavaParser.parse(code), processor, "")
+
+ Truth.assertThat(errors).hasSize(1)
+ Truth.assertThat(errors.first()).isInstanceOf(InvalidProtoLogCallException::class.java)
}
- @Test(expected = InvalidProtoLogCallException::class)
+ @Test
fun process_staticGroup() {
val code = """
package org.example;
@@ -181,10 +189,13 @@ class ProtoLogCallProcessorImplTest {
}
}
"""
- visitor.process(StaticJavaParser.parse(code), processor, "")
+ val errors = visitor.process(StaticJavaParser.parse(code), processor, "")
+
+ Truth.assertThat(errors).hasSize(1)
+ Truth.assertThat(errors.first()).isInstanceOf(InvalidProtoLogCallException::class.java)
}
- @Test(expected = InvalidProtoLogCallException::class)
+ @Test
fun process_badGroup() {
val code = """
package org.example;
@@ -195,10 +206,13 @@ class ProtoLogCallProcessorImplTest {
}
}
"""
- visitor.process(StaticJavaParser.parse(code), processor, "")
+ val errors = visitor.process(StaticJavaParser.parse(code), processor, "")
+
+ Truth.assertThat(errors).hasSize(1)
+ Truth.assertThat(errors.first()).isInstanceOf(InvalidProtoLogCallException::class.java)
}
- @Test(expected = InvalidProtoLogCallException::class)
+ @Test
fun process_invalidSignature() {
val code = """
package org.example;
@@ -209,7 +223,10 @@ class ProtoLogCallProcessorImplTest {
}
}
"""
- visitor.process(StaticJavaParser.parse(code), processor, "")
+ val errors = visitor.process(StaticJavaParser.parse(code), processor, "")
+
+ Truth.assertThat(errors).hasSize(1)
+ Truth.assertThat(errors.first()).isInstanceOf(InvalidProtoLogCallException::class.java)
}
@Test
@@ -228,4 +245,39 @@ class ProtoLogCallProcessorImplTest {
visitor.process(StaticJavaParser.parse(code), processor, "")
checkCalls()
}
+
+ @Test
+ fun throws_clear_error_message_on_invalid_format_exception() {
+ val code = """
+ package org.example;
+
+ class Test {
+ void test() {
+ ProtoLog.d(ProtoLogGroup.TEST, "Invalid message %9 %");
+ }
+ }
+ """
+ groupMap["TEST"] = LogGroup("TEST", false, true, "WindowManager")
+
+ val processor = object : ProtoLogCallVisitor {
+ override fun processCall(
+ call: MethodCallExpr,
+ messageString: String,
+ level: LogLevel,
+ group: LogGroup,
+ lineNumber: Int,
+ ) {
+ throw InvalidFormatStringException("Invalid Protolog message format")
+ }
+ }
+
+ val errors = visitor.process(StaticJavaParser.parse(code), processor, "MyTestFile.java")
+ Truth.assertThat(errors).hasSize(1)
+
+ val exception = errors.first()
+ Truth.assertThat(exception).hasMessageThat()
+ .contains("Code processing error in MyTestFile.java:6")
+ Truth.assertThat(exception.cause).hasMessageThat()
+ .contains("Invalid Protolog message format")
+ }
}
diff --git a/tools/protologtool/tests/com/android/protolog/tool/SourceTransformerTest.kt b/tools/protologtool/tests/com/android/protolog/tool/SourceTransformerTest.kt
index 674a907d68d9..271fc6064678 100644
--- a/tools/protologtool/tests/com/android/protolog/tool/SourceTransformerTest.kt
+++ b/tools/protologtool/tests/com/android/protolog/tool/SourceTransformerTest.kt
@@ -160,10 +160,10 @@ class SourceTransformerTest {
visitor.processCall(code.findAll(MethodCallExpr::class.java)[0], "test %d %f",
LogLevel.WARN, LogGroup("TEST_GROUP", true, true, "WM_TEST"), 123)
- invocation.arguments[0] as CompilationUnit
+ listOf<CodeProcessingException>()
}
- val out = sourceJarWriter.processClass(TEST_CODE, PATH, PATH, code)
+ val (out, _) = sourceJarWriter.processClass(TEST_CODE, PATH, PATH, code)
code = StaticJavaParser.parse(out)
val protoLogCalls = code.findAll(MethodCallExpr::class.java).filter {
@@ -201,10 +201,10 @@ class SourceTransformerTest {
visitor.processCall(calls[2], "test %d %f",
LogLevel.WARN, LogGroup("TEST_GROUP", true, true, "WM_TEST"), 123)
- invocation.arguments[0] as CompilationUnit
+ listOf<CodeProcessingException>()
}
- val out = sourceJarWriter.processClass(TEST_CODE_MULTICALLS, PATH, PATH, code)
+ val (out, _) = sourceJarWriter.processClass(TEST_CODE_MULTICALLS, PATH, PATH, code)
code = StaticJavaParser.parse(out)
val protoLogCalls = code.findAll(MethodCallExpr::class.java).filter {
@@ -238,10 +238,10 @@ class SourceTransformerTest {
"test %d %f abc %s\n test", LogLevel.WARN, LogGroup("TEST_GROUP",
true, true, "WM_TEST"), 123)
- invocation.arguments[0] as CompilationUnit
+ listOf<CodeProcessingException>()
}
- val out = sourceJarWriter.processClass(TEST_CODE_MULTILINE, PATH, PATH, code)
+ val (out, _) = sourceJarWriter.processClass(TEST_CODE_MULTILINE, PATH, PATH, code)
code = StaticJavaParser.parse(out)
val protoLogCalls = code.findAll(MethodCallExpr::class.java).filter {
@@ -275,10 +275,10 @@ class SourceTransformerTest {
visitor.processCall(code.findAll(MethodCallExpr::class.java)[0], "test",
LogLevel.WARN, LogGroup("TEST_GROUP", true, true, "WM_TEST"), 456)
- invocation.arguments[0] as CompilationUnit
+ listOf<CodeProcessingException>()
}
- val out = sourceJarWriter.processClass(TEST_CODE_NO_PARAMS, PATH, PATH, code)
+ val (out, _) = sourceJarWriter.processClass(TEST_CODE_NO_PARAMS, PATH, PATH, code)
code = StaticJavaParser.parse(out)
val protoLogCalls = code.findAll(MethodCallExpr::class.java).filter {
@@ -309,10 +309,10 @@ class SourceTransformerTest {
visitor.processCall(code.findAll(MethodCallExpr::class.java)[0], "test %d %f",
LogLevel.WARN, LogGroup("TEST_GROUP", true, false, "WM_TEST"), 789)
- invocation.arguments[0] as CompilationUnit
+ listOf<CodeProcessingException>()
}
- val out = sourceJarWriter.processClass(TEST_CODE, PATH, PATH, code)
+ val (out, _) = sourceJarWriter.processClass(TEST_CODE, PATH, PATH, code)
code = StaticJavaParser.parse(out)
val protoLogCalls = code.findAll(MethodCallExpr::class.java).filter {
@@ -346,10 +346,10 @@ class SourceTransformerTest {
"test %d %f abc %s\n test", LogLevel.WARN, LogGroup("TEST_GROUP",
true, false, "WM_TEST"), 123)
- invocation.arguments[0] as CompilationUnit
+ listOf<CodeProcessingException>()
}
- val out = sourceJarWriter.processClass(TEST_CODE_MULTILINE, PATH, PATH, code)
+ val (out, _) = sourceJarWriter.processClass(TEST_CODE_MULTILINE, PATH, PATH, code)
code = StaticJavaParser.parse(out)
val protoLogCalls = code.findAll(MethodCallExpr::class.java).filter {